#![feature(map_first_last)]
#![feature(half_open_range_patterns)]
extern crate num;
use std::f64;
use app_dirs::*;
#[macro_use]
extern crate lazy_static;
mod circular_backqueue;
mod interpolate;
mod ranged_map;
use std::ops;
use interpolate::Interpolator;
type Timestamp = u64;
use std::fs::File;
use std::io::prelude::*;
use std::sync::RwLock;
use tini::Ini;
const APP_INFO: AppInfo = AppInfo { name: "libscroll", author: "Sawyer Bergeron" };
#[allow(non_snake_case)]
pub struct Config {
pub EVENT_EXPIRY_COUNT: usize,
pub SAMPLE_EXPIRY_COUNT: usize,
pub TICKS_TO_COAST: f64,
pub TIMESTEP: f64,
pub MIN_VELOCITY_TO_IDLE: f64,
pub POST_ACCEL_SCALE_VELOCITY: f64,
pub PRE_ACCEL_SCALE_VELOCITY: f64,
pub SHIFT_WINDOW_MS: f64,
pub OVERSCROLL_ELASTICITY_COEFFICIENT: f64,
pub CONTENT_MASS_VALUE: f64,
pub OVERSCROLL_SPRING_CONSTANT: f64,
pub BOUNCE_DAMP_FACTOR: f64,
pub MAX_MS_WITHOUT_ZERO_INJECTION: f64,
pub MULTIPLY_FIRST_EVENT: f64,
pub ACCEL_DECEL_DESCRIMINANT: f64,
pub ACCELERATION_EXPONENT: f64,
pub FLING_BOOST_CONSTANT_FACTOR: f64,
pub FLIPS_TO_IDLE: u64,
}
impl Default for Config {
fn default() -> Self {
Config {
TIMESTEP: 0.1,
MIN_VELOCITY_TO_IDLE: 0.002,
EVENT_EXPIRY_COUNT: 20,
SAMPLE_EXPIRY_COUNT: 20,
TICKS_TO_COAST: 1.6,
FLIPS_TO_IDLE: 20,
POST_ACCEL_SCALE_VELOCITY: 19.0,
PRE_ACCEL_SCALE_VELOCITY: 1.0,
SHIFT_WINDOW_MS: 0.0,
OVERSCROLL_ELASTICITY_COEFFICIENT: 1.0,
CONTENT_MASS_VALUE: 6000.0,
OVERSCROLL_SPRING_CONSTANT: 0.4,
BOUNCE_DAMP_FACTOR: 0.9974,
MAX_MS_WITHOUT_ZERO_INJECTION: 150.0,
MULTIPLY_FIRST_EVENT: 500.0,
ACCEL_DECEL_DESCRIMINANT: 10.0,
ACCELERATION_EXPONENT: 1.4,
FLING_BOOST_CONSTANT_FACTOR: 2.0,
}
}
}
lazy_static! {
static ref CONFIG: RwLock<Config> = RwLock::new(Config::default());
}
const SAMPLE_OVER_X_FRAMES: usize = 10;
const DEBUG: bool = false;
const VALUE_MULTIPLIER: f64 = 6.0;
pub struct Scrollview {
content_height: f64,
content_width: f64,
viewport_height: f64,
viewport_width: f64,
current_source: Source,
dbg_amt_x: f64,
dbg_amt_y: f64,
input_per_frame_log: circular_backqueue::ForgetfulLogQueue<u32>,
x: Interpolator,
y: Interpolator,
}
#[derive(Copy)]
#[derive(Clone)]
#[derive(Default)]
pub struct AxisVector<T> where T: num::Num, T: PartialOrd, T: Copy {
pub x: T,
pub y: T,
x_threshold: T,
y_threshold: T,
decaying: bool,
}
impl<T> AxisVector<T> where T: num::Num, T: PartialOrd, T: Copy {
}
impl AxisVector<f64> {
pub fn scale(&self, scalar: f64) -> AxisVector<f64> {
AxisVector {
x: self.x * scalar,
y: self.y * scalar,
..self.clone()
}
}
}
impl<T> ops::Add<AxisVector<T>> for AxisVector<T> where T: num::Num, T: PartialOrd, T: Copy {
type Output = AxisVector<T>;
fn add(self, rhs: AxisVector<T>) -> AxisVector<T> {
AxisVector {
x: self.x + rhs.x,
y: self.y + rhs.y,
..self
}
}
}
impl<T> std::fmt::Display for AxisVector<T>
where T: std::fmt::Display, T: num::Num, T: PartialOrd, T: Copy
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
#[derive(Clone, Copy, Debug)]
pub enum Axis {
Horizontal,
Vertical,
}
#[derive(Copy, Clone, Debug)]
pub enum Source {
Undefined,
Touchscreen,
Touchpad,
Mousewheel,
PreciseMousewheel,
Passthrough,
KineticPassthrough,
Previous,
}
impl Source {
fn overscrolls(&self) -> bool {
let r = match self {
Self::Touchscreen => true,
Self::Touchpad => true,
_ => false,
};
r
}
fn kinetic(&self) -> bool {
match self {
Self::Touchpad | Self::Touchscreen | Self::KineticPassthrough => true,
_ => false,
}
}
fn accelerates(&self) -> bool {
match self {
Self::Touchpad => true,
_ => false,
}
}
}
impl Default for Source {
fn default() -> Self { Source::Undefined }
}
impl Scrollview {
pub fn sample(&mut self, timestamp: Timestamp) -> AxisVector<f64> {
if !DEBUG {
AxisVector {
x: 0.0,
y: self.y.sample(timestamp as f64),
..Default::default()
}
} else {
AxisVector {
x: self.dbg_amt_x,
y: self.dbg_amt_y,
..Default::default()
}
}
}
pub fn new() -> Scrollview {
eprintln!("Updating config...");
Self::update_config();
Scrollview {
input_per_frame_log: circular_backqueue::ForgetfulLogQueue::new(SAMPLE_OVER_X_FRAMES),
content_height: 0.0,
content_width: 0.0,
viewport_height: 0.0,
viewport_width: 0.0,
current_source: Source::Undefined,
dbg_amt_y: 0.0,
dbg_amt_x: 0.0,
x: Interpolator::new(false, (0.0, 0.0), 0.0),
y: Interpolator::new(false, (0.0, 0.0), 0.0),
}
}
pub fn del(_: Scrollview) {}
pub fn set_geometry(
&mut self,
content_height: f64,
content_width: f64,
viewport_height: f64,
viewport_width: f64,
) {
self.content_height = content_height;
self.content_width = content_width;
self.viewport_height = viewport_height;
self.viewport_width = viewport_width;
self.x.set_geometry(0.0, (content_width - viewport_width) as f64);
self.y.set_geometry(0.0, (content_height - viewport_height) as f64);
}
pub fn animating(&self) -> bool {
self.x.animating() || self.y.animating()
}
pub fn push_pan(&mut self, axis: Axis, amount: f64, timestamp: Option<u64>) {
if !DEBUG {
match axis {
Axis::Horizontal => self.x.signal_pan(timestamp.unwrap() as f64, amount),
Axis::Vertical => self.y.signal_pan(timestamp.unwrap() as f64, amount),
}
} else {
match axis {
Axis::Horizontal => self.dbg_amt_x += amount,
Axis::Vertical => self.dbg_amt_y += amount,
}
}
}
pub fn push_fling(&mut self, timestamp: Option<u64>) {
eprintln!("Updating config...");
Self::update_config();
println!("push_fling with {}", timestamp.unwrap());
self.x.signal_fling(timestamp.unwrap() as f64);
self.y.signal_fling(timestamp.unwrap() as f64);
}
pub fn push_interrupt(&mut self, timestamp: Option<u64>) {
self.x.signal_interrupt(timestamp.unwrap() as f64);
self.y.signal_interrupt(timestamp.unwrap() as f64);
}
pub fn set_source(&mut self, source: Source) {
self.current_source = source;
self.x.set_source(source);
self.y.set_source(source);
}
fn update_config() {
let mut config_struct = CONFIG.write().expect("Couldn't lock config struct");
let mut config_dir: std::path::PathBuf = app_root(AppDataType::UserConfig, &APP_INFO).unwrap();
let config_file = std::path::PathBuf::from("config.ini");
config_dir.push(config_file);
let _ = Ini::from_file(&config_dir).map(|config| {
println!("Found config file, applying...");
config.get("config", "event_expiry_count").map(|v: usize| { config_struct.EVENT_EXPIRY_COUNT = v});
config.get("config", "sample_expiry_count").map(|v: usize| { config_struct.SAMPLE_EXPIRY_COUNT = v});
config.get("config", "ticks_to_coast").map(|v: f64| { config_struct.TICKS_TO_COAST = v});
config.get("config", "timestep").map(|v: f64| { config_struct.TIMESTEP = v});
config.get("config", "min_velocity_to_idle").map(|v: f64| { config_struct.MIN_VELOCITY_TO_IDLE = v});
config.get("config", "post_acceleration_scale_velocity").map(|v: f64| { config_struct.POST_ACCEL_SCALE_VELOCITY = v});
config.get("config", "pre_acceleration_scale_velocity").map(|v: f64| { config_struct.PRE_ACCEL_SCALE_VELOCITY = v});
config.get("config", "shift_window_ms").map(|v: f64| { config_struct.SHIFT_WINDOW_MS = v});
config.get("config", "overscroll_elasticity_coefficient").map(|v: f64| { config_struct.OVERSCROLL_ELASTICITY_COEFFICIENT = v});
config.get("config", "content_mass_value").map(|v: f64| { config_struct.CONTENT_MASS_VALUE = v});
config.get("config", "overscroll_spring_constant").map(|v: f64| { config_struct.OVERSCROLL_SPRING_CONSTANT = v});
config.get("config", "bounce_damping_factor").map(|v: f64| { config_struct.BOUNCE_DAMP_FACTOR = v});
config.get("config", "zero_delta_injection_wait_ms").map(|v: f64| { config_struct.MAX_MS_WITHOUT_ZERO_INJECTION = v});
config.get("config", "first_event_multiplier").map(|v: f64| { config_struct.MULTIPLY_FIRST_EVENT = v});
config.get("config", "accel_decel_descriminant").map(|v: f64| { config_struct.ACCEL_DECEL_DESCRIMINANT = v});
config.get("config", "acceleration_exponent").map(|v: f64| { config_struct.ACCELERATION_EXPONENT = v});
config.get("config", "fling_boost_constant_factor").map(|v: f64| { config_struct.FLING_BOOST_CONSTANT_FACTOR = v});
config.get("config", "flips_until_idle").map(|v: u64| { config_struct.FLIPS_TO_IDLE = v});
}).map_err(|_| {
println!("Couldn't find config file");
});
}
}