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

Reply via email to