evdev: add minimal support for touchpads

The added support is very very basic (single touch, motion only,
no acceleration, no pressure recognition), but anything more
complex requires a state machine that will be hopefully provided
by libinputcommon in the future.
And at least, with this patch the pointer moves, which will be
useful for people testing wayland in 3.10 without a physical mouse.

https://bugzilla.gnome.org/show_bug.cgi?id=706494
This commit is contained in:
Giovanni Campagna 2013-09-09 10:51:11 +02:00
parent 986e46dc66
commit 89cd3112fe

View File

@ -25,6 +25,7 @@
#include "config.h"
#endif
#include <math.h>
#include <linux/input.h>
#include <sys/stat.h>
#include <sys/types.h>
@ -61,6 +62,12 @@
#define AUTOREPEAT_VALUE 2
/* Taken from weston/src/evdev-touchpad.c
In the future, we may want to make this configurable, or
delegate everything to loadable input drivers.
*/
#define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0
struct _ClutterDeviceManagerEvdevPrivate
{
GUdevClient *udev_client;
@ -130,7 +137,14 @@ struct _ClutterEventSource
GPollFD event_poll_fd; /* file descriptor of the /dev node */
struct libevdev *dev;
/* For mice (and emulated for touchpads) */
int dx, dy;
/* For touchpads */
gboolean touching;
int margin;
int last_x, last_y;
int cur_x, cur_y;
};
static gboolean
@ -447,6 +461,47 @@ notify_button (ClutterEventSource *source,
queue_event (event);
}
static void
process_absolute_motion (ClutterEventSource *source)
{
/* Compute deltas and update absolute positions */
if (source->touching)
{
source->dx = source->cur_x - source->last_x;
source->dy = source->cur_y - source->last_y;
source->last_x = source->cur_x;
source->last_y = source->cur_y;
}
}
static void
process_relative_motion (ClutterEventSource *source,
guint32 _time)
{
/* Flush accumulated motion deltas */
if (source->dx != 0 || source->dy != 0)
{
notify_relative_motion (source, _time, source->dx, source->dy);
source->dx = 0;
source->dy = 0;
}
}
static inline int
hysteresis (int in, int center, int margin)
{
int diff = in - center;
if (ABS (diff) <= margin)
return center;
if (diff > margin)
return center + diff - margin;
else if (diff < -margin)
return center + diff + margin;
return center + diff;
}
static void
dispatch_one_event (ClutterEventSource *source,
struct input_event *e)
@ -466,6 +521,15 @@ dispatch_one_event (ClutterEventSource *source,
if (e->value != AUTOREPEAT_VALUE)
notify_button (source, _time, e->code, e->value);
}
else if (e->code == BTN_TOOL_FINGER && e->value != AUTOREPEAT_VALUE)
{
source->touching = e->value;
if (e->value)
{
source->last_x = source->cur_x;
source->last_y = source->cur_y;
}
}
else
{
/* We don't know about this code, ignore */
@ -475,13 +539,8 @@ dispatch_one_event (ClutterEventSource *source,
case EV_SYN:
if (e->code == SYN_REPORT)
{
/* Flush accumulated motion deltas */
if (source->dx != 0 || source->dy != 0)
{
notify_relative_motion (source, _time, source->dx, source->dy);
source->dx = 0;
source->dy = 0;
}
process_absolute_motion (source);
process_relative_motion (source, _time);
}
break;
@ -510,6 +569,17 @@ dispatch_one_event (ClutterEventSource *source,
break;
case EV_ABS:
switch (e->code)
{
case ABS_X:
source->cur_x = hysteresis (e->value, source->cur_x, source->margin);
break;
case ABS_Y:
source->cur_y = hysteresis (e->value, source->cur_y, source->margin);
break;
}
break;
default:
g_warning ("Unhandled event of type %d", e->type);
break;
@ -646,6 +716,7 @@ clutter_event_source_new (ClutterInputDeviceEvdev *input_device)
const gchar *node_path;
gint fd, clkid;
GError *error;
ClutterInputDeviceType device_type;
/* grab the udev input device node and open it */
node_path = _clutter_input_device_evdev_get_device_path (input_device);
@ -684,6 +755,22 @@ clutter_event_source_new (ClutterInputDeviceEvdev *input_device)
event_source->event_poll_fd.events = G_IO_IN;
libevdev_new_from_fd (fd, &event_source->dev);
device_type = clutter_input_device_get_device_type (CLUTTER_INPUT_DEVICE (input_device));
if (device_type == CLUTTER_TOUCHPAD_DEVICE)
{
const struct input_absinfo *info_x, *info_y;
double width, height, diagonal;
info_x = libevdev_get_abs_info (event_source->dev, ABS_X);
info_y = libevdev_get_abs_info (event_source->dev, ABS_Y);
width = ABS (info_x->maximum - info_x->minimum);
height = ABS (info_y->maximum - info_y->minimum);
diagonal = sqrt (width * width + height * height);
event_source->margin = diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
}
/* and finally configure and attach the GSource */
g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS);
g_source_add_poll (source, &event_source->event_poll_fd);