=================================================================== ChangeSet@xxxx, 2004-08-10 00:53:32-05:00, dtor_core@xxxxxxxxxxxxx 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@xxxxxxxx> * Copyright (C) 1998 Ian Zimmerman <itz@xxxxxxxxx> * Copyright (c) 2001,2002 Nico Schottelius <nico-gpm@xxxxxxxxxxxxxxx> + * Copyright (c) 2003 Dmitry Torokhov <dtor@xxxxxxx> * * 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; _______________________________________________ gpm mailing list gpm@xxxxxxxxxxxxxx http://lists.linux.it/listinfo/gpm