On Fri, Nov 24, 2006 at 03:13:06PM +0100, Denis Oliver Kropp wrote:
> Attilio Fiandrotti wrote:
> > i recently did a survey between debian user asking to run an
> > experimental iso image including this patch: most users reported full
> > success, few of them reported excessive tap sensitivity, but no failures
> > ATM.
> > so i guess this patch should be ok for a wide rage of touchpads.
>
> I did some more testing and I accidentally click in about half of the
> land-move-take-off cycles.
Inspired by Attilio's work (thanks) I wanted to improve the driver a
little more. I can't live without drag support so I started to add that
and then I wanted to slow the cursor down a bit so I tried to add some
rudimentary acceleration stuff. The pressure handling was removed but I
suppose it could be added back in. I've attached my current code. It
feels quite good to me but I'd like to get other people's opinion before
I go and mess up the code currently in cvs...
--
Ville Syrjälä
[EMAIL PROTECTED]
http://www.sci.fi/~syrjala/
Index: inputdrivers/linux_input/linux_input.c
===================================================================
RCS file: /cvs/directfb/DirectFB/inputdrivers/linux_input/linux_input.c,v
retrieving revision 1.53
diff -u -r1.53 linux_input.c
--- inputdrivers/linux_input/linux_input.c 24 Nov 2006 14:02:55 -0000
1.53
+++ inputdrivers/linux_input/linux_input.c 27 Nov 2006 21:43:18 -0000
@@ -290,31 +290,39 @@
/*
* Touchpads related stuff
*/
-#define TOUCHPAD_SCALING_FACTOR 5
-#define TOUCHPAD_FSM_START 0
-#define TOUCHPAD_FSM_MAIN 1
-#define TOUCHPAD_FSM_END 2
+enum {
+ TOUCHPAD_FSM_START,
+ TOUCHPAD_FSM_MAIN,
+ TOUCHPAD_FSM_DRAG_START,
+ TOUCHPAD_FSM_DRAG_MAIN,
+};
-/* expressed in usecs
+/* expressed in usecs
*/
-#define TOUCHPAD_SINGLE_CLICK_TIMEOUT 250000
+#define TOUCHPAD_FSM_TIMEOUT 125000
-/* lower touchpad pressure treshold for the mouse cursor to start moving
- */
-#define TOUCHPAD_START_MOTION_PRESSURE_TRESHOLD 20
+struct touchpad_fsm_state {
+ int fsm_state;
+ int x_old, y_old, dx, dy;
+ int dx_old, dy_old, x_accel, y_accel;
+ struct timeval timeout_abs;
+ struct timeval timeout_rel;
+};
-/* motion is reduced in a indirectly proportional way to finger pressure,
- * this is the unit treshold
- */
-#define TOUCHPAD_SCALING_FACTOR_PRESSURE 40
+static void
+touchpad_fsm_init( struct touchpad_fsm_state *state );
-int x_old = -1, y_old = -1, dx, dy, last_pressure, fire_single_click_flag = 0;
-struct timeval *last_mousetouch;
-int touchpad_state = TOUCHPAD_FSM_START;
+static int
+touchpad_fsm( struct touchpad_fsm_state *state,
+ struct input_event *levt,
+ DFBInputEvent *devt );
+static void
+timeout_clear( struct timeval *timeout );
+static int
+timeout_is_set( struct timeval *timeout );
static int
-touchpad_fsm ( struct input_event *levt,
- DFBInputEvent *devt );
+timeout_passed( struct timeval *timeout, struct timeval *current );
/*
* Translates a Linux input keycode into a DirectFB keycode.
@@ -733,15 +741,52 @@
linux_input_EventThread( DirectThread *thread, void *driver_data )
{
LinuxInputData *data = (LinuxInputData*) driver_data;
- int readlen;
+ int readlen, status;
struct input_event levt[64];
+ fd_set rfds;
+ struct touchpad_fsm_state fsm_state;
+ struct timeval time;
+ int i;
- last_mousetouch = malloc ( sizeof(struct timeval));
+ FD_ZERO( &rfds );
+ touchpad_fsm_init( &fsm_state );
- while ((readlen = read(data->fd, levt, sizeof(levt)) / sizeof(levt[0])) > 0
- || (readlen < 0 && errno == EINTR))
- {
- int i;
+ while (1) {
+ FD_SET( data->fd, &rfds );
+
+ if (timeout_is_set( &fsm_state.timeout_abs )) {
+ gettimeofday(&time, NULL);
+
+ if (!timeout_passed( &fsm_state.timeout_abs, &time )) {
+ time = fsm_state.timeout_rel;
+ status = select( data->fd + 1, &rfds, NULL, NULL, &time );
+ } else {
+ status = 0;
+ }
+ }
+ else {
+ status = select( data->fd + 1, &rfds, NULL, NULL, NULL );
+ }
+
+ if (status < 0 && errno != EINTR)
+ break;
+
+ direct_thread_testcancel( thread );
+
+ /* timeout? */
+ if (status == 0) {
+ DFBInputEvent devt;
+
+ if (touchpad_fsm( &fsm_state, NULL, &devt ) > 0)
+ dfb_input_dispatch( data->device, &devt );
+
+ continue;
+ }
+
+ readlen = read( data->fd, levt, sizeof(levt) ) / sizeof(levt[0]);
+
+ if (readlen < 0 && errno != EINTR)
+ break;
direct_thread_testcancel( thread );
@@ -750,13 +795,16 @@
for (i=0; i<readlen; i++) {
DFBInputEvent devt;
-//printf("levt->type = %d, levt->code = %d, levt->value = %d\n", levt[i].type,
levt[i].code, levt->value );
- if (!translate_event( &levt[i], &devt ))
- continue;
- if ( (devt.type == DIET_AXISMOTION && (devt.flags &
DIEF_AXISABS)) || levt[i].code == BTN_TOUCH || ( levt[i].type == EV_ABS &&
levt[i].code == ABS_PRESSURE ) ) {
- if (touchpad_fsm ( &levt[i], &devt ) == 0)
+ /* FIXME detect the touchpad in init? */
+ status = touchpad_fsm( &fsm_state, &levt[i], &devt );
+ if (status < 0) {
+ /* Not handled. Try the direct approach. */
+ if (!translate_event( &levt[i], &devt ))
continue;
+ } else if (status == 0) {
+ /* Handled but no further processing is necessary. */
+ continue;
}
if (devt.type == DIET_AXISMOTION && (devt.flags &
DIEF_AXISREL)) {
@@ -789,7 +837,7 @@
flush_xy( data );
}
- if (readlen <= 0)
+ if (status <= 0)
D_PERROR ("linux_input thread died\n");
return NULL;
@@ -1156,103 +1204,286 @@
D_FREE( data );
}
+static int
+timeout_is_set( struct timeval *timeout )
+{
+ return timeout->tv_sec && timeout->tv_usec;
+}
+
+static int
+timeout_passed( struct timeval *timeout, struct timeval *current )
+{
+ /* not set */
+ if (!timeout_is_set( timeout ))
+ return 1;
+
+ if (current->tv_usec >= 1000000000) {
+ D_ERROR("AIEE");
+ return 0;
+ }
+
+ return current->tv_sec > timeout->tv_sec ||
+ (current->tv_sec == timeout->tv_sec && current->tv_usec >
timeout->tv_usec);
+}
+
+static void
+timeout_clear( struct timeval *timeout )
+{
+ timeout->tv_sec = 0;
+ timeout->tv_usec = 0;
+}
+
+static void
+timeout_set( struct timeval *timeout, struct timeval *base, struct timeval
*rel )
+{
+ timeout->tv_sec = base->tv_sec + rel->tv_sec;
+ timeout->tv_usec = base->tv_usec + rel->tv_usec;
+ while (timeout->tv_usec > 1000000000) {
+ timeout->tv_sec++;
+ timeout->tv_usec -= 1000000000;
+ }
+}
+
+#define MIN_ACCEL 10
+#define MAX_ACCEL 1
+#define ACCEL_INC_THRESHOLD 15
+#define ACCEL_DEC_THRESHOLD -15
+static void
+touchpad_fsm_init( struct touchpad_fsm_state *state )
+{
+ state->dx_old = 0;
+ state->dy_old = 0;
+ state->x_accel = MIN_ACCEL;
+ state->y_accel = MIN_ACCEL;
+
+ state->x_old = -1;
+ state->y_old = -1;
+ state->fsm_state = TOUCHPAD_FSM_START;
+ state->timeout_rel.tv_sec = 0;
+ state->timeout_rel.tv_usec = TOUCHPAD_FSM_TIMEOUT;
+ timeout_clear(&state->timeout_abs);
+};
+
+static int touchpad_translate( struct touchpad_fsm_state *state,
+ struct input_event *levt,
+ DFBInputEvent *devt )
+{
+ devt->flags = DIEF_TIMESTAMP | DIEF_AXISREL;
+ devt->timestamp = levt->time;
+ devt->type = DIET_AXISMOTION;
+ int ddx, ddy;
+
+ switch (levt->code) {
+ case ABS_X:
+ if (state->x_old == -1)
+ state->x_old = levt->value;
+ state->dx = levt->value - state->x_old;
+
+ /* change direction? */
+ if (state->dx * state->dx_old < 0) {
+ state->x_accel = MIN_ACCEL;
+ state->dx_old = state->dx;
+ }
+
+ ddx = state->dx - state->dx_old;
+ if (ddx > ACCEL_INC_THRESHOLD) {
+ if (state->x_accel > MAX_ACCEL)
+ state->x_accel--;
+ state->dx_old = state->dx;
+ }
+ else if (ddx < ACCEL_DEC_THRESHOLD) {
+ if (state->x_accel < MIN_ACCEL)
+ state->x_accel++;
+ state->dx_old = state->dx;
+ }
+
+ state->dx /= state->x_accel;
+ state->x_old = levt->value;
+
+ devt->axis = DIAI_X;
+ devt->axisrel = state->dx;
+ return 1;
+
+ case ABS_Y:
+ if (state->y_old == -1)
+ state->y_old = levt->value;
+ state->dy = levt->value - state->y_old;
+
+ /* change direction? */
+ if (state->dy * state->dy_old < 0) {
+ state->y_accel = MIN_ACCEL;
+ state->dy_old = state->dy;
+ }
+
+ ddy = state->dy - state->dy_old;
+ if (ddy > ACCEL_INC_THRESHOLD) {
+ if (state->y_accel > MAX_ACCEL)
+ state->y_accel--;
+ state->dy_old = state->dy;
+ }
+ else if (ddy < ACCEL_DEC_THRESHOLD) {
+ if (state->y_accel < MIN_ACCEL)
+ state->y_accel++;
+ state->dy_old = state->dy;
+ }
+
+ state->dy /= state->y_accel;
+ state->y_old = levt->value;
+
+ devt->axis = DIAI_Y;
+ devt->axisrel = state->dy;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
/*
* This FSM takes into accout finger landing on touchpad and leaving and
* translates absolute DFBInputEvent into a relative one
*/
static int
-touchpad_fsm ( struct input_event *levt,
- DFBInputEvent *devt )
+touchpad_fsm( struct touchpad_fsm_state *state,
+ struct input_event *levt,
+ DFBInputEvent *devt )
{
- int ret_val;
+ /* select() timeout? */
+ if (!levt) {
+ /* Check if button release is due. */
+ if (state->fsm_state == TOUCHPAD_FSM_DRAG_START) {
+ devt->flags = DIEF_TIMESTAMP;
+ devt->timestamp = state->timeout_abs; /* timeout_abs of current
time? */
+ devt->type = DIET_BUTTONRELEASE;
+ devt->button = DIBI_FIRST;
- if ( levt->type == EV_ABS && levt->code == ABS_PRESSURE ) {
- last_pressure = levt->value;
- return 0;
+ touchpad_fsm_init(state);
+ return 1;
+ }
+
+ /* Already passed, clear it so select() won't return until there is
something to do. */
+ timeout_clear( &state->timeout_abs );
+ return 0;
}
- switch (touchpad_state) {
+ /* More or less ignore these events for now */
+ if ((levt->type == EV_SYN && levt->code == SYN_REPORT) ||
+ (levt->type == EV_ABS && levt->code == ABS_PRESSURE) ||
+ (levt->type == EV_ABS && levt->code == ABS_TOOL_WIDTH) ||
+ (levt->type == EV_KEY && levt->code == BTN_TOOL_FINGER)) {
+
+ /* Check if button release is due. */
+ if (state->fsm_state == TOUCHPAD_FSM_DRAG_START &&
+ timeout_passed( &state->timeout_abs, &levt->time )) {
+ devt->flags = DIEF_TIMESTAMP;
+ devt->timestamp = state->timeout_abs; /* timeout_abs of
levt->time? */
+ devt->type = DIET_BUTTONRELEASE;
+ devt->button = DIBI_FIRST;
- case TOUCHPAD_FSM_START:
- /* finger is landing */
- if ((levt->type == EV_KEY && levt->code == BTN_TOUCH &&
levt->value == 1) || (last_pressure > TOUCHPAD_START_MOTION_PRESSURE_TRESHOLD))
{
- last_mousetouch->tv_sec = (levt->time).tv_sec;
- last_mousetouch->tv_usec = (levt->time).tv_usec;
- if (last_pressure >
TOUCHPAD_START_MOTION_PRESSURE_TRESHOLD)
- touchpad_state = TOUCHPAD_FSM_MAIN;
- }
- ret_val = 0;
- break;
+ touchpad_fsm_init( state );
+ return 1;
+ }
- case TOUCHPAD_FSM_MAIN:
- /* translating mouse movements into relative coordinates */
- if (levt->type == EV_ABS && (levt->code == ABS_X || levt->code
== ABS_Y)) {
- switch (devt->axis) {
- case DIAI_X:
- if (x_old == -1)
- x_old = devt->axisabs;
- dx = (devt->axisabs - x_old ) /
TOUCHPAD_SCALING_FACTOR;
- dx = dx * (last_pressure/
TOUCHPAD_SCALING_FACTOR_PRESSURE);
- x_old = devt->axisabs;
- devt->axisrel = dx;
- devt->flags = devt->flags |= DIEF_AXISABS;
- devt->flags = devt->flags |= DIEF_AXISREL;
- ret_val = 1;
- break;
- case DIAI_Y:
- if (y_old == -1)
- y_old = devt->axisabs;
- dy = (devt->axisabs - y_old ) /
TOUCHPAD_SCALING_FACTOR;
- dy = dy * (last_pressure/
TOUCHPAD_SCALING_FACTOR_PRESSURE);
- y_old = devt->axisabs;
- devt->axisrel = dy;
- devt->flags = devt->flags |= DIEF_AXISABS;
- devt->flags = devt->flags |= DIEF_AXISREL;
- ret_val = 1;
- break;
- default:
- ret_val = 0;
- break;
- }
- }
+ return 0;
+ }
- /* finger is leaving */
- else if (levt->type == EV_KEY && levt->code == BTN_TOUCH &&
levt->value == 0) {
- if ( ((levt->time).tv_sec * 1000000 +
(levt->time).tv_usec) -\
- (last_mousetouch->tv_sec * 1000000 +
last_mousetouch->tv_usec)\
- <= TOUCHPAD_SINGLE_CLICK_TIMEOUT) {
- devt->type = DIET_BUTTONPRESS;
- devt->button = DIBI_FIRST;
- fire_single_click_flag = 1;
- ret_val = 1;
- }
- else
- ret_val = 0;
+ /* FIXME detect the touchpad in init instead of guessing based on the
events we receive here? */
+ if (!(levt->type == EV_ABS && levt->code == ABS_X) &&
+ !(levt->type == EV_ABS && levt->code == ABS_Y) &&
+ !(levt->type == EV_KEY && levt->code == BTN_TOUCH)) {
+
+ /* This crude check tries to make sure we don't print this error for
non-touchpads. */
+ if (state->fsm_state != TOUCHPAD_FSM_START)
+ D_ERROR("DirectFB/linux_input: Unexpected event in touchpad FSM
(%d %d %d)\n",
+ levt->type, levt->code, levt->value);
+
+ return -1;
+ }
+
+ switch (state->fsm_state) {
+ case TOUCHPAD_FSM_START:
+ /* finger is landing */
+ if (levt->type == EV_KEY && levt->code == BTN_TOUCH && levt->value
== 1) {
+ state->fsm_state = TOUCHPAD_FSM_MAIN;
+ timeout_set( &state->timeout_abs, &levt->time,
&state->timeout_rel );
+ }
+ return 0;
- touchpad_state = TOUCHPAD_FSM_END;
+ case TOUCHPAD_FSM_MAIN:
+ /* translating mouse movements into relative coordinates */
+ if (levt->type == EV_ABS && (levt->code == ABS_X || levt->code ==
ABS_Y)) {
+ if (timeout_passed( &state->timeout_abs, &levt->time )) {
+ timeout_clear( &state->timeout_abs );
+ return touchpad_translate( state, levt, devt );
+ }
+ }
+ /* finger is leaving */
+ else if (levt->type == EV_KEY && levt->code == BTN_TOUCH &&
levt->value == 0) {
+ if (!timeout_passed( &state->timeout_abs, &levt->time )) {
+ devt->flags = DIEF_TIMESTAMP;
+ devt->timestamp = levt->time;
+ devt->type = DIET_BUTTONPRESS;
+ devt->button = DIBI_FIRST;
+
+ state->dx_old = 0;
+ state->dy_old = 0;
+ state->x_accel = MIN_ACCEL;
+ state->y_accel = MIN_ACCEL;
+
+ state->x_old = -1;
+ state->y_old = -1;
+ state->fsm_state = TOUCHPAD_FSM_DRAG_START;
+ timeout_set( &state->timeout_abs, &levt->time,
&state->timeout_rel );
+ return 1;
}
- else
- ret_val = 0;
- break;
-
- case TOUCHPAD_FSM_END:
- x_old = y_old = last_pressure = -1;
- touchpad_state = TOUCHPAD_FSM_START;
-
- if (fire_single_click_flag == 1) {
- fire_single_click_flag = 0;
- devt->type = DIET_BUTTONRELEASE;
- devt->button = DIBI_FIRST;
- ret_val = 1;
+ else {
+ /* user just landed his finger on the pad for a while. */
+ touchpad_fsm_init( state );
}
- else
- ret_val = 0;
- break;
+ }
+ return 0;
- default:
- ret_val = 0;
- break;
+ case TOUCHPAD_FSM_DRAG_START:
+ if (timeout_passed( &state->timeout_abs, &levt->time )){
+ devt->flags = DIEF_TIMESTAMP;
+ devt->timestamp = state->timeout_abs; /* timeout_abs of
levt->time? */
+ devt->type = DIET_BUTTONRELEASE;
+ devt->button = DIBI_FIRST;
+
+ touchpad_fsm_init(state);
+ return 1;
+ } else {
+ /* finger is landing */
+ if (levt->type == EV_KEY && levt->code == BTN_TOUCH &&
levt->value == 1) {
+ state->fsm_state = TOUCHPAD_FSM_DRAG_MAIN;
+ timeout_set( &state->timeout_abs, &levt->time,
&state->timeout_rel );
+ }
+ }
+ return 0;
+
+ case TOUCHPAD_FSM_DRAG_MAIN:
+ /* translating mouse movements into relative coordinates */
+ if (levt->type == EV_ABS && (levt->code == ABS_X || levt->code ==
ABS_Y)) {
+ if (timeout_passed( &state->timeout_abs, &levt->time )) {
+ timeout_clear( &state->timeout_abs );
+ return touchpad_translate( state, levt, devt );
+ }
+ }
+ /* finger is leaving */
+ else if (levt->type == EV_KEY && levt->code == BTN_TOUCH &&
levt->value == 0) {
+ devt->flags = DIEF_TIMESTAMP;
+ devt->timestamp = levt->time;
+ devt->type = DIET_BUTTONRELEASE;
+ devt->button = DIBI_FIRST;
+
+ touchpad_fsm_init( state );
+ return 1;
+ }
+ return 0;
+
+ default:
+ return 0;
}
- return ret_val;
+ return 0;
}
_______________________________________________
directfb-dev mailing list
[email protected]
http://mail.directfb.org/cgi-bin/mailman/listinfo/directfb-dev