+static inline double
+scale_axis(const struct input_absinfo *absinfo, double val, double to_range)
+{
+ return (val - absinfo->minimum) * to_range /
+ (absinfo->maximum - absinfo->minimum + 1);
+}
+
+double
+evdev_device_transform_x(struct evdev_device *device,
+ double x,
+ uint32_t width)
+{
+ return scale_axis(device->abs.absinfo_x, x, width);
+}
+
+double
+evdev_device_transform_y(struct evdev_device *device,
+ double y,
+ uint32_t height)
+{
+ return scale_axis(device->abs.absinfo_y, y, height);
+}
+
+static void
+evdev_flush_pending_event(struct evdev_device *device, uint64_t time)
+{
+ struct motion_params motion;
+ int32_t cx, cy;
+ double x, y;
+ int slot;
+ int seat_slot;
+ struct libinput_device *base = &device->base;
+ struct libinput_seat *seat = base->seat;
+
+ slot = device->mt.slot;
+
+ switch (device->pending_event) {
+ case EVDEV_NONE:
+ return;
+ case EVDEV_RELATIVE_MOTION:
+ motion.dx = device->rel.dx;
+ motion.dy = device->rel.dy;
+ device->rel.dx = 0;
+ device->rel.dy = 0;
+
+ /* Apply pointer acceleration. */
+ filter_dispatch(device->pointer.filter, &motion, device, time);
+
+ if (motion.dx == 0.0 && motion.dy == 0.0)
+ break;
+
+ pointer_notify_motion(base, time, motion.dx, motion.dy);
+ break;
+ case EVDEV_ABSOLUTE_MT_DOWN:
+ if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
+ break;
+
+ if (device->mt.slots[slot].seat_slot != -1) {
+ log_bug_kernel("%s: Driver sent multiple touch down for the "
+ "same slot", device->devnode);
+ break;
+ }
+
+ seat_slot = ffs(~seat->slot_map) - 1;
+ device->mt.slots[slot].seat_slot = seat_slot;
+
+ if (seat_slot == -1)
+ break;
+
+ seat->slot_map |= 1 << seat_slot;
+ x = device->mt.slots[slot].x;
+ y = device->mt.slots[slot].y;
+
+ touch_notify_touch_down(base, time, slot, seat_slot, x, y);
+ break;
+ case EVDEV_ABSOLUTE_MT_MOTION:
+ if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
+ break;
+
+ seat_slot = device->mt.slots[slot].seat_slot;
+ x = device->mt.slots[slot].x;
+ y = device->mt.slots[slot].y;
+
+ if (seat_slot == -1)
+ break;
+
+ touch_notify_touch_motion(base, time, slot, seat_slot, x, y);
+ break;
+ case EVDEV_ABSOLUTE_MT_UP:
+ if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
+ break;
+
+ seat_slot = device->mt.slots[slot].seat_slot;
+ device->mt.slots[slot].seat_slot = -1;
+
+ if (seat_slot == -1)
+ break;
+
+ seat->slot_map &= ~(1 << seat_slot);
+
+ touch_notify_touch_up(base, time, slot, seat_slot);
+ break;
+ case EVDEV_ABSOLUTE_TOUCH_DOWN:
+ if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
+ break;
+
+ if (device->abs.seat_slot != -1) {
+ log_bug_kernel("%s: Driver sent multiple touch down for the "
+ "same slot", device->devnode);
+ break;
+ }
+
+ seat_slot = ffs(~seat->slot_map) - 1;
+ device->abs.seat_slot = seat_slot;
+
+ if (seat_slot == -1)
+ break;
+
+ seat->slot_map |= 1 << seat_slot;
+
+ transform_absolute(device, &cx, &cy);
+
+ touch_notify_touch_down(base, time, -1, seat_slot, cx, cy);
+ break;
+ case EVDEV_ABSOLUTE_MOTION:
+ transform_absolute(device, &cx, &cy);
+ x = cx;
+ y = cy;
+
+ if (device->seat_caps & EVDEV_DEVICE_TOUCH) {
+ seat_slot = device->abs.seat_slot;
+
+ if (seat_slot == -1)
+ break;
+
+ touch_notify_touch_motion(base, time, -1, seat_slot, x, y);
+ } else if (device->seat_caps & EVDEV_DEVICE_POINTER) {
+ pointer_notify_motion_absolute(base, time, x, y);
+ }
+ break;
+ case EVDEV_ABSOLUTE_TOUCH_UP:
+ if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
+ break;
+
+ seat_slot = device->abs.seat_slot;
+ device->abs.seat_slot = -1;
+
+ if (seat_slot == -1)
+ break;
+
+ seat->slot_map &= ~(1 << seat_slot);
+
+ touch_notify_touch_up(base, time, -1, seat_slot);
+ break;
+ default:
+ assert(0 && "Unknown pending event type");
+ break;
+ }
+
+ device->pending_event = EVDEV_NONE;
+}
+
+static void
+evdev_process_touch_button(struct evdev_device *device,
+ uint64_t time, int value)
+{
+ if (device->pending_event != EVDEV_NONE &&
+ device->pending_event != EVDEV_ABSOLUTE_MOTION)
+ evdev_flush_pending_event(device, time);
+
+ device->pending_event = (value ?
+ EVDEV_ABSOLUTE_TOUCH_DOWN :
+ EVDEV_ABSOLUTE_TOUCH_UP);
+}
+