On Sun, Mar 31, 2013 at 10:04:44PM +0200, Martin Minarik wrote: > Computes an accurate velocity instead of approximation. > > Changes: > Add disclaimer. Implements 8 acceleration profiles taken from X ptrveloc.c. > The sampling has been fixed to handle eventual time overflow.
Since we added them I can't remember seeing a configuration that uses a non-default acceleration profile. I don't think implementing multiple profiles is really worthwhile. The only other profile that is really in use is the synaptics one (which uses pressure-based accel) and that too because it's the default. Try to find a single accel method that is good enough for most cases and the vast majority of users won't notice. Cheers, Peter > > Configuration: > Tune the values in mouse_init(). > > The tune_constant_coefficient is simply a multiplier of how fast > the pointer moves. The 1.0 is the native device speed. Recommended: > > 0.03125 0.0625 0.25 0.5 0.75 1.0 1.5 2.0 2.5 3.0 3.5 > > The tune_acceleration_profile is used to select one of the 8 profiles: > http://www.x.org/wiki/Development/Documentation/PointerAcceleration#AccelerationProfiles > To disable acceleration, use no_profile, number 0. > > tune_acceleration_treshold - should be a bit larger than 1. > tune_min_multiplier - the multiplier when moving slow. > tune_acc_multiplier - the multiplier when moving fast. > --- > src/Makefile.am | 4 + > src/evdev-mouse.c | 467 > +++++++++++++++++++++++++++++++++++++++++++++++++++++ > src/evdev.c | 3 + > src/evdev.h | 3 + > 4 files changed, 477 insertions(+) > create mode 100644 src/evdev-mouse.c > > diff --git a/src/Makefile.am b/src/Makefile.am > index d56daa0..2c8b3eb 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -138,6 +138,7 @@ drm_backend_la_SOURCES = \ > udev-seat.h \ > evdev.c \ > evdev.h \ > + evdev-mouse.c \ > evdev-touchpad.c \ > launcher-util.c \ > launcher-util.h \ > @@ -177,7 +178,9 @@ rpi_backend_la_SOURCES = \ > tty.c \ > evdev.c \ > evdev.h \ > + evdev-mouse.c \ > evdev-touchpad.c > + > endif > > if ENABLE_HEADLESS_COMPOSITOR > @@ -211,6 +214,7 @@ fbdev_backend_la_SOURCES = \ > evdev.c \ > evdev.h \ > evdev-touchpad.c \ > + evdev-mouse.c \ > launcher-util.c > endif > > diff --git a/src/evdev-mouse.c b/src/evdev-mouse.c > new file mode 100644 > index 0000000..351eadc > --- /dev/null > +++ b/src/evdev-mouse.c > @@ -0,0 +1,467 @@ > +/* > + * Copyright ?? 2013 Martin Minarik > + * Copyright ?? 2006-2009 Simon Thum > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > + * copy of this software and associated documentation files (the "Software"), > + * to deal in the Software without restriction, including without limitation > + * the rights to use, copy, modify, merge, publish, distribute, sublicense, > + * and/or sell copies of the Software, and to permit persons to whom the > + * Software is furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice (including the next > + * paragraph) shall be included in all copies or substantial portions of the > + * Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER > + * DEALINGS IN THE SOFTWARE. > + */ > + > +#include <stdlib.h> > +#include <stdbool.h> > + > +#include "compositor.h" > +#include "evdev.h" > + > +#define DEFAULT_AXIS_STEP_DISTANCE wl_fixed_from_int(10) > +#define MOUSE_MOTION_SAMPLING_PERIOD 32 > + > +typedef double (*accel_velocity_func_t)(double min_acc, double velocity, > + double threshold, double acc); > + > +/** > + * just a smooth function in [0..1] -> [0..1] > + * - point symmetry at 0.5 > + * - f'(0) = f'(1) = 0 > + * - starts faster than a sinoid > + * - smoothness C1 (Cinf if you dare to ignore endpoints) > + */ > +static inline double > +calc_penumbral_gradient(double x) > +{ > + x *= 2.0f; > + x -= 1.0f; > + return 0.5f + (x * sqrt(1.0 - x * x) + asin(x)) / M_PI; > +} > + > +/***************************************** > + * Acceleration functions and profiles > + ****************************************/ > + > +/** > + * acceleration function similar to classic accelerated/unaccelerated, > + * but with smooth transition in between (and towards zero for adaptive > dec.). > + */ > +static double > +simple_smooth_profile(double min_acc, > + double velocity, double threshold, double acc) > +{ > + if (velocity < 1.0f) > + return calc_penumbral_gradient(0.5 + velocity * 0.5) * 2.0f - > 1.0f; > + if (threshold < 1.0f) > + threshold = 1.0f; > + if (velocity <= threshold) > + return 1; > + velocity /= threshold; > + if (velocity >= acc) > + return acc; > + else > + return 1.0f + (calc_penumbral_gradient(velocity / acc) * (acc - > 1.0f)); > +} > + > +/** > + * Polynomial function similar previous one, but with f(1) = 1 > + */ > +static double > +polynomial_acceleration_profile(double min_acc, > + double velocity, double ignored, double acc) > +{ > + return pow(velocity, (acc - 1.0) * 0.5); > +} > + > +/** > + * returns acceleration for velocity. > + * This profile selects the two functions like the old scheme did > + */ > +static double > +classic_profile(double min_acc, double velocity, double threshold, double > acc) > +{ > + if (threshold > 0) > + return simple_smooth_profile(min_acc, velocity, threshold, acc); > + else > + return polynomial_acceleration_profile(min_acc, velocity, 0, > acc); > +} > + > +/** > + * Power profile > + * This has a completely smooth transition curve, i.e. no jumps in the > + * derivatives. > + * > + * This has the expense of overall response dependency on min-acceleration. > + * In effect, min_acc mimics const_acceleration in this profile. > + */ > +static double > +power_profile(double min_acc, double velocity, double threshold, double acc) > +{ > + double vel_dist; > + > + acc = (acc - 1.0) * 0.1f + 1.0;/* without this, acc of 2 is unuseable */ > + > + if (velocity <= threshold) > + return min_acc; > + vel_dist = velocity - threshold; > + return (pow(acc, vel_dist)) * min_acc; > +} > + > +/** > + * This profile uses the first half of the penumbral gradient as a start > + * and then scales linearly. > + */ > +static double > +smooth_linear_profile(double min_acc, double velocity, double threshold, > double acc) > +{ > + double res, nv; > + > + if (acc > 1.0f) > + acc -= 1.0f; /*this is so acc = 1 is no acceleration */ > + else > + return 1.0f; > + > + nv = (velocity - threshold) * acc * 0.5f; > + > + if (nv < 0) { > + res = 0; > + } > + else if (nv < 2) { > + res = calc_penumbral_gradient(nv * 0.25f) * 2.0f; > + } else { > + nv -= 2.0f; > + res = nv * 2.0f / M_PI /* steepness of gradient at 0.5 */ > + + 1.0f; /* gradient crosses 2|1 */ > + } > + res += min_acc; > + return res; > +} > + > +/** > + * From 0 to threshold, the response graduates smoothly from min_accel to > + * acceleration. Beyond threshold it is exactly the specified acceleration. > + */ > +static double > +smooth_limited_profile(double min_acc, double velocity, double threshold, > double acc) > +{ > + double res; > + > + if (velocity >= threshold || threshold == 0.0f) > + return acc; > + > + velocity /= threshold; /* should be [0..1[ now */ > + > + res = calc_penumbral_gradient(velocity) * (acc - min_acc); > + > + return min_acc + res; > +} > + > +static double > +linear_profile(double min_acc, double velocity, double threshold, double acc) > +{ > + return acc * velocity; > +} > + > +static double > +no_profile(double min_acc, double velocity, double threshold, double acc) > +{ > + return 1.0f; > +} > + > +static accel_velocity_func_t accel_profiles[8] = { > + no_profile, > + classic_profile, > + polynomial_acceleration_profile, > + smooth_linear_profile, > + simple_smooth_profile, > + power_profile, > + linear_profile, > + smooth_limited_profile > +}; > + > +/********************************************************/ > + > +struct mouse_motion { > + int32_t dx; > + int32_t dy; > +}; > + > +struct mouse_dispatch { > + struct evdev_dispatch base; > + struct evdev_device *device; > + > + double actual_acceleration_factor; > + > + struct mouse_motion motion_sample; > + uint32_t motion_period_id; > + uint32_t motion_period_next; > + double last_velocity; > + double last_computed_acc; > + > + double tune_constant_coefficient; > + accel_velocity_func_t tune_acceleration_profile; > + double tune_min_multiplier; > + double tune_acc_multiplier; > + double tune_acceleration_treshold; > +}; > + > +static inline void > +mouse_process_relative(struct mouse_dispatch *mouse, > + struct evdev_device *device, > + struct input_event *event, uint32_t time) > +{ > + switch (event->code) { > + case REL_X: > + if (mouse->tune_acceleration_profile != no_profile) > + mouse->motion_sample.dx += event->value; > + device->rel.dx += wl_fixed_from_double((double) event->value * > + mouse->actual_acceleration_factor); > + > + device->pending_events |= EVDEV_RELATIVE_MOTION; > + break; > + case REL_Y: > + if (mouse->tune_acceleration_profile != no_profile) > + mouse->motion_sample.dy += event->value; > + device->rel.dy += wl_fixed_from_double((double) event->value * > + mouse->actual_acceleration_factor); > + > + device->pending_events |= EVDEV_RELATIVE_MOTION; > + break; > + case REL_WHEEL: > + switch (event->value) { > + case -1: > + /* Scroll down */ > + case 1: > + /* Scroll up */ > + notify_axis(device->seat, > + time, > + WL_POINTER_AXIS_VERTICAL_SCROLL, > + -1 * event->value * > DEFAULT_AXIS_STEP_DISTANCE); > + break; > + default: > + break; > + } > + break; > + case REL_HWHEEL: > + switch (event->value) { > + case -1: > + /* Scroll left */ > + case 1: > + /* Scroll right */ > + notify_axis(device->seat, > + time, > + WL_POINTER_AXIS_HORIZONTAL_SCROLL, > + event->value * DEFAULT_AXIS_STEP_DISTANCE); > + break; > + default: > + break; > + > + } > + } > +} > + > +static inline void > +mouse_process_key(struct mouse_dispatch *mouse, > + struct evdev_device *device, > + struct input_event *e, > + uint32_t time) > +{ > + if (e->value == 2) > + return; > + > + switch (e->code) { > + case BTN_LEFT: > + case BTN_RIGHT: > + case BTN_MIDDLE: > + case BTN_SIDE: > + case BTN_EXTRA: > + case BTN_FORWARD: > + case BTN_BACK: > + case BTN_TASK: > + notify_button(device->seat, > + time, e->code, > + e->value ? WL_POINTER_BUTTON_STATE_PRESSED : > + WL_POINTER_BUTTON_STATE_RELEASED); > + break; > + > + default: > + notify_key(device->seat, > + time, e->code, > + e->value ? WL_KEYBOARD_KEY_STATE_PRESSED : > + WL_KEYBOARD_KEY_STATE_RELEASED, > + STATE_UPDATE_AUTOMATIC); > + break; > + } > +} > + > +static void > +mouse_sampler_reset(struct mouse_dispatch *m) > +{ > + m->actual_acceleration_factor = m->tune_constant_coefficient * > + m->tune_min_multiplier; > + m->motion_sample.dx = 0; > + m->motion_sample.dy = 0; > + m->last_velocity = 0.0; > + m->last_computed_acc = 0.0; > +} > + > +static void > +mouse_sampler_advance_period(struct mouse_dispatch *m, uint32_t time) > +{ > + const unsigned long int period = MOUSE_MOTION_SAMPLING_PERIOD; > + const uint32_t uint32max = ~0; > + > + m->motion_period_id = time / period; > + m->motion_period_next = (uint32max & (time + period)) / period; > +} > + > +static double > +mouse_compute_acceleration(struct mouse_dispatch *m, double vel) > +{ > + double computed; > + computed = m->tune_acceleration_profile(m->tune_min_multiplier, vel, > + m->tune_acceleration_treshold, > + m->tune_acc_multiplier); > + > + if (computed < m->tune_min_multiplier) > + return m->tune_min_multiplier; > + > + return computed; > +} > + > +static void > +mouse_sampler_apply_acceleration_mult(struct mouse_dispatch *m, uint32_t > time) > +{ > + struct mouse_motion *sample = &m->motion_sample; > + > + const unsigned long int period = MOUSE_MOTION_SAMPLING_PERIOD; > + > + unsigned long int distance_sq; > + double velocity; > + double velocity_avg; > + double comp_acc; > + double comp_acc_avg; > + double mult; > + > + distance_sq = sample->dx * sample->dx + sample->dy * sample->dy; > + > + velocity = sqrtl(distance_sq) / period; > + velocity_avg = (velocity + m->last_velocity) / 2.0; > + > + comp_acc = mouse_compute_acceleration(m, velocity); > + comp_acc_avg = mouse_compute_acceleration(m, velocity_avg); > + > + /* Use Simpson's rule to calculate the avarage acceleration between > + * the previous motion and the most recent. */ > + mult = (comp_acc + 4.0 * comp_acc_avg + m->last_computed_acc) * 0.16666; > + > + m->last_velocity = velocity; > + m->last_computed_acc = comp_acc; > + m->actual_acceleration_factor = m->tune_constant_coefficient * mult; > + sample->dx = 0; > + sample->dy = 0; > +} > + > +static void > +mouse_sampler_refresh(struct mouse_dispatch *m, uint32_t time) > +{ > + uint32_t this_period_id = time / MOUSE_MOTION_SAMPLING_PERIOD; > + > + if (this_period_id != m->motion_period_id) { > + if (this_period_id == m->motion_period_next) { > + mouse_sampler_apply_acceleration_mult(m, time); > + } else { > + mouse_sampler_reset(m); > + } > + mouse_sampler_advance_period(m, time); > + } > +} > + > +static void > +mouse_process(struct evdev_dispatch *dispatch, > + struct evdev_device *device, > + struct input_event *event, > + uint32_t time) > +{ > + struct mouse_dispatch *mouse = > + (struct mouse_dispatch *) dispatch; > + > + switch (event->type) { > + case EV_REL: > + mouse_process_relative(mouse, device, event, time); > + break; > + case EV_KEY: > + mouse_process_key(mouse, device, event, time); > + break; > + case EV_SYN: > + device->pending_events |= EVDEV_SYN; > + > + if (mouse->tune_acceleration_profile != no_profile) > + mouse_sampler_refresh(mouse, time); > + break; > + } > +} > + > +static void > +mouse_destroy(struct evdev_dispatch *dispatch) > +{ > + struct mouse_dispatch *mouse = > + (struct mouse_dispatch *) dispatch; > + > + free(mouse); > +} > + > +struct evdev_dispatch_interface mouse_interface = { > + mouse_process, > + mouse_destroy > +}; > + > +static int > +mouse_init(struct mouse_dispatch *mouse, struct evdev_device *device) > +{ > + mouse->base.interface = &mouse_interface; > + mouse->device = device; > + > + /* Configure mouse speed */ > + mouse->tune_constant_coefficient = 1.0; > + > + /* Configure mouse acceleration */ > + mouse->tune_acceleration_profile = accel_profiles[2]; > + mouse->tune_acceleration_treshold = 1.5; > + mouse->tune_min_multiplier = 1.0; > + mouse->tune_acc_multiplier = 3.0; > + > + /* Prepare motion sampling */ > + mouse_sampler_reset(mouse); > + mouse_sampler_advance_period(mouse, 0); > + > + return 0; > +} > + > +struct evdev_dispatch * > +evdev_mouse_create(struct evdev_device *device) > +{ > + struct mouse_dispatch *mouse; > + > + mouse = malloc(sizeof *mouse); > + if (mouse == NULL) > + return NULL; > + > + if (mouse_init(mouse, device) != 0) { > + free(mouse); > + return NULL; > + } > + > + return &mouse->base; > +} > diff --git a/src/evdev.c b/src/evdev.c > index d2954b5..bbad5ad 100644 > --- a/src/evdev.c > +++ b/src/evdev.c > @@ -459,6 +459,9 @@ evdev_handle_device(struct evdev_device *device) > !TEST_BIT(key_bits, BTN_TOOL_PEN) && > has_abs) > device->dispatch = evdev_touchpad_create(device); > + else if (TEST_BIT(key_bits, BTN_LEFT) && !has_abs) > + device->dispatch = evdev_mouse_create(device); > + > for (i = KEY_ESC; i < KEY_MAX; i++) { > if (i >= BTN_MISC && i < KEY_OK) > continue; > diff --git a/src/evdev.h b/src/evdev.h > index eb5c868..f670682 100644 > --- a/src/evdev.h > +++ b/src/evdev.h > @@ -111,6 +111,9 @@ struct evdev_dispatch { > struct evdev_dispatch * > evdev_touchpad_create(struct evdev_device *device); > > +struct evdev_dispatch * > +evdev_mouse_create(struct evdev_device *device); > + > void > evdev_led_update(struct evdev_device *device, enum weston_led leds); > > -- > 1.7.10.4 > > _______________________________________________ > wayland-devel mailing list > [email protected] > http://lists.freedesktop.org/mailman/listinfo/wayland-devel _______________________________________________ wayland-devel mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/wayland-devel
