[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