| /* |
| * Linux mouse driver for Twin |
| * |
| * Copyright 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org> |
| * |
| * This Library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public License as |
| * published by the Free Software Foundation; either version 2 of the |
| * License, or (at your option) any later version. |
| * |
| * This Library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with the Twin Library; see the file COPYING. If not, |
| * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| * Boston, MA 02111-1307, USA. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <math.h> |
| |
| #include "twin_linux_mouse.h" |
| |
| #define QUADRATIC_ACCELERATION 1 |
| #define DEFAULT_ACC_NUMERATOR (2*TWIN_FIXED_ONE) |
| #define DEFAULT_ACC_DENOMINATOR TWIN_FIXED_ONE |
| #define DEFAULT_ACC_THRESHOLD (4*TWIN_FIXED_ONE) |
| |
| static void twin_linux_mouse_check_bounds(twin_linux_mouse_t *tm) |
| { |
| if (tm->x < 0) |
| tm->x = 0; |
| if (tm->x > tm->screen->width) |
| tm->x = tm->screen->width; |
| if (tm->y < 0) |
| tm->y = 0; |
| if (tm->y > tm->screen->height) |
| tm->y = tm->screen->height; |
| } |
| |
| /* This is directly copied from kdrive */ |
| static void twin_linux_mouse_accel(twin_linux_mouse_t *tm, int *dx, int *dy) |
| { |
| int square = *dx * *dx + *dy * *dy; |
| twin_fixed_t speed = square>65535 ? twin_int_to_fixed(256) : |
| twin_fixed_sqrt(twin_int_to_fixed(square)); |
| twin_fixed_t accel; |
| #ifdef QUADRATIC_ACCELERATION |
| twin_fixed_t m; |
| |
| /* |
| * Ok, so we want it moving num/den times faster at threshold*2 |
| * |
| * accel = m *threshold + b |
| * 1 = m * 0 + b -> b = 1 |
| * |
| * num/den = m * (threshold * 2) + 1 |
| * |
| * num / den - 1 = m * threshold * 2 |
| * (num / den - 1) / threshold * 2 = m |
| */ |
| m = twin_fixed_div(tm->acc_num, tm->acc_den - TWIN_FIXED_ONE); |
| m = twin_fixed_div(m, (2*tm->acc_threshold)); |
| accel = twin_fixed_mul(m, speed + TWIN_FIXED_ONE); |
| #else |
| accel = TWIN_FIXED_ONE; |
| |
| if (speed > tm->acc_threshold) |
| accel = twin_fixed_div(tm->acc_num, tm->acc_den); |
| #endif |
| *dx = twin_fixed_to_int(twin_fixed_mul(accel, twin_int_to_fixed(*dx))); |
| *dy = twin_fixed_to_int(twin_fixed_mul(accel, twin_int_to_fixed(*dy))); |
| } |
| |
| static twin_bool_t twin_linux_mouse_events(int file, twin_file_op_t ops, |
| void *closure) |
| { |
| twin_linux_mouse_t *tm = closure; |
| char evts[34]; |
| char *ep; |
| int n = tm->res_cnt; |
| twin_event_t tev; |
| |
| if (n) |
| memcpy(evts, tm->residual, n); |
| n += read(file, evts + n, 32); |
| |
| for(ep = evts; n >= 3; n -= 3, ep += 3) { |
| int dx, dy, btn; |
| dx = ep[1]; |
| if (ep[0] & 0x10) |
| dx -= 256; |
| dy = ep[2]; |
| if (ep[0] & 0x20) |
| dy -= 256; |
| dy = -dy; |
| /* we handle only one btn for now */ |
| btn = ep[0] & 0x1; |
| if (dx || dy) { |
| twin_linux_mouse_accel(tm, &dx, &dy); |
| tm->x += dx; |
| tm->y += dy; |
| twin_linux_mouse_check_bounds(tm); |
| tev.kind = TwinEventMotion; |
| tev.u.pointer.screen_x = tm->x; |
| tev.u.pointer.screen_y = tm->y; |
| tev.u.pointer.button = tm->btns; |
| twin_screen_dispatch (tm->screen, &tev); |
| } |
| if (btn != tm->btns) { |
| tm->btns = btn; |
| tev.kind = (btn & 0x1) ? |
| TwinEventButtonDown : TwinEventButtonUp; |
| tev.u.pointer.screen_x = tm->x; |
| tev.u.pointer.screen_y = tm->y; |
| tev.u.pointer.button = tm->btns; |
| twin_screen_dispatch(tm->screen, &tev); |
| } |
| } |
| tm->res_cnt = n; |
| if (n) |
| memcpy(tm->residual, ep, n); |
| |
| return 1; |
| } |
| |
| twin_linux_mouse_t *twin_linux_mouse_create(const char *file, |
| twin_screen_t *screen) |
| { |
| twin_linux_mouse_t *tm; |
| |
| tm = calloc(1, sizeof(twin_linux_mouse_t)); |
| if (tm == NULL) |
| return NULL; |
| |
| if (file == NULL) |
| file = "/dev/input/mice"; |
| |
| tm->screen = screen; |
| tm->acc_num = DEFAULT_ACC_NUMERATOR; |
| tm->acc_den = DEFAULT_ACC_DENOMINATOR; |
| tm->acc_threshold = DEFAULT_ACC_THRESHOLD; |
| tm->x = screen->width / 2; |
| tm->y = screen->height / 2; |
| tm->fd = open(file, O_RDONLY); |
| if (tm->fd < 0) { |
| free(tm); |
| return NULL; |
| } |
| |
| twin_set_file(twin_linux_mouse_events, tm->fd, TWIN_READ, tm); |
| |
| return tm; |
| } |
| |
| void twin_linux_mouse_destroy(twin_linux_mouse_t *tm) |
| { |
| close(tm->fd); |
| free(tm); |
| } |
| |
| void twin_linux_mouse_screen_changed(twin_linux_mouse_t *tm) |
| { |
| int oldx, oldy; |
| |
| oldx = tm->x; |
| oldy = tm->y; |
| twin_linux_mouse_check_bounds(tm); |
| if (tm->x != oldx || tm->y != oldy) { |
| twin_event_t tev; |
| |
| tev.kind = TwinEventMotion; |
| tev.u.pointer.screen_x = tm->x; |
| tev.u.pointer.screen_y = tm->y; |
| tev.u.pointer.button = tm->btns; |
| twin_screen_dispatch (tm->screen, &tev); |
| } |
| } |
| |
| void twin_linux_mouse_set_accel(twin_linux_mouse_t *tm, |
| int num, int den, int threshold) |
| { |
| tm->acc_num = twin_int_to_fixed(num); |
| tm->acc_den = twin_int_to_fixed(den); |
| tm->acc_threshold = twin_int_to_fixed(threshold); |
| } |