mirror of
https://gitlab.freedesktop.org/libinput/libei.git
synced 2026-01-08 15:10:15 +01:00
Punt this job to the caller, any structured logging handler doesn't need them anyway and it makes handling of messages more awkward. For our default log handlers (fprintf) we can just append them ourselves. Fixes #19
999 lines
24 KiB
C
999 lines
24 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 = dup(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_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)
|
|
{
|
|
if (device->state != EIS_DEVICE_STATE_RESUMED)
|
|
return;
|
|
|
|
assert(!device->send_frame_event);
|
|
|
|
device->state = EIS_DEVICE_STATE_EMULATING;
|
|
|
|
handle_request_noargs(device, start_emulating);
|
|
}
|
|
|
|
_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)
|
|
{
|
|
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);
|
|
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);
|
|
}
|