/* * Copyright © 2012 Jonas Ådahl * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of the copyright holders not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. The copyright holders make * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "compositor.h" #include "filter.h" void weston_filter_dispatch(struct weston_motion_filter *filter, struct weston_motion_params *motion, void *data, uint32_t time) { filter->interface->filter(filter, motion, data, time); } /* * Pointer acceleration filter */ #define MAX_VELOCITY_DIFF 1.0 #define MOTION_TIMEOUT 300 /* (ms) */ #define NUM_POINTER_TRACKERS 16 struct pointer_tracker { double dx; double dy; uint32_t time; int dir; }; struct pointer_accelerator; struct pointer_accelerator { struct weston_motion_filter base; accel_profile_func_t profile; double velocity; double last_velocity; int last_dx; int last_dy; struct pointer_tracker *trackers; int cur_tracker; }; enum directions { N = 1 << 0, NE = 1 << 1, E = 1 << 2, SE = 1 << 3, S = 1 << 4, SW = 1 << 5, W = 1 << 6, NW = 1 << 7, UNDEFINED_DIRECTION = 0xff }; static int get_direction(int dx, int dy) { int dir = UNDEFINED_DIRECTION; int d1, d2; double r; if (abs(dx) < 2 && abs(dy) < 2) { if (dx > 0 && dy > 0) dir = S | SE | E; else if (dx > 0 && dy < 0) dir = N | NE | E; else if (dx < 0 && dy > 0) dir = S | SW | W; else if (dx < 0 && dy < 0) dir = N | NW | W; else if (dx > 0) dir = NW | W | SW; else if (dx < 0) dir = NE | E | SE; else if (dy > 0) dir = SE | S | SW; else if (dy < 0) dir = NE | N | NW; } else { /* Calculate r within the interval [0 to 8) * * r = [0 .. 2π] where 0 is North * d_f = r / 2π ([0 .. 1)) * d_8 = 8 * d_f */ r = atan2(dy, dx); r = fmod(r + 2.5*M_PI, 2*M_PI); r *= 4*M_1_PI; /* Mark one or two close enough octants */ d1 = (int)(r + 0.9) % 8; d2 = (int)(r + 0.1) % 8; dir = (1 << d1) | (1 << d2); } return dir; } static void feed_trackers(struct pointer_accelerator *accel, double dx, double dy, uint32_t time) { int i, current; struct pointer_tracker *trackers = accel->trackers; for (i = 0; i < NUM_POINTER_TRACKERS; i++) { trackers[i].dx += dx; trackers[i].dy += dy; } current = (accel->cur_tracker + 1) % NUM_POINTER_TRACKERS; accel->cur_tracker = current; trackers[current].dx = 0.0; trackers[current].dy = 0.0; trackers[current].time = time; trackers[current].dir = get_direction(dx, dy); } static struct pointer_tracker * tracker_by_offset(struct pointer_accelerator *accel, unsigned int offset) { unsigned int index = (accel->cur_tracker + NUM_POINTER_TRACKERS - offset) % NUM_POINTER_TRACKERS; return &accel->trackers[index]; } static double calculate_tracker_velocity(struct pointer_tracker *tracker, uint32_t time) { int dx; int dy; double distance; dx = tracker->dx; dy = tracker->dy; distance = sqrt(dx*dx + dy*dy); return distance / (double)(time - tracker->time); } static double calculate_velocity(struct pointer_accelerator *accel, uint32_t time) { struct pointer_tracker *tracker; double velocity; double result = 0.0; double initial_velocity; double velocity_diff; unsigned int offset; unsigned int dir = tracker_by_offset(accel, 0)->dir; /* Find first velocity */ for (offset = 1; offset < NUM_POINTER_TRACKERS; offset++) { tracker = tracker_by_offset(accel, offset); if (time <= tracker->time) continue; result = initial_velocity = calculate_tracker_velocity(tracker, time); if (initial_velocity > 0.0) break; } /* Find least recent vector within a timelimit, maximum velocity diff * and direction threshold. */ for (; offset < NUM_POINTER_TRACKERS; offset++) { tracker = tracker_by_offset(accel, offset); /* Stop if too far away in time */ if (time - tracker->time > MOTION_TIMEOUT || tracker->time > time) break; /* Stop if direction changed */ dir &= tracker->dir; if (dir == 0) break; velocity = calculate_tracker_velocity(tracker, time); /* Stop if velocity differs too much from initial */ velocity_diff = fabs(initial_velocity - velocity); if (velocity_diff > MAX_VELOCITY_DIFF) break; result = velocity; } return result; } static double acceleration_profile(struct pointer_accelerator *accel, void *data, double velocity, uint32_t time) { return accel->profile(&accel->base, data, velocity, time); } static double calculate_acceleration(struct pointer_accelerator *accel, void *data, double velocity, uint32_t time) { double factor; /* Use Simpson's rule to calculate the avarage acceleration between * the previous motion and the most recent. */ factor = acceleration_profile(accel, data, velocity, time); factor += acceleration_profile(accel, data, accel->last_velocity, time); factor += 4.0 * acceleration_profile(accel, data, (accel->last_velocity + velocity) / 2, time); factor = factor / 6.0; return factor; } static double soften_delta(double last_delta, double delta) { if (delta < -1.0 || delta > 1.0) { if (delta > last_delta) return delta - 0.5; else if (delta < last_delta) return delta + 0.5; } return delta; } static void apply_softening(struct pointer_accelerator *accel, struct weston_motion_params *motion) { motion->dx = soften_delta(accel->last_dx, motion->dx); motion->dy = soften_delta(accel->last_dy, motion->dy); } static void accelerator_filter(struct weston_motion_filter *filter, struct weston_motion_params *motion, void *data, uint32_t time) { struct pointer_accelerator *accel = (struct pointer_accelerator *) filter; double velocity; double accel_value; feed_trackers(accel, motion->dx, motion->dy, time); velocity = calculate_velocity(accel, time); accel_value = calculate_acceleration(accel, data, velocity, time); motion->dx = accel_value * motion->dx; motion->dy = accel_value * motion->dy; apply_softening(accel, motion); accel->last_dx = motion->dx; accel->last_dy = motion->dy; accel->last_velocity = velocity; } static void accelerator_destroy(struct weston_motion_filter *filter) { struct pointer_accelerator *accel = (struct pointer_accelerator *) filter; free(accel->trackers); free(accel); } struct weston_motion_filter_interface accelerator_interface = { accelerator_filter, accelerator_destroy }; struct weston_motion_filter * create_pointer_accelator_filter(accel_profile_func_t profile) { struct pointer_accelerator *filter; filter = malloc(sizeof *filter); if (filter == NULL) return NULL; filter->base.interface = &accelerator_interface; wl_list_init(&filter->base.link); filter->profile = profile; filter->last_velocity = 0.0; filter->last_dx = 0; filter->last_dy = 0; filter->trackers = calloc(NUM_POINTER_TRACKERS, sizeof *filter->trackers); filter->cur_tracker = 0; return &filter->base; }