blob: 085cb9a2c52301f40785660cadc6e0dcdbf0fda2 [file] [log] [blame]
/*
* 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);
}