libei/src/libeis-device.c
Peter Hutterer 0541668443 eis: don't send stop_emulating for a sender device
The check is currently missing from a number of libeis APIs but in most
cases we can blame the EIS implementation and say "don't do this".
Device removal is an exception since that is still required.
2023-02-13 16:18:08 +10:00

1000 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-macros.h"
#include "util-bits.h"
#include "util-io.h"
#include "libeis-private.h"
#include "libeis-proto.h"
_public_
OBJECT_IMPLEMENT_REF(eis_keymap);
_public_
OBJECT_IMPLEMENT_UNREF_CLEANUP(eis_keymap);
_public_
OBJECT_IMPLEMENT_GETTER(eis_keymap, type, enum eis_keymap_type);
_public_
OBJECT_IMPLEMENT_GETTER(eis_keymap, fd, int);
_public_
OBJECT_IMPLEMENT_GETTER(eis_keymap, size, size_t);
_public_
OBJECT_IMPLEMENT_GETTER(eis_keymap, device, struct eis_device *);
_public_
OBJECT_IMPLEMENT_GETTER(eis_keymap, user_data, void *);
_public_
OBJECT_IMPLEMENT_SETTER(eis_keymap, user_data, void *);
static void
eis_keymap_destroy(struct eis_keymap *keymap)
{
if (!keymap->assigned)
eis_device_unref(keymap->device);
xclose(keymap->fd);
}
static
OBJECT_IMPLEMENT_CREATE(eis_keymap);
_public_ struct eis_keymap *
eis_device_new_keymap(struct eis_device *device,
enum eis_keymap_type type, int fd, size_t size)
{
switch (type) {
case EIS_KEYMAP_TYPE_XKB:
break;
default:
return NULL;
}
if (fd < 0 || size == 0)
return NULL;
int newfd = xdup(fd);
if (newfd < 0)
return NULL;
struct eis_keymap *keymap = eis_keymap_create(NULL);
keymap->device = eis_device_ref(device);
keymap->fd = newfd;
keymap->type = type;
keymap->size = size;
return keymap;
}
struct eis *
eis_device_get_context(struct eis_device *device)
{
return eis_client_get_context(eis_device_get_client(device));
}
_public_ void
eis_keymap_add(struct eis_keymap *keymap)
{
struct eis_device *device = eis_keymap_get_device(keymap);
if (device->state != EIS_DEVICE_STATE_NEW) {
log_bug_client(eis_device_get_context(device),
"%s: device already (dis)connected", __func__);
return;
}
if (device->keymap) {
log_bug_client(eis_device_get_context(device),
"%s: only one keymap can only be assigned and only once", __func__);
return;
}
/* New keymap holds ref to the device, for assigned keymap the device
* holds the ref to the keymap instead */
device->keymap = eis_keymap_ref(keymap);
keymap->assigned = true;
eis_device_unref(keymap->device);
}
_public_ struct eis_keymap *
eis_device_keyboard_get_keymap(struct eis_device *device)
{
return device->keymap;
}
static void
eis_device_destroy(struct eis_device *device)
{
struct eis_region *r;
struct eis_event *event;
list_for_each_safe(r, &device->regions, link)
eis_region_unref(r);
/* regions_new does not own a ref */
eis_keymap_unref(device->keymap);
list_for_each_safe(event, &device->pending_event_queue, link) {
list_remove(&event->link);
eis_event_unref(event);
}
free(device->name);
}
_public_
OBJECT_IMPLEMENT_REF(eis_device);
_public_
OBJECT_IMPLEMENT_UNREF_CLEANUP(eis_device);
static
OBJECT_IMPLEMENT_CREATE(eis_device);
static
OBJECT_IMPLEMENT_PARENT(eis_device, eis_seat);
_public_
OBJECT_IMPLEMENT_GETTER(eis_device, user_data, void *);
_public_
OBJECT_IMPLEMENT_SETTER(eis_device, user_data, void *);
_public_
OBJECT_IMPLEMENT_GETTER(eis_device, name, const char *);
_public_
OBJECT_IMPLEMENT_GETTER(eis_device, type, enum eis_device_type);
_public_
OBJECT_IMPLEMENT_GETTER(eis_device, width, uint32_t);
_public_
OBJECT_IMPLEMENT_GETTER(eis_device, height, uint32_t);
_public_ struct eis_seat *
eis_device_get_seat(struct eis_device *device)
{
return eis_device_parent(device);
}
_public_ struct eis_region *
eis_device_get_region(struct eis_device *device, size_t index)
{
return list_nth_entry(struct eis_region, &device->regions, link, index);
}
_public_ struct eis_client *
eis_device_get_client(struct eis_device *device)
{
return eis_seat_get_client(eis_device_get_seat(device));
}
_public_ struct eis_device *
eis_seat_new_device(struct eis_seat *seat)
{
static uint32_t deviceid;
struct eis_device *device = eis_device_create(&seat->object);
device->id = seat->id | ++deviceid;
device->name = xstrdup("unnamed device");
device->capabilities = 0;
device->state = EIS_DEVICE_STATE_NEW;
device->type = EIS_DEVICE_TYPE_VIRTUAL;
list_init(&device->regions);
list_init(&device->regions_new);
list_init(&device->pending_event_queue);
list_append(&seat->devices, &device->link);
return eis_device_ref(device);
}
_public_ void
eis_device_configure_type(struct eis_device *device, enum eis_device_type type)
{
if (device->state != EIS_DEVICE_STATE_NEW)
return;
switch (type) {
case EIS_DEVICE_TYPE_VIRTUAL:
case EIS_DEVICE_TYPE_PHYSICAL:
break;
default:
log_bug_client(eis_device_get_context(device), "Invalid device type %u", type);
return;
}
device->type = type;
}
_public_ void
eis_device_configure_name(struct eis_device *device, const char *name)
{
if (device->state != EIS_DEVICE_STATE_NEW)
return;
free(device->name);
device->name = xstrdup(name);
}
_public_ void
eis_device_configure_capability(struct eis_device *device, enum eis_device_capability cap)
{
if (device->state != EIS_DEVICE_STATE_NEW)
return;
if (!eis_seat_has_capability(eis_device_get_seat(device), cap))
return;
flag_set(device->capabilities, cap);
}
_public_ void
eis_device_configure_size(struct eis_device *device, uint32_t width, uint32_t height)
{
if (device->type != EIS_DEVICE_TYPE_PHYSICAL) {
log_bug_client(eis_device_get_context(device), "Device type physical requird for size");
return;
}
if (width > 2000 || height > 2000)
log_warn(eis_device_get_context(device), "Suspicious device size: %ux%umm", width, height);
device->width = width;
device->height = height;
}
_public_ void
eis_device_add(struct eis_device *device)
{
if (device->state != EIS_DEVICE_STATE_NEW) {
log_bug_client(eis_device_get_context(device),
"%s: device already (dis)connected", __func__);
return;
}
if (!device->capabilities) {
log_bug_client(eis_device_get_context(device),
"%s: adding device without capabilities", __func__);
}
device->state = EIS_DEVICE_STATE_PAUSED;
eis_client_add_device(eis_device_get_client(device), device);
}
_public_ void
eis_device_remove(struct eis_device *device)
{
if (device->state == EIS_DEVICE_STATE_DEAD)
return;
if (device->state == EIS_DEVICE_STATE_EMULATING &&
!eis_client_is_sender(eis_device_get_client(device)))
eis_device_stop_emulating(device);
device->state = EIS_DEVICE_STATE_DEAD;
eis_client_remove_device(eis_device_get_client(device), device);
list_remove(&device->link);
eis_device_unref(device);
}
_public_ bool
eis_device_has_capability(struct eis_device *device,
enum eis_device_capability cap)
{
switch (cap) {
case EIS_DEVICE_CAP_POINTER:
case EIS_DEVICE_CAP_POINTER_ABSOLUTE:
case EIS_DEVICE_CAP_KEYBOARD:
case EIS_DEVICE_CAP_TOUCH:
return flag_is_set(device->capabilities, cap);
}
return false;
}
#define handle_request_noargs(device_, func_) { \
struct eis *eis = eis_device_get_context(device); \
eis->requests->func_(device_, device->id); \
}
#define handle_request(device_, func_, ...) { \
struct eis *eis = eis_device_get_context(device); \
eis->requests->func_(device_, device->id, __VA_ARGS__); \
}
static void
eis_device_frame_now(struct eis_device *device)
{
uint64_t now = eis_now(eis_device_get_context(device));
eis_device_frame(device, now);
}
static void
_flush_frame(struct eis_device *device, const char *func)
{
if (device->send_frame_event) {
log_bug_client(eis_device_get_context(device),
"%s: missing call to eis_device_frame()", func);
eis_device_frame_now(device);
}
}
#define eis_device_flush_frame(d_) _flush_frame(d_, __func__)
_public_ void
eis_device_start_emulating(struct eis_device *device, uint32_t sequence)
{
if (device->state != EIS_DEVICE_STATE_RESUMED)
return;
assert(!device->send_frame_event);
device->state = EIS_DEVICE_STATE_EMULATING;
handle_request(device, start_emulating, sequence);
}
_public_ void
eis_device_stop_emulating(struct eis_device *device)
{
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
eis_device_flush_frame(device);
device->state = EIS_DEVICE_STATE_RESUMED;
handle_request_noargs(device, stop_emulating);
}
_public_ void
eis_device_pointer_motion(struct eis_device *device,
double x, double y)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a pointer", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
device->send_frame_event = true;
handle_request(device, rel, x, y);
}
_public_ void
eis_device_pointer_motion_absolute(struct eis_device *device,
double x, double y)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not an absolute pointer", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
struct eis_region *r;
list_for_each(r, &device->regions, link) {
if (!eis_region_contains(r, x, y)) {
return;
}
}
device->send_frame_event = true;
handle_request(device, abs, x, y);
}
_public_ void
eis_device_pointer_button(struct eis_device *device,
uint32_t button, bool is_press)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a pointer", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
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(eis_device_get_context(device),
"%s: button code must be one of BTN_*", __func__);
return;
}
device->send_frame_event = true;
handle_request(device, button, button, is_press);
}
static inline void
eis_device_resume_scrolling(struct eis_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
eis_device_pointer_scroll(struct eis_device *device,
double x, double y)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER) &&
!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a (absolute) pointer", __func__);
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
eis_device_resume_scrolling(device, x, y);
device->send_frame_event = true;
handle_request(device, scroll, x, y);
}
_public_ void
eis_device_pointer_scroll_stop(struct eis_device *device, bool x, bool y)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER) &&
!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a (absolute) pointer", __func__);
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
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) {
device->send_frame_event = true;
handle_request(device, scroll_stop, x, y, false);
}
}
_public_ void
eis_device_pointer_scroll_cancel(struct eis_device *device, bool x, bool y)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER) &&
!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a (absolute) pointer", __func__);
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
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) {
device->send_frame_event = true;
handle_request(device, scroll_stop, x, y, true);
}
}
_public_ void
eis_device_pointer_scroll_discrete(struct eis_device *device,
int32_t x, int32_t y)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER) &&
!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a (absolute) pointer", __func__);
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
eis_device_resume_scrolling(device, x, y);
device->send_frame_event = true;
handle_request(device, scroll_discrete, x, y);
}
_public_ void
eis_device_keyboard_key(struct eis_device *device,
uint32_t key, bool is_press)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_KEYBOARD)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a keyboard", __func__);
return;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
device->send_frame_event = true;
handle_request(device, key, key, is_press);
}
_public_
OBJECT_IMPLEMENT_REF(eis_touch);
_public_
OBJECT_IMPLEMENT_UNREF_CLEANUP(eis_touch);
_public_
OBJECT_IMPLEMENT_GETTER(eis_touch, device, struct eis_device*);
_public_
OBJECT_IMPLEMENT_GETTER(eis_touch, user_data, void *);
_public_
OBJECT_IMPLEMENT_SETTER(eis_touch, user_data, void *);
static void
eis_touch_destroy(struct eis_touch *touch)
{
if (touch->state == TOUCH_IS_DOWN)
eis_touch_up(touch);
/* Enforce a frame, otherwise we're just pending. If the client
* doesn't want this, it needs to eis_touch_up() */
eis_device_frame_now(touch->device);
eis_device_unref(touch->device);
}
static
OBJECT_IMPLEMENT_CREATE(eis_touch);
_public_ struct eis_touch *
eis_device_touch_new(struct eis_device *device)
{
static uint32_t tracking_id = 0;
/* Not using the device as parent object because we need a ref
* to it */
struct eis_touch *touch = eis_touch_create(NULL);
touch->device = eis_device_ref(device);
touch->state = TOUCH_IS_NEW;
touch->tracking_id = ++tracking_id;
return touch;
}
_public_ void
eis_touch_down(struct eis_touch *touch, double x, double y)
{
struct eis_device *device = eis_touch_get_device(touch);
if (touch->state != TOUCH_IS_NEW) {
log_bug_client(eis_device_get_context(device),
"%s: touch %u already down or up", __func__, touch->tracking_id);
return;
}
struct eis_region *r;
list_for_each(r, &device->regions, link) {
if (!eis_region_contains(r, x, y)) {
log_bug_client(eis_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;
device->send_frame_event = true;
handle_request(device, touch_down, touch->tracking_id, x, y);
}
_public_ void
eis_touch_motion(struct eis_touch *touch, double x, double y)
{
if (touch->state != TOUCH_IS_DOWN)
return;
struct eis_device *device = eis_touch_get_device(touch);
struct eis_region *r;
list_for_each(r, &device->regions, link) {
if (!eis_region_contains(r, x, y)) {
log_bug_client(eis_device_get_context(device),
"%s: touch %u has invalid x/y coordinates", __func__, touch->tracking_id);
eis_touch_up(touch);
return;
}
}
device->send_frame_event = true;
handle_request(device, touch_motion, touch->tracking_id, x, y);
}
_public_ void
eis_touch_up(struct eis_touch *touch)
{
struct eis_device *device = eis_touch_get_device(touch);
if (touch->state != TOUCH_IS_DOWN) {
log_bug_client(eis_device_get_context(device),
"%s: touch %u is not currently down", __func__, touch->tracking_id);
return;
}
touch->state = TOUCH_IS_UP;
device->send_frame_event = true;
handle_request(device, touch_up, touch->tracking_id);
}
_public_ void
eis_device_frame(struct eis_device *device, uint64_t time)
{
if (device->state != EIS_DEVICE_STATE_EMULATING)
return;
if (!device->send_frame_event)
return;
device->send_frame_event = false;
handle_request(device, frame, time);
}
int
eis_device_event_frame(struct eis_device *device, uint64_t time)
{
if (device->state != EIS_DEVICE_STATE_EMULATING)
return -EINVAL;
eis_queue_frame_event(device, time);
return 0;
}
int
eis_device_event_pointer_rel(struct eis_device *device,
double x, double y)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a pointer", __func__);
return -EINVAL;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return -EINVAL;
eis_queue_pointer_rel_event(device, x, y);
return 0;
}
static inline bool
eis_device_in_region(struct eis_device *device, double x, double y)
{
struct eis_region *r;
list_for_each(r, &device->regions, link) {
if (eis_region_contains(r, x, y))
return true;
}
return false;
}
int
eis_device_event_pointer_abs(struct eis_device *device,
double x, double y)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not an absolute pointer", __func__);
return -EINVAL;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return -EINVAL;
if (!eis_device_in_region(device, x, y))
return -EINVAL;
eis_queue_pointer_abs_event(device, x, y);
return 0;
}
int
eis_device_event_pointer_button(struct eis_device *device,
uint32_t button, bool is_press)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a pointer", __func__);
return -EINVAL;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return -EINVAL;
eis_queue_pointer_button_event(device, button, is_press);
return 0;
}
int
eis_device_event_pointer_scroll(struct eis_device *device,
double x, double y)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER) &&
!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a (absolute) pointer", __func__);
return -EINVAL;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return -EINVAL;
eis_queue_pointer_scroll_event(device, x, y);
return 0;
}
int
eis_device_event_pointer_scroll_discrete(struct eis_device *device,
int32_t x, int32_t y)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER) &&
!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a (absolute) pointer", __func__);
return -EINVAL;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return -EINVAL;
eis_queue_pointer_scroll_discrete_event(device, x, y);
return 0;
}
int
eis_device_event_pointer_scroll_stop(struct eis_device *device, bool x, bool y)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER) &&
!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a (absolute) pointer", __func__);
return -EINVAL;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return -EINVAL;
eis_queue_pointer_scroll_stop_event(device, x, y);
return 0;
}
int
eis_device_event_pointer_scroll_cancel(struct eis_device *device, bool x, bool y)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER) &&
!eis_device_has_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a (absolute) pointer", __func__);
return -EINVAL;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return -EINVAL;
eis_queue_pointer_scroll_cancel_event(device, x, y);
return 0;
}
int
eis_device_event_keyboard_key(struct eis_device *device,
uint32_t key, bool is_press)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_KEYBOARD)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a keyboard", __func__);
return -EINVAL;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return -EINVAL;
eis_queue_keyboard_key_event(device, key, is_press);
return 0;
}
int
eis_device_event_touch_down(struct eis_device *device, uint32_t touchid, double x, double y)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_TOUCH)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a touch device", __func__);
return -EINVAL;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return -EINVAL;
eis_queue_touch_down_event(device, touchid, x, y);
return 0;
}
int
eis_device_event_touch_motion(struct eis_device *device, uint32_t touchid, double x, double y)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_TOUCH)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a touch device", __func__);
return -EINVAL;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return -EINVAL;
eis_queue_touch_motion_event(device, touchid, x, y);
return 0;
}
int
eis_device_event_touch_up(struct eis_device *device, uint32_t touchid)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_TOUCH)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a touch device", __func__);
return -EINVAL;
}
if (device->state != EIS_DEVICE_STATE_EMULATING)
return -EINVAL;
eis_queue_touch_up_event(device, touchid);
return 0;
}
void
eis_device_closed_by_client(struct eis_device *device)
{
switch (device->state) {
case EIS_DEVICE_STATE_DEAD:
case EIS_DEVICE_STATE_CLOSED_BY_CLIENT:
/* libei bug, ignore */
break;
case EIS_DEVICE_STATE_EMULATING:
if (!eis_client_is_sender(eis_device_get_client(device)))
eis_queue_device_stop_emulating_event(device);
_fallthrough_;
case EIS_DEVICE_STATE_NEW:
case EIS_DEVICE_STATE_PAUSED:
case EIS_DEVICE_STATE_RESUMED:
eis_queue_device_closed_event(device);
device->state = EIS_DEVICE_STATE_CLOSED_BY_CLIENT;
break;
}
}
void
eis_device_event_start_emulating(struct eis_device *device, uint32_t sequence)
{
switch (device->state) {
case EIS_DEVICE_STATE_DEAD:
case EIS_DEVICE_STATE_CLOSED_BY_CLIENT:
case EIS_DEVICE_STATE_NEW:
case EIS_DEVICE_STATE_PAUSED:
break;
case EIS_DEVICE_STATE_RESUMED:
eis_queue_device_start_emulating_event(device, sequence);
device->state = EIS_DEVICE_STATE_EMULATING;
break;
case EIS_DEVICE_STATE_EMULATING:
break;
}
}
void
eis_device_event_stop_emulating(struct eis_device *device)
{
switch (device->state) {
case EIS_DEVICE_STATE_DEAD:
case EIS_DEVICE_STATE_CLOSED_BY_CLIENT:
case EIS_DEVICE_STATE_NEW:
case EIS_DEVICE_STATE_PAUSED:
case EIS_DEVICE_STATE_RESUMED:
break;
case EIS_DEVICE_STATE_EMULATING:
eis_queue_device_stop_emulating_event(device);
device->state = EIS_DEVICE_STATE_RESUMED;
break;
}
}
_public_ void
eis_device_pause(struct eis_device *device)
{
if (device->state != EIS_DEVICE_STATE_RESUMED)
return;
device->state = EIS_DEVICE_STATE_PAUSED;
eis_client_pause_device(eis_device_get_client(device), device);
}
_public_ void
eis_device_resume(struct eis_device *device)
{
if (device->state != EIS_DEVICE_STATE_PAUSED)
return;
device->state = EIS_DEVICE_STATE_RESUMED;
eis_client_resume_device(eis_device_get_client(device), device);
}
_public_ void
eis_device_keyboard_send_xkb_modifiers(struct eis_device *device, uint32_t depressed,
uint32_t latched, uint32_t locked, uint32_t group)
{
if (!eis_device_has_capability(device, EIS_DEVICE_CAP_KEYBOARD)) {
log_bug_client(eis_device_get_context(device),
"%s: device is not a keyboard", __func__);
return;
}
eis_client_keyboard_modifiers(eis_device_get_client(device),
device, depressed, latched, locked, group);
}