struct {
int min_x, max_x, min_y, max_y;
int old_x, old_y, reset_x, reset_y;
+ int slot_mt;
} abs;
- int is_touchpad;
+ int is_touchpad, is_mt;
};
/* event type flags */
-#define EVDEV_ABSOLUTE_MOTION (1 << 0)
-#define EVDEV_RELATIVE_MOTION (1 << 1)
+#define EVDEV_ABSOLUTE_MOTION (1 << 0)
+#define EVDEV_ABSOLUTE_MT_DOWN (1 << 1)
+#define EVDEV_ABSOLUTE_MT_MOTION (1 << 2)
+#define EVDEV_ABSOLUTE_MT_UP (1 << 3)
+#define EVDEV_RELATIVE_MOTION (1 << 4)
struct evdev_motion_accumulator {
int x, y;
int dx, dy;
+ int mt_x, mt_y;
int type; /* event type flags */
};
device->abs.reset_y = 1;
}
break;
-
case BTN_TOUCH:
+ /* Multitouch touchscreen devices might not send individually
+ * button events each time a new finger is down. So we don't
+ * send notification for such devices and we solve the button
+ * case emulating on compositor side. */
+ if (device->is_mt)
+ break;
+
/* Treat BTN_TOUCH from devices that only have BTN_TOUCH as
* BTN_LEFT */
e->code = BTN_LEFT;
}
}
+static void
+evdev_process_touch(struct evdev_input_device *device,
+ struct input_event *e, int time,
+ struct evdev_motion_accumulator *accum)
+{
+ const int screen_width = device->output->current->width;
+ const int screen_height = device->output->current->height;
+
+ switch (e->code) {
+ case ABS_MT_SLOT:
+ device->abs.slot_mt = e->value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ if (e->value >= 0)
+ accum->type |= EVDEV_ABSOLUTE_MT_DOWN;
+ else
+ accum->type |= EVDEV_ABSOLUTE_MT_UP;
+ break;
+ case ABS_MT_POSITION_X:
+ accum->mt_x = (e->value - device->abs.min_x) * screen_width /
+ (device->abs.max_x - device->abs.min_x) +
+ device->output->x;
+ accum->type |= EVDEV_ABSOLUTE_MT_MOTION;
+ break;
+ case ABS_MT_POSITION_Y:
+ accum->mt_y = (e->value - device->abs.min_y) * screen_height /
+ (device->abs.max_y - device->abs.min_y) +
+ device->output->y;
+ accum->type |= EVDEV_ABSOLUTE_MT_MOTION;
+ break;
+ }
+}
+
static inline void
evdev_process_absolute_motion(struct evdev_input_device *device,
struct input_event *e, struct evdev_motion_accumulator *accum)
accum->x = (e->value - device->abs.min_x) * screen_width /
(device->abs.max_x - device->abs.min_x) +
device->output->x;
- accum->type = EVDEV_ABSOLUTE_MOTION;
+ accum->type |= EVDEV_ABSOLUTE_MOTION;
break;
case ABS_Y:
accum->y = (e->value - device->abs.min_y) * screen_height /
(device->abs.max_y - device->abs.min_y) +
device->output->y;
- accum->type = EVDEV_ABSOLUTE_MOTION;
+ accum->type |= EVDEV_ABSOLUTE_MOTION;
break;
}
}
(device->abs.max_x - device->abs.min_x);
}
device->abs.old_x = e->value;
- accum->type = EVDEV_RELATIVE_MOTION;
+ accum->type |= EVDEV_RELATIVE_MOTION;
break;
case ABS_Y:
e->value -= device->abs.min_y;
(device->abs.max_y - device->abs.min_y);
}
device->abs.old_y = e->value;
- accum->type = EVDEV_RELATIVE_MOTION;
+ accum->type |= EVDEV_RELATIVE_MOTION;
break;
}
}
switch (e->code) {
case REL_X:
accum->dx += e->value;
- accum->type = EVDEV_RELATIVE_MOTION;
+ accum->type |= EVDEV_RELATIVE_MOTION;
break;
case REL_Y:
accum->dy += e->value;
- accum->type = EVDEV_RELATIVE_MOTION;
+ accum->type |= EVDEV_RELATIVE_MOTION;
break;
}
}
+static inline void
+evdev_process_absolute(struct evdev_input_device *device,
+ struct input_event *e, int time,
+ struct evdev_motion_accumulator *accum)
+{
+ if (device->is_touchpad) {
+ evdev_process_absolute_motion_touchpad(device, e, accum);
+ } else if (device->is_mt) {
+ evdev_process_touch(device, e, time, accum);
+ } else {
+ evdev_process_absolute_motion(device, e, accum);
+ }
+}
+
static int
is_motion_event(struct input_event *e)
{
switch (e->code) {
case ABS_X:
case ABS_Y:
+ case ABS_MT_POSITION_X:
+ case ABS_MT_POSITION_Y:
return 1;
}
}
}
static void
-evdev_reset_accum(struct wl_input_device *device,
- struct evdev_motion_accumulator *accum)
-{
- memset(accum, 0, sizeof *accum);
-
- /* There are cases where only one axis on ts devices can be sent
- * through the bytestream whereas the other could be omitted. For
- * this, we have to save the old value that will be forwarded without
- * modifications to the compositor. */
- accum->x = device->x;
- accum->y = device->y;
-}
-
-static void
evdev_flush_motion(struct wl_input_device *device, uint32_t time,
- struct evdev_motion_accumulator *accum)
+ struct evdev_motion_accumulator *accum, int slot_mt)
{
if (!accum->type)
return;
- if (accum->type == EVDEV_RELATIVE_MOTION)
+ if (accum->type & EVDEV_RELATIVE_MOTION) {
notify_motion(device, time,
device->x + accum->dx, device->y + accum->dy);
- if (accum->type == EVDEV_ABSOLUTE_MOTION)
+ accum->type &= ~EVDEV_RELATIVE_MOTION;
+ accum->dx = 0;
+ accum->dy = 0;
+ }
+ if (accum->type & EVDEV_ABSOLUTE_MT_DOWN) {
+ notify_touch(device, time, slot_mt, accum->mt_x, accum->mt_y,
+ WL_INPUT_DEVICE_TOUCH_DOWN);
+ accum->type &= ~EVDEV_ABSOLUTE_MT_DOWN;
+ accum->type &= ~EVDEV_ABSOLUTE_MT_MOTION;
+ }
+ if (accum->type & EVDEV_ABSOLUTE_MT_MOTION) {
+ notify_touch(device, time, slot_mt, accum->mt_x, accum->mt_y,
+ WL_INPUT_DEVICE_TOUCH_MOTION);
+ accum->type &= ~EVDEV_ABSOLUTE_MT_DOWN;
+ accum->type &= ~EVDEV_ABSOLUTE_MT_MOTION;
+ }
+ if (accum->type & EVDEV_ABSOLUTE_MT_UP) {
+ notify_touch(device, time, slot_mt, 0, 0,
+ WL_INPUT_DEVICE_TOUCH_UP);
+ accum->type &= ~EVDEV_ABSOLUTE_MT_UP;
+ }
+ if (accum->type & EVDEV_ABSOLUTE_MOTION) {
notify_motion(device, time, accum->x, accum->y);
-
- evdev_reset_accum(device, accum);
+ accum->type &= ~EVDEV_ABSOLUTE_MOTION;
+ }
}
static int
return 1;
}
- evdev_reset_accum(&device->master->base.input_device, &accumulator);
+ accumulator.type = 0;
+ accumulator.mt_x = accumulator.x = device->master->base.input_device.x;
+ accumulator.mt_y = accumulator.y = device->master->base.input_device.y;
e = ev;
end = (void *) ev + len;
* events and send as a bunch */
if (!is_motion_event(e))
evdev_flush_motion(&device->master->base.input_device,
- time, &accumulator);
+ time, &accumulator,
+ device->abs.slot_mt);
switch (e->type) {
case EV_REL:
evdev_process_relative_motion(e, &accumulator);
break;
case EV_ABS:
- if (device->is_touchpad)
- evdev_process_absolute_motion_touchpad(device,
- e, &accumulator);
- else
- evdev_process_absolute_motion(device, e,
- &accumulator);
+ evdev_process_absolute(device, e, time, &accumulator);
break;
case EV_KEY:
evdev_process_key(device, e, time);
}
}
- evdev_flush_motion(&device->master->base.input_device, time, &accumulator);
+ evdev_flush_motion(&device->master->base.input_device, time,
+ &accumulator, device->abs.slot_mt);
return 1;
}
device->abs.min_y = absinfo.minimum;
device->abs.max_y = absinfo.maximum;
}
+ if (TEST_BIT(abs_bits, ABS_MT_SLOT)) {
+ device->is_mt = 1;
+ device->abs.slot_mt = 0;
+ }
}
if (TEST_BIT(ev_bits, EV_KEY)) {
has_key = 1;
device->master = master;
device->is_touchpad = 0;
+ device->is_mt = 0;
device->devnode = strdup(path);
+ device->abs.slot_mt = -1;
device->fd = open(path, O_RDONLY);
if (device->fd < 0)