mirror of
https://gitlab.freedesktop.org/libinput/libei.git
synced 2026-01-21 23:30:25 +01:00
When a device has multiple regions, ei_device_pointer_motion_absolute() would walk all of the regions and bail out if the coordinates are outside any region. As a matter of facts, it means that an absolute event must fall within all the device regions to succeed. That doesn't seem right, it should be the opposite, the coordinates must be within at least one region. Fix the logic by reusing the existing ei_device_in_region() function. Signed-off-by: Olivier Fourdan <ofourdan@redhat.com>
1005 lines
25 KiB
C
1005 lines
25 KiB
C
/* SPDX-License-Identifier: MIT */
|
|
/*
|
|
* Copyright © 2020 Red Hat, Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
* Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <errno.h>
|
|
|
|
#include "util-bits.h"
|
|
#include "util-macros.h"
|
|
#include "util-mem.h"
|
|
#include "util-io.h"
|
|
#include "util-strings.h"
|
|
|
|
#include "libei-private.h"
|
|
|
|
static const char *
|
|
ei_device_state_to_string(enum ei_device_state state)
|
|
{
|
|
switch (state) {
|
|
CASE_RETURN_STRING(EI_DEVICE_STATE_NEW);
|
|
CASE_RETURN_STRING(EI_DEVICE_STATE_PAUSED);
|
|
CASE_RETURN_STRING(EI_DEVICE_STATE_RESUMED);
|
|
CASE_RETURN_STRING(EI_DEVICE_STATE_EMULATING);
|
|
CASE_RETURN_STRING(EI_DEVICE_STATE_REMOVED_FROM_CLIENT);
|
|
CASE_RETURN_STRING(EI_DEVICE_STATE_REMOVED_FROM_SERVER);
|
|
CASE_RETURN_STRING(EI_DEVICE_STATE_DEAD);
|
|
}
|
|
assert(!"Unhandled device state");
|
|
}
|
|
|
|
static void
|
|
ei_device_set_state(struct ei_device *device,
|
|
enum ei_device_state state)
|
|
{
|
|
enum ei_device_state old_state = device->state;
|
|
device->state = state;
|
|
log_debug(ei_device_get_context(device), "device %#x: %s → %s",
|
|
device->id, ei_device_state_to_string(old_state),
|
|
ei_device_state_to_string(state));
|
|
}
|
|
|
|
static void
|
|
ei_device_destroy(struct ei_device *device)
|
|
{
|
|
struct ei_seat *seat = ei_device_get_seat(device);
|
|
struct ei_region *region;
|
|
struct ei_event *event;
|
|
|
|
assert(device->state == EI_DEVICE_STATE_DEAD);
|
|
|
|
list_for_each_safe(region, &device->regions, link)
|
|
ei_region_unref(region);
|
|
|
|
list_for_each_safe(event, &device->pending_event_queue, link) {
|
|
list_remove(&event->link);
|
|
ei_event_unref(event);
|
|
}
|
|
|
|
list_remove(&device->link);
|
|
ei_keymap_unref(device->keymap);
|
|
ei_seat_unref(seat);
|
|
free(device->name);
|
|
}
|
|
|
|
_public_
|
|
OBJECT_IMPLEMENT_REF(ei_device);
|
|
_public_
|
|
OBJECT_IMPLEMENT_UNREF_CLEANUP(ei_device);
|
|
|
|
static
|
|
OBJECT_IMPLEMENT_CREATE(ei_device);
|
|
static
|
|
OBJECT_IMPLEMENT_PARENT(ei_device, ei_seat);
|
|
_public_
|
|
OBJECT_IMPLEMENT_GETTER(ei_device, type, enum ei_device_type);
|
|
_public_
|
|
OBJECT_IMPLEMENT_GETTER(ei_device, name, const char *);
|
|
_public_
|
|
OBJECT_IMPLEMENT_GETTER(ei_device, user_data, void *);
|
|
_public_
|
|
OBJECT_IMPLEMENT_SETTER(ei_device, user_data, void *);
|
|
|
|
_public_ struct ei_seat *
|
|
ei_device_get_seat(struct ei_device *device)
|
|
{
|
|
return ei_device_parent(device);
|
|
}
|
|
|
|
_public_ struct ei*
|
|
ei_device_get_context(struct ei_device *device)
|
|
{
|
|
assert(device);
|
|
return ei_seat_get_context(ei_device_get_seat(device));
|
|
}
|
|
|
|
struct ei_device *
|
|
ei_device_new(struct ei_seat *seat, uint32_t deviceid)
|
|
{
|
|
struct ei_device *device = ei_device_create(&seat->object);
|
|
|
|
device->capabilities = 0;
|
|
device->id = deviceid;
|
|
device->state = EI_DEVICE_STATE_NEW;
|
|
device->name = xaprintf("unnamed device %#x", device->id);
|
|
list_init(&device->regions);
|
|
list_init(&device->pending_event_queue);
|
|
|
|
/* We have a ref to the seat to make sure our seat doesn't get
|
|
* destroyed while a ref to the device is still alive.
|
|
* And the seat has a ref to the device in the seat->devices list.
|
|
* dropped when the device is removed.
|
|
*/
|
|
ei_seat_ref(seat);
|
|
|
|
/* this list "owns" the ref for this device */
|
|
ei_device_ref(device);
|
|
list_append(&seat->devices, &device->link);
|
|
|
|
return device;
|
|
}
|
|
|
|
void
|
|
ei_device_done(struct ei_device *device)
|
|
{
|
|
ei_device_paused(device);
|
|
}
|
|
|
|
void
|
|
ei_device_add_region(struct ei_device *device, struct ei_region *region)
|
|
{
|
|
if (device->state != EI_DEVICE_STATE_NEW)
|
|
return;
|
|
|
|
ei_region_ref(region);
|
|
list_append(&device->regions, ®ion->link);
|
|
}
|
|
|
|
_public_
|
|
OBJECT_IMPLEMENT_REF(ei_keymap);
|
|
_public_
|
|
OBJECT_IMPLEMENT_UNREF_CLEANUP(ei_keymap);
|
|
_public_
|
|
OBJECT_IMPLEMENT_GETTER(ei_keymap, type, enum ei_keymap_type);
|
|
_public_
|
|
OBJECT_IMPLEMENT_GETTER(ei_keymap, fd, int);
|
|
_public_
|
|
OBJECT_IMPLEMENT_GETTER(ei_keymap, size, size_t);
|
|
_public_
|
|
OBJECT_IMPLEMENT_GETTER(ei_keymap, device, struct ei_device *);
|
|
_public_
|
|
OBJECT_IMPLEMENT_GETTER(ei_keymap, user_data, void *);
|
|
_public_
|
|
OBJECT_IMPLEMENT_SETTER(ei_keymap, user_data, void *);
|
|
|
|
static void
|
|
ei_keymap_destroy(struct ei_keymap *keymap)
|
|
{
|
|
xclose(keymap->fd);
|
|
}
|
|
|
|
static
|
|
OBJECT_IMPLEMENT_CREATE(ei_keymap);
|
|
|
|
_public_ struct ei_keymap *
|
|
ei_device_keyboard_get_keymap(struct ei_device *device)
|
|
{
|
|
return device->keymap;
|
|
}
|
|
|
|
static struct ei_keymap *
|
|
ei_keymap_new(enum ei_keymap_type type, int fd, size_t size)
|
|
{
|
|
_unref_(ei_keymap) *keymap = ei_keymap_create(NULL);
|
|
|
|
switch (type) {
|
|
case EI_KEYMAP_TYPE_XKB:
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
if (fd < 0 || size == 0)
|
|
return NULL;
|
|
|
|
int newfd = dup(fd);
|
|
if (newfd < 0)
|
|
return NULL;
|
|
|
|
keymap->fd = newfd;
|
|
keymap->type = type;
|
|
keymap->size = size;
|
|
|
|
return ei_keymap_ref(keymap);
|
|
}
|
|
|
|
void
|
|
ei_device_set_keymap(struct ei_device *device,
|
|
enum ei_keymap_type type,
|
|
int keymap_fd, size_t size)
|
|
{
|
|
device->keymap = ei_keymap_unref(device->keymap);
|
|
|
|
if (!type)
|
|
return;
|
|
|
|
_unref_(ei_keymap) *keymap = ei_keymap_new(type, keymap_fd, size);
|
|
if (!keymap) {
|
|
log_bug(ei_device_get_context(device),
|
|
"Failed to apply server-requested keymap");
|
|
return; /* FIXME: ei_device_remove() here */
|
|
}
|
|
|
|
keymap->device = device;
|
|
device->keymap = ei_keymap_ref(keymap);
|
|
}
|
|
|
|
|
|
_public_ void
|
|
ei_device_close(struct ei_device *device)
|
|
{
|
|
switch (device->state) {
|
|
case EI_DEVICE_STATE_NEW:
|
|
case EI_DEVICE_STATE_DEAD:
|
|
case EI_DEVICE_STATE_REMOVED_FROM_CLIENT:
|
|
case EI_DEVICE_STATE_REMOVED_FROM_SERVER:
|
|
break;
|
|
case EI_DEVICE_STATE_EMULATING:
|
|
if (ei_is_sender(ei_device_get_context(device)))
|
|
ei_send_stop_emulating(device);
|
|
_fallthrough_;
|
|
case EI_DEVICE_STATE_PAUSED:
|
|
case EI_DEVICE_STATE_RESUMED:
|
|
ei_device_set_state(device, EI_DEVICE_STATE_REMOVED_FROM_CLIENT);
|
|
ei_send_close_device(device);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
ei_device_removed_by_server(struct ei_device *device)
|
|
{
|
|
struct ei_seat *seat = ei_device_get_seat(device);
|
|
|
|
switch (device->state) {
|
|
case EI_DEVICE_STATE_NEW:
|
|
case EI_DEVICE_STATE_DEAD:
|
|
case EI_DEVICE_STATE_REMOVED_FROM_SERVER:
|
|
break;
|
|
case EI_DEVICE_STATE_REMOVED_FROM_CLIENT:
|
|
case EI_DEVICE_STATE_PAUSED:
|
|
case EI_DEVICE_STATE_RESUMED:
|
|
case EI_DEVICE_STATE_EMULATING:
|
|
ei_queue_device_removed_event(device);
|
|
ei_device_set_state(device, EI_DEVICE_STATE_DEAD);
|
|
/* Device is dead now. Move it from the devices list to
|
|
* removed and drop the seat's ref to the device.
|
|
* This should be the last ref to the device that libei has
|
|
* (not counting any queued events). Device is kept alive by
|
|
* any client refs but once those drop, the device can be
|
|
* destroyed.
|
|
*/
|
|
list_remove(&device->link);
|
|
list_append(&seat->devices_removed, &device->link);
|
|
ei_device_unref(device);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
ei_device_resumed(struct ei_device *device)
|
|
{
|
|
ei_device_set_state(device, EI_DEVICE_STATE_RESUMED);
|
|
}
|
|
|
|
void
|
|
ei_device_paused(struct ei_device *device)
|
|
{
|
|
ei_device_set_state(device, EI_DEVICE_STATE_PAUSED);
|
|
}
|
|
|
|
void
|
|
ei_device_added(struct ei_device *device)
|
|
{
|
|
}
|
|
|
|
void
|
|
ei_device_set_type(struct ei_device *device, enum ei_device_type type)
|
|
{
|
|
switch(type) {
|
|
case EI_DEVICE_TYPE_PHYSICAL:
|
|
case EI_DEVICE_TYPE_VIRTUAL:
|
|
device->type = type;
|
|
break;
|
|
default:
|
|
log_bug(ei_device_get_context(device), "Invalid device type %u", type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
ei_device_set_size(struct ei_device *device, uint32_t width, uint32_t height)
|
|
{
|
|
if (device->type == EI_DEVICE_TYPE_PHYSICAL) {
|
|
device->width = width;
|
|
device->height = height;
|
|
}
|
|
}
|
|
|
|
void
|
|
ei_device_set_name(struct ei_device *device, const char *name)
|
|
{
|
|
free(device->name);
|
|
device->name = xstrdup(name);
|
|
}
|
|
|
|
void
|
|
ei_device_set_capabilities(struct ei_device *device,
|
|
uint32_t capabilities)
|
|
{
|
|
device->capabilities = capabilities;
|
|
}
|
|
|
|
_public_ bool
|
|
ei_device_has_capability(struct ei_device *device,
|
|
enum ei_device_capability cap)
|
|
{
|
|
switch (cap) {
|
|
case EI_DEVICE_CAP_POINTER:
|
|
case EI_DEVICE_CAP_POINTER_ABSOLUTE:
|
|
case EI_DEVICE_CAP_KEYBOARD:
|
|
case EI_DEVICE_CAP_TOUCH:
|
|
return flag_is_set(device->capabilities, cap);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
ei_device_frame_now(struct ei_device *device)
|
|
{
|
|
uint64_t now = ei_now(ei_device_get_context(device));
|
|
|
|
ei_device_frame(device, now);
|
|
}
|
|
|
|
static void
|
|
_flush_frame(struct ei_device *device, const char *func)
|
|
{
|
|
if (device->send_frame_event) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: missing call to ei_device_frame()", func);
|
|
ei_device_frame_now(device);
|
|
}
|
|
}
|
|
#define ei_device_flush_frame(d_) _flush_frame(d_, __func__)
|
|
|
|
_public_ void
|
|
ei_device_start_emulating(struct ei_device *device)
|
|
{
|
|
if (device->state != EI_DEVICE_STATE_RESUMED)
|
|
return;
|
|
|
|
assert(!device->send_frame_event);
|
|
|
|
device->state = EI_DEVICE_STATE_EMULATING;
|
|
ei_send_start_emulating(device);
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_stop_emulating(struct ei_device *device)
|
|
{
|
|
if (device->state != EI_DEVICE_STATE_EMULATING)
|
|
return;
|
|
|
|
ei_device_flush_frame(device);
|
|
device->state = EI_DEVICE_STATE_RESUMED;
|
|
ei_send_stop_emulating(device);
|
|
}
|
|
|
|
_public_ struct ei_region *
|
|
ei_device_get_region(struct ei_device *device, size_t index)
|
|
{
|
|
return list_nth_entry(struct ei_region, &device->regions, link, index);
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_pointer_motion(struct ei_device *device,
|
|
double x, double y)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a pointer", __func__);
|
|
return;
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not not emulating", __func__);
|
|
return;
|
|
}
|
|
|
|
ei_send_pointer_rel(device, x, y);
|
|
}
|
|
|
|
static inline bool
|
|
ei_device_in_region(struct ei_device *device, double x, double y)
|
|
{
|
|
struct ei_region *r;
|
|
|
|
list_for_each(r, &device->regions, link) {
|
|
if (ei_region_contains(r, x, y))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_pointer_motion_absolute(struct ei_device *device,
|
|
double x, double y)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not an absolute pointer", __func__);
|
|
return;
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not not emulating", __func__);
|
|
return;
|
|
}
|
|
|
|
if (!ei_device_in_region(device, x, y))
|
|
return;
|
|
|
|
ei_send_pointer_abs(device, x, y);
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_pointer_button(struct ei_device *device,
|
|
uint32_t button, bool is_press)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a pointer", __func__);
|
|
return;
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not not emulating", __func__);
|
|
return;
|
|
}
|
|
|
|
/* Ignore anything < BTN_MOUSE. Avoids the common error of sending
|
|
* numerical buttons instead of BTN_LEFT and friends. */
|
|
if (button < 0x110) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: button code must be one of BTN_*", __func__);
|
|
return;
|
|
}
|
|
|
|
ei_send_pointer_button(device, button, is_press);
|
|
}
|
|
|
|
static inline void
|
|
ei_device_resume_scrolling(struct ei_device *device, double x, double y)
|
|
{
|
|
if (x) {
|
|
device->scroll.x_is_stopped = false;
|
|
device->scroll.x_is_cancelled = false;
|
|
}
|
|
if (y) {
|
|
device->scroll.y_is_stopped = false;
|
|
device->scroll.y_is_cancelled = false;
|
|
}
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_pointer_scroll(struct ei_device *device,
|
|
double x, double y)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) &&
|
|
!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a (absolute) pointer", __func__);
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not not emulating", __func__);
|
|
return;
|
|
}
|
|
|
|
ei_device_resume_scrolling(device, x, y);
|
|
|
|
ei_send_pointer_scroll(device, x, y);
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_pointer_scroll_stop(struct ei_device *device, bool x, bool y)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) &&
|
|
!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a (absolute) pointer", __func__);
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not not emulating", __func__);
|
|
return;
|
|
}
|
|
|
|
/* Filter out duplicate scroll stop requests */
|
|
if (x && !device->scroll.x_is_stopped)
|
|
device->scroll.x_is_stopped = true;
|
|
else
|
|
x = false;
|
|
|
|
if (y && !device->scroll.y_is_stopped)
|
|
device->scroll.y_is_stopped = true;
|
|
else
|
|
y = false;
|
|
|
|
if (x || y)
|
|
ei_send_pointer_scroll_stop(device, x, y);
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_pointer_scroll_cancel(struct ei_device *device, bool x, bool y)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) &&
|
|
!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a (absolute) pointer", __func__);
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not not emulating", __func__);
|
|
return;
|
|
}
|
|
|
|
/* Filter out duplicate scroll cancelled requests */
|
|
if (x && !device->scroll.x_is_cancelled) {
|
|
device->scroll.x_is_stopped = true;
|
|
device->scroll.x_is_cancelled = true;
|
|
} else {
|
|
x = false;
|
|
}
|
|
|
|
if (y && !device->scroll.y_is_cancelled) {
|
|
device->scroll.y_is_stopped = true;
|
|
device->scroll.y_is_cancelled = true;
|
|
} else {
|
|
y = false;
|
|
}
|
|
|
|
if (x || y)
|
|
ei_send_pointer_scroll_cancel(device, x, y);
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_pointer_scroll_discrete(struct ei_device *device,
|
|
int32_t x, int32_t y)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) &&
|
|
!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a (absolute) pointer", __func__);
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not not emulating", __func__);
|
|
return;
|
|
}
|
|
|
|
ei_device_resume_scrolling(device, x, y);
|
|
|
|
ei_send_pointer_scroll_discrete(device, x, y);
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_keyboard_key(struct ei_device *device,
|
|
uint32_t key, bool is_press)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a keyboard", __func__);
|
|
return;
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not not emulating", __func__);
|
|
return;
|
|
}
|
|
|
|
ei_send_keyboard_key(device, key, is_press);
|
|
}
|
|
|
|
|
|
_public_
|
|
OBJECT_IMPLEMENT_REF(ei_touch);
|
|
_public_
|
|
OBJECT_IMPLEMENT_UNREF_CLEANUP(ei_touch);
|
|
_public_
|
|
OBJECT_IMPLEMENT_GETTER(ei_touch, device, struct ei_device*);
|
|
_public_
|
|
OBJECT_IMPLEMENT_GETTER(ei_touch, user_data, void *);
|
|
_public_
|
|
OBJECT_IMPLEMENT_SETTER(ei_touch, user_data, void *);
|
|
|
|
static void
|
|
ei_touch_destroy(struct ei_touch *touch)
|
|
{
|
|
if (touch->state == TOUCH_IS_DOWN)
|
|
ei_touch_up(touch);
|
|
/* Enforce a frame, otherwise we're just pending. If the client
|
|
* doesn't want this, it needs to ei_touch_up() */
|
|
ei_device_frame_now(touch->device);
|
|
ei_device_unref(touch->device);
|
|
}
|
|
|
|
static
|
|
OBJECT_IMPLEMENT_CREATE(ei_touch);
|
|
|
|
_public_ struct ei_touch *
|
|
ei_device_touch_new(struct ei_device *device)
|
|
{
|
|
static uint32_t tracking_id = 0;
|
|
|
|
/* Not using the device as parent object because we need a ref
|
|
* to it */
|
|
struct ei_touch *touch = ei_touch_create(NULL);
|
|
|
|
touch->device = ei_device_ref(device);
|
|
touch->state = TOUCH_IS_NEW;
|
|
touch->tracking_id = ++tracking_id;
|
|
|
|
return touch;
|
|
}
|
|
|
|
_public_ void
|
|
ei_touch_down(struct ei_touch *touch, double x, double y)
|
|
{
|
|
struct ei_device *device = ei_touch_get_device(touch);
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not not emulating", __func__);
|
|
return;
|
|
}
|
|
|
|
if (touch->state != TOUCH_IS_NEW) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: touch %u already down or up", __func__, touch->tracking_id);
|
|
return;
|
|
}
|
|
|
|
struct ei_region *r;
|
|
list_for_each(r, &device->regions, link) {
|
|
if (!ei_region_contains(r, x, y)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: touch %u has invalid x/y coordinates", __func__, touch->tracking_id);
|
|
touch->state = TOUCH_IS_UP;
|
|
return;
|
|
}
|
|
}
|
|
|
|
touch->state = TOUCH_IS_DOWN;
|
|
|
|
ei_send_touch_down(device, touch->tracking_id, x, y);
|
|
}
|
|
|
|
_public_ void
|
|
ei_touch_motion(struct ei_touch *touch, double x, double y)
|
|
{
|
|
struct ei_device *device = ei_touch_get_device(touch);
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not not emulating", __func__);
|
|
return;
|
|
}
|
|
|
|
if (touch->state != TOUCH_IS_DOWN) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: touch %u is not currently down", __func__, touch->tracking_id);
|
|
return;
|
|
}
|
|
|
|
struct ei_region *r;
|
|
list_for_each(r, &device->regions, link) {
|
|
if (!ei_region_contains(r, x, y)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: touch %u has invalid x/y coordinates", __func__, touch->tracking_id);
|
|
ei_touch_up(touch);
|
|
return;
|
|
}
|
|
}
|
|
|
|
ei_send_touch_motion(touch->device, touch->tracking_id, x, y);
|
|
}
|
|
|
|
_public_ void
|
|
ei_touch_up(struct ei_touch *touch)
|
|
{
|
|
struct ei_device *device = ei_touch_get_device(touch);
|
|
if (device->state != EI_DEVICE_STATE_EMULATING) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not not emulating", __func__);
|
|
return;
|
|
}
|
|
|
|
if (touch->state != TOUCH_IS_DOWN) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: touch %u is not currently down", __func__, touch->tracking_id);
|
|
return;
|
|
}
|
|
|
|
touch->state = TOUCH_IS_UP;
|
|
ei_send_touch_up(touch->device, touch->tracking_id);
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_frame(struct ei_device *device, uint64_t time)
|
|
{
|
|
if (device->state != EI_DEVICE_STATE_EMULATING)
|
|
return;
|
|
|
|
ei_send_frame(device, time);
|
|
}
|
|
|
|
void
|
|
ei_device_event_start_emulating(struct ei_device *device)
|
|
{
|
|
switch (device->state) {
|
|
case EI_DEVICE_STATE_DEAD:
|
|
case EI_DEVICE_STATE_NEW:
|
|
case EI_DEVICE_STATE_PAUSED:
|
|
case EI_DEVICE_STATE_EMULATING:
|
|
break;
|
|
case EI_DEVICE_STATE_RESUMED:
|
|
ei_queue_device_start_emulating_event(device);
|
|
device->state = EI_DEVICE_STATE_EMULATING;
|
|
break;
|
|
case EI_DEVICE_STATE_REMOVED_FROM_CLIENT:
|
|
case EI_DEVICE_STATE_REMOVED_FROM_SERVER:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
ei_device_event_stop_emulating(struct ei_device *device)
|
|
{
|
|
switch (device->state) {
|
|
case EI_DEVICE_STATE_DEAD:
|
|
case EI_DEVICE_STATE_NEW:
|
|
case EI_DEVICE_STATE_PAUSED:
|
|
case EI_DEVICE_STATE_RESUMED:
|
|
break;
|
|
case EI_DEVICE_STATE_EMULATING:
|
|
ei_queue_device_stop_emulating_event(device);
|
|
device->state = EI_DEVICE_STATE_RESUMED;
|
|
break;
|
|
case EI_DEVICE_STATE_REMOVED_FROM_CLIENT:
|
|
case EI_DEVICE_STATE_REMOVED_FROM_SERVER:
|
|
break;
|
|
}
|
|
}
|
|
|
|
int
|
|
ei_device_event_frame(struct ei_device *device, uint64_t time)
|
|
{
|
|
if (device->state != EI_DEVICE_STATE_EMULATING)
|
|
return -EINVAL;
|
|
|
|
ei_queue_frame_event(device, time);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ei_device_event_pointer_rel(struct ei_device *device, double x, double y)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a pointer", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING)
|
|
return -EINVAL;
|
|
|
|
ei_queue_pointer_rel_event(device, x, y);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ei_device_event_pointer_abs(struct ei_device *device,
|
|
double x, double y)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not an absolute pointer", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING)
|
|
return -EINVAL;
|
|
|
|
if (!ei_device_in_region(device, x, y))
|
|
return -EINVAL;
|
|
|
|
ei_queue_pointer_abs_event(device, x, y);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ei_device_event_pointer_button(struct ei_device *device,
|
|
uint32_t button, bool is_press)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a pointer", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING)
|
|
return -EINVAL;
|
|
|
|
ei_queue_pointer_button_event(device, button, is_press);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ei_device_event_pointer_scroll(struct ei_device *device,
|
|
double x, double y)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) &&
|
|
!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a (absolute) pointer", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING)
|
|
return -EINVAL;
|
|
|
|
ei_queue_pointer_scroll_event(device, x, y);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ei_device_event_pointer_scroll_discrete(struct ei_device *device,
|
|
int32_t x, int32_t y)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) &&
|
|
!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a (absolute) pointer", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING)
|
|
return -EINVAL;
|
|
|
|
ei_queue_pointer_scroll_discrete_event(device, x, y);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ei_device_event_pointer_scroll_stop(struct ei_device *device, bool x, bool y)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) &&
|
|
!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a (absolute) pointer", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING)
|
|
return -EINVAL;
|
|
|
|
ei_queue_pointer_scroll_stop_event(device, x, y);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ei_device_event_pointer_scroll_cancel(struct ei_device *device, bool x, bool y)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) &&
|
|
!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a (absolute) pointer", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING)
|
|
return -EINVAL;
|
|
|
|
ei_queue_pointer_scroll_cancel_event(device, x, y);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ei_device_event_keyboard_key(struct ei_device *device,
|
|
uint32_t key, bool is_press)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a keyboard", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING)
|
|
return -EINVAL;
|
|
|
|
ei_queue_keyboard_key_event(device, key, is_press);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ei_device_event_touch_down(struct ei_device *device, uint32_t touchid, double x, double y)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a touch device", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING)
|
|
return -EINVAL;
|
|
|
|
ei_queue_touch_down_event(device, touchid, x, y);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ei_device_event_touch_motion(struct ei_device *device, uint32_t touchid, double x, double y)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a touch device", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING)
|
|
return -EINVAL;
|
|
|
|
ei_queue_touch_motion_event(device, touchid, x, y);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ei_device_event_touch_up(struct ei_device *device, uint32_t touchid)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH)) {
|
|
log_bug_client(ei_device_get_context(device),
|
|
"%s: device is not a touch device", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (device->state != EI_DEVICE_STATE_EMULATING)
|
|
return -EINVAL;
|
|
|
|
ei_queue_touch_up_event(device, touchid);
|
|
|
|
return 0;
|
|
}
|
|
|