[gpm] [PATCH 13/18] evdev-tap-and-select.patch

Dmitry Torokhov dtor_core@ameritech.net
Tue Aug 10 08:59:52 CEST 2004


===================================================================


ChangeSet@1.17, 2004-08-10 00:53:32-05:00, dtor_core@ameritech.net
  Tap-and-select support in evdev driver (for "touchpad" and
  "synaptics" types).


 evdev.c          |  206 ++++++++++++++++++++++++++++++++++++++-----------------
 gpm.c            |   89 +++++++++++++++--------
 headers/gpmInt.h |    7 +
 server_tools.c   |    2 
 4 files changed, 209 insertions(+), 95 deletions(-)


===================================================================



diff -Nru a/src/evdev.c b/src/evdev.c
--- a/src/evdev.c	2004-08-10 01:18:00 -05:00
+++ b/src/evdev.c	2004-08-10 01:18:00 -05:00
@@ -71,6 +71,15 @@
    TOUCH_PALM
 };
 
+enum gesture_type {
+   GESTURE_NONE,
+   GESTURE_TAP_PENDING,
+   GESTURE_TAP,
+   GESTURE_DRAG_PENDING,
+   GESTURE_DRAG,
+   GESTURE_DOUBLE_TAP
+};
+
 enum edge_type {
    BOTTOM_EDGE = 1,
    TOP_EDGE = 2,
@@ -94,9 +103,11 @@
 
 struct touch_data {
    enum touch_type type;
+   enum gesture_type gesture;
    int finger_count;
    int x, y;
    int buttons;
+   int clicks;
    struct timeval start;
 };
 
@@ -222,18 +233,19 @@
 {
    struct event_data *pkt = &evdev->pkt;
 
+   state->dx = state->dy = 0;
    if (evdev->touch.type == TOUCH_FINGERS) {
       fx(0) = pkt->abs_x;
       fy(0) = pkt->abs_y;
-		if (evdev->pkt_count >= 2) {
-	      state->dx = ((fx(0) - fx(1)) / 2 + (fx(1) - fx(2)) / 2) / 8; //SYN_REL_DECEL_FACTOR;
-			state->dy = ((fy(0) - fy(1)) / 2 + (fy(1) - fy(2)) / 2) / 8; //SYN_REL_DECEL_FACTOR;
-		}
-		evdev->pkt_count++;
-   } else {
-      state->dx = state->dy = 0;
-		evdev->pkt_count = 0;
-	}
+      if (evdev->pkt_count >= 2 &&
+          evdev->touch.gesture != GESTURE_DRAG_PENDING) {
+         state->dx = ((fx(0) - fx(1)) / 2 + (fx(1) - fx(2)) / 2) / 8; //SYN_REL_DECEL_FACTOR;
+         state->dy = ((fy(0) - fy(1)) / 2 + (fy(1) - fy(2)) / 2) / 8; //SYN_REL_DECEL_FACTOR;
+      }
+      evdev->pkt_count++;
+   } else  {
+      evdev->pkt_count = 0;
+   }
 }
 
 static enum touch_type tp_detect_touch(struct event_device *evdev)
@@ -313,87 +325,122 @@
            abs(evdev->pkt.abs_y - evdev->touch.y) < evdev->tap_move);
 }
 
-static int tp_process_tap(struct event_device *evdev, struct Gpm_Event *state)
+static int tp_tap_to_buttons(struct event_device *evdev)
+{
+   enum edge_type edge;
+
+   if (!evdev->touch.finger_count) {
+      edge = tp_detect_edges(evdev, evdev->pkt.abs_x, evdev->pkt.abs_y);
+      switch (edge) {
+         case RIGHT_TOP_EDGE:
+            return GPM_B_MIDDLE;
+            break;
+         case RIGHT_BOTTOM_EDGE:
+            return GPM_B_RIGHT;
+            break;
+         default:
+            return GPM_B_LEFT;
+            break;
+      }
+   } else {
+      switch (evdev->touch.finger_count) {
+         case 2:
+            return GPM_B_MIDDLE;
+         case 3:
+            return GPM_B_RIGHT;
+         default:
+            return GPM_B_LEFT;
+      }
+   }
+}
+
+static void tp_detect_gesture(struct event_device *evdev, int timed_out, struct Gpm_Event *state)
 {
    struct touch_data *touch = &evdev->touch;
    int was_touching = touch->type == TOUCH_FINGERS;
-   enum edge_type edge;
 
-   touch->type = evdev->type == EVDEV_SYNAPTICS ?
-                     syn_detect_touch(evdev) : tp_detect_touch(evdev);
-   if (touch->type != TOUCH_PALM) {
-      if (touch->type == TOUCH_FINGERS && !was_touching) {
+   touch->type = timed_out ?
+                     TOUCH_NONE : (evdev->type == EVDEV_SYNAPTICS ?
+                                   syn_detect_touch(evdev) : tp_detect_touch(evdev));
+
+   if (touch->type == TOUCH_FINGERS) {
+      if (!was_touching) {
          GET_TIME(touch->start);
-         touch->x = evdev->pkt.abs_x;
-         touch->y = evdev->pkt.abs_y;
-         touch->buttons = 0;
-      } else if (was_touching && touch->type == TOUCH_NONE) {
+         if (touch->gesture == GESTURE_TAP_PENDING) {
+            touch->gesture = GESTURE_DRAG_PENDING;
+         } else {
+            touch->x = evdev->pkt.abs_x;
+            touch->y = evdev->pkt.abs_y;
+            touch->buttons = 0;
+         }
+      } else if (touch->gesture == GESTURE_DRAG_PENDING && tp_touch_expired(evdev)) {
+         touch->gesture = GESTURE_DRAG;
+      }
+   } else if (touch->type == TOUCH_NONE) {
+      if (was_touching) {
          if (tp_detect_tap(evdev)) {
-            edge = tp_detect_edges(evdev, evdev->pkt.abs_x, evdev->pkt.abs_y);
-            if (!touch->finger_count) {
-               switch (edge) {
-                  case RIGHT_TOP_EDGE:
-                     touch->buttons |= GPM_B_MIDDLE;
-                     break;
-                  case RIGHT_BOTTOM_EDGE:
-                     touch->buttons |= GPM_B_RIGHT;
-                     break;
-                  default:
-                     touch->buttons |= GPM_B_LEFT;
-                     break;
-               }
+            if (touch->gesture == GESTURE_DRAG_PENDING) {
+               touch->gesture = GESTURE_DOUBLE_TAP;
+               touch->clicks = 4;
             } else {
-               switch (touch->finger_count) {
-                  case 2:
-                     touch->buttons |= GPM_B_MIDDLE;
-                     break;
-                  case 3:
-                     touch->buttons |= GPM_B_RIGHT;
-                     break;
-                  default:
-                     touch->buttons |= GPM_B_LEFT;
-                     break;
+               if ((touch->buttons = tp_tap_to_buttons(evdev)) == GPM_B_LEFT) {
+                  touch->gesture = GESTURE_TAP_PENDING;
+               } else {
+                  touch->gesture = GESTURE_TAP;
+                  touch->clicks = 2;
                }
             }
-            return 1;
+         } else {
+            touch->gesture = GESTURE_NONE;
+         }
+      } else {
+         if (touch->gesture == GESTURE_TAP_PENDING && tp_touch_expired(evdev)) {
+            touch->gesture = GESTURE_TAP;
+            touch->clicks = 2;
          }
       }
    }
-   return 0;
 }
 
-static void compose_gpm_event(struct event_device *evdev, Gpm_Event *state)
+static int compose_gpm_event(struct event_device *evdev, int timed_out, Gpm_Event *state)
 {
    struct event_data *pkt = &evdev->pkt;
+   int next_timeout = -1;
 
 	state->buttons = pkt->buttons;
    state->wdx = pkt->wdx; state->wdy = pkt->wdy;
 
    switch (evdev->type) {
       case EVDEV_RELATIVE:
-         state->dx = pkt->dx; state->dy = pkt->dy;
+         if (!timed_out)
+            state->dx = pkt->dx; state->dy = pkt->dy;
          break;
 
       case EVDEV_ABSOLUTE:
-         if (pkt->abs_x < evdev->left_edge)
-            pkt->abs_x = evdev->left_edge;
-         else if (pkt->abs_x > evdev->right_edge)
-            pkt->abs_x = evdev->right_edge;
-         if (pkt->abs_y < evdev->bottom_edge)
-            pkt->abs_y = evdev->bottom_edge;
-         else if (pkt->abs_y > evdev->top_edge)
-            pkt->abs_y = evdev->top_edge;
-         state->x = (pkt->abs_x - evdev->left_edge) *
+         if (!timed_out) {
+            if (pkt->abs_x < evdev->left_edge)
+               pkt->abs_x = evdev->left_edge;
+            else if (pkt->abs_x > evdev->right_edge)
+               pkt->abs_x = evdev->right_edge;
+
+            if (pkt->abs_y < evdev->bottom_edge)
+               pkt->abs_y = evdev->bottom_edge;
+            else if (pkt->abs_y > evdev->top_edge)
+               pkt->abs_y = evdev->top_edge;
+
+            state->x = (pkt->abs_x - evdev->left_edge) *
                         console.max_x / (evdev->right_edge - evdev->left_edge);
-         state->y = (pkt->abs_y - evdev->bottom_edge) *
+            state->y = (pkt->abs_y - evdev->bottom_edge) *
                         console.max_y / (evdev->top_edge - evdev->bottom_edge);
-         if (evdev->y_inverted) state->y = console.max_y - state->y;
+
+            if (evdev->y_inverted) state->y = console.max_y - state->y;
+         }
          break;
 
       case EVDEV_TOUCHPAD:
       case EVDEV_SYNAPTICS:
-         if (tp_process_tap(evdev, state))
-            state->buttons |= evdev->touch.buttons;
+         tp_detect_gesture(evdev, timed_out, state);
+         tp_figure_deltas(evdev, state);
 
          if (evdev->type == EVDEV_SYNAPTICS &&
              evdev->touch.type == TOUCH_FINGERS &&
@@ -405,10 +452,38 @@
          } else
             evdev->touch.finger_count = 0;
 
-         tp_figure_deltas(evdev, state);
+         switch(evdev->touch.gesture) {
+            case GESTURE_DOUBLE_TAP:
+            case GESTURE_TAP:
+               if (evdev->touch.clicks <= 0) {
+                  evdev->touch.gesture = GESTURE_NONE;
+               } else {
+                  if (--evdev->touch.clicks % 2)
+                     state->buttons |= evdev->touch.buttons;
+                  else
+                     state->buttons &= ~evdev->touch.buttons;
+                  next_timeout = 0;
+               }
+               break;
+           case GESTURE_DRAG:
+               state->buttons |= evdev->touch.buttons;
+               break;
+            case GESTURE_DRAG_PENDING:
+               next_timeout = evdev->tap_time;
+               break;
+            case GESTURE_TAP_PENDING:
+               next_timeout = evdev->tap_time;
+               break;
+            default:
+               break;
+         }
+
          break;
    }
+
    if (evdev->y_inverted) state->dy = -state->dy;
+
+   return next_timeout;
 }
 
 int M_evdev(struct micedev *dev, struct miceopt *opts,
@@ -416,13 +491,18 @@
 {
    struct event_device *evdev = dev->private;
    struct input_event *event = (struct input_event *)data;
+   int timed_out = data == NULL;
 
-   parse_input_event(event, &evdev->pkt);
-   if (evdev->pkt.synced || evdev->dont_sync) {
-      compose_gpm_event(evdev, state);
+   if (!timed_out)
+      parse_input_event(event, &evdev->pkt);
+
+   if (timed_out || evdev->pkt.synced || evdev->dont_sync) {
+      dev->timeout = compose_gpm_event(evdev, timed_out, state);
       evdev->pkt.synced = 0;
       return 0;
    }
+
+   dev->timeout = -1;
    return -1;
 }
 
diff -Nru a/src/gpm.c b/src/gpm.c
--- a/src/gpm.c	2004-08-10 01:18:00 -05:00
+++ b/src/gpm.c	2004-08-10 01:18:00 -05:00
@@ -5,6 +5,7 @@
  * Copyright (C) 1994-1999   Alessandro Rubini <rubini@linux.it>
  * Copyright (C) 1998        Ian Zimmerman <itz@rahul.net>
  * Copyright (c) 2001,2002   Nico Schottelius <nico-gpm@schottelius.org>
+ * Copyright (c) 2003        Dmitry Torokhov <dtor@mail.ru>
  *
  *   This program is free software; you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -24,6 +25,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>        /* strerror(); ?!?  */
+#include <limits.h>
 #include <errno.h>
 #include <unistd.h>        /* select(); */
 #include <signal.h>        /* SIGPIPE */
@@ -41,8 +43,11 @@
 #define max(a,b) ((a)>(b) ? (a) : (b))
 #endif
 
+#ifndef min
+#define min(a,b) ((a)<(b) ? (a) : (b))
+#endif
+
 #define NULL_SET ((fd_set *)NULL)
-#define resetTimeout() (timeout.tv_sec = SELECT_TIME, timeout.tv_usec = 0)
 #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *)NULL))
 #define DIF_TIME(t1,t2) ((t2.tv_sec - t1.tv_sec)*1000 + (t2.tv_usec - t1.tv_usec)/1000)
 
@@ -163,7 +168,7 @@
 
    if (absolute_dev) {
       /* prepare the values from a absolute device for repeater mode */
-      gettimeofday(&now, (struct timezone *)NULL);
+      GET_TIME(now);
       if (DIF_TIME(last, now) > 250) {
          event->dx = 0;
          event->dy = 0;
@@ -295,7 +300,7 @@
  * to retrieve the hardware independent event data, then optionally repeat
  * the data via repeat_fun() to the repeater device
  *-------------------------------------------------------------------*/
-static enum mouse_rslt processMouse(struct micetab *mouse, int attempt,
+static enum mouse_rslt processMouse(struct micetab *mouse, int timeout, int attempt,
                                     Gpm_Event *event, int text_mode)
 {
    static int last_active;
@@ -307,7 +312,7 @@
    struct miceopt *opt = &mouse->options;
    enum mouse_rslt rslt = MOUSE_DATA_OK;
    unsigned char shift_state;
-   char *data;
+   char *data = NULL;
    int i;
 
    if (attempt > 1) {          /* continue interrupted cluster loop */
@@ -325,7 +330,12 @@
       i = 0;
 
       do { /* cluster loop */
-         if ((data = getMouseData(mouse->dev.fd, type, text_mode)) == NULL ||
+         if (!timeout && (data = getMouseData(mouse->dev.fd, type, text_mode)) != NULL) {
+            GET_TIME(mouse->timestamp);
+         }
+
+         /* in case of timeout data passed to typr->fun() is NULL */
+         if ((!timeout && data == NULL) ||
              type->fun(&mouse->dev, &mouse->options, data, &nEvent) == -1) {
             if (!i) return MOUSE_NO_DATA;
             else break;
@@ -438,18 +448,49 @@
    return rslt;
 }
 
+static int wait_for_data(fd_set *connSet, int maxfd, fd_set *selSet)
+{
+   struct micetab *mouse;
+   struct timeval now, timeout = { 0, 0 };
+   int mouse_tmo, tmo = INT_MAX;
+
+   GET_TIME(now);
+
+   *selSet = *connSet;
+   for (mouse = micelist; mouse; mouse = mouse->next) {
+      FD_SET(mouse->dev.fd, selSet);
+      maxfd = max(maxfd, mouse->dev.fd);
+      if (mouse->dev.timeout >= 0) {
+         mouse_tmo = mouse->dev.timeout - DIF_TIME(mouse->timestamp, now);
+         tmo = min(tmo, mouse_tmo);
+      }
+   }
+
+   if (tmo == INT_MAX)
+      timeout.tv_sec = SELECT_TIME;
+   else if (tmo > 0) {
+      timeout.tv_sec = tmo / 1000;
+      timeout.tv_usec = (tmo % 1000) * 1000;
+   }
+
+   return select(maxfd + 1, selSet, NULL_SET, NULL_SET, &timeout);
+}
+
+
+
 /*-------------------------------------------------------------------*/
 int old_main()
 {
    int ctlfd;
    int i, text_mode;
-   struct timeval timeout;
+   struct timeval now;
    int maxfd = -1;
    int pending, attempt;
+   int timed_out;
    Gpm_Event event;
    struct micetab *mouse;
    struct client_info *ci;
-   fd_set selSet, readySet, connSet;
+   fd_set selSet, connSet;
    enum mouse_rslt rslt;
 
    /*....................................... catch interesting signals */
@@ -468,23 +509,11 @@
    FD_SET(ctlfd, &connSet);
    maxfd = max(maxfd, ctlfd);
 
-   for (mouse = micelist; mouse; mouse = mouse->next) {
-      FD_SET(mouse->dev.fd, &connSet);
-      maxfd = max(maxfd, mouse->dev.fd);
-   }
-
-   readySet = connSet;
-
    /*--------------------------------------- main loop begins here */
 
    while (1) {
-      selSet = readySet;
-      resetTimeout();
 
-      while ((pending = select(maxfd + 1, &selSet, NULL_SET, NULL_SET, &timeout)) == 0) {
-         selSet = readySet;
-         resetTimeout();
-      }
+      pending = wait_for_data(&connSet, maxfd, &selSet);
 
       if (console_resized) { /* did the console resize? */
          handle_console_resize(&event);
@@ -496,8 +525,6 @@
       if (pending < 0) {
          if (errno == EBADF) gpm_report(GPM_PR_OOPS, GPM_MESS_SELECT_PROB);
          gpm_report(GPM_PR_ERR, GPM_MESS_SELECT_STRING, strerror(errno));
-         selSet = readySet;
-         resetTimeout();
          continue;
       }
 
@@ -521,9 +548,6 @@
             gpm_report(GPM_PR_OOPS, GPM_MESS_OPEN, micelist->device);
          if (micelist->type->init)
             micelist->type->init(&micelist->dev, &micelist->options, micelist->type);
-         maxfd = max(maxfd, micelist->dev.fd);
-         readySet = connSet;
-         FD_SET(micelist->dev.fd, &readySet);
          continue; /* reselect */
       }
 
@@ -532,13 +556,18 @@
        * Well, actually, run a loop to maintain inlining of functions without
        * lenghtening the file. This is not too clean a code, but it works....
        */
+      GET_TIME(now);
       for (mouse = micelist; mouse; mouse = mouse->next) {
-         if (FD_ISSET(mouse->dev.fd, &selSet)) {
-            FD_CLR(mouse->dev.fd, &selSet);
-            pending--;
+         timed_out = mouse->dev.timeout >= 0 &&
+                     DIF_TIME(mouse->timestamp, now) >= mouse->dev.timeout;
+         if (timed_out || FD_ISSET(mouse->dev.fd, &selSet)) {
+            if (FD_ISSET(mouse->dev.fd, &selSet)) {
+               FD_CLR(mouse->dev.fd, &selSet);
+               pending--;
+            }
             attempt = 0;
             do {
-               rslt = processMouse(mouse, ++attempt, &event, text_mode);
+               rslt = processMouse(mouse, timed_out, ++attempt, &event, text_mode);
                if (rslt != MOUSE_NO_DATA) {
                   /* pass it to the client or to the default handler,
                    * or to the selection handler
@@ -563,7 +592,6 @@
                do_client(ci, &e);
             }
             FD_SET(ci->fd, &connSet);
-            FD_SET(ci->fd, &readySet);
             maxfd = max(maxfd, ci->fd);
          }
       }
@@ -578,7 +606,6 @@
                if (!process_client_request(ci, i, event.x, event.y, event.clicks,
                                            event.buttons, micelist->options.three_button)) {
                   FD_CLR(ci->fd, &connSet);
-                  FD_CLR(ci->fd, &readySet);
                   remove_client(ci, i);
                }
             }
diff -Nru a/src/headers/gpmInt.h b/src/headers/gpmInt.h
--- a/src/headers/gpmInt.h	2004-08-10 01:18:00 -05:00
+++ b/src/headers/gpmInt.h	2004-08-10 01:18:00 -05:00
@@ -23,6 +23,7 @@
 #ifndef _GPMINT_INCLUDED
 #define _GPMINT_INCLUDED
 
+#include <sys/time.h>   /* timeval */
 #include "gpm.h"
 
 #if !defined(__GNUC__)
@@ -100,7 +101,10 @@
 
 struct micedev {
    int   fd;
-   void  *private;
+   int   timeout;             /* the protocol driver wants to be called 
+                                 after X msec even if there is no new data
+                                 arrived (-1 to disable/default) */
+   void  *private;            /* private data maintained by protocol driver */
 };
 
 struct miceopt {
@@ -163,6 +167,7 @@
    Gpm_Type       *type;
    char           *device;
    int            buttons;    /* mouse's button state from last read */
+   struct timeval timestamp;  /* last time mouse data arrived */ 
 };
 
 struct options {
diff -Nru a/src/server_tools.c b/src/server_tools.c
--- a/src/server_tools.c	2004-08-10 01:18:00 -05:00
+++ b/src/server_tools.c	2004-08-10 01:18:00 -05:00
@@ -43,6 +43,8 @@
 
    memset(mouse, 0, sizeof(struct micetab));
 
+   mouse->dev.timeout = -1;
+
    mouse->options.sequence = NULL;
    mouse->options.sample = DEF_SAMPLE;
    mouse->options.delta = DEF_DELTA;


More information about the gpm mailing list