mirror of
https://gitlab.freedesktop.org/libinput/libei.git
synced 2025-12-28 23:40:08 +01:00
Add a new simple object "brei_result" that maps to the protocol-type reason + explanation. That object is now returned instead of the errno, giving us better debugging options. This changes the dispatcher functions from returning an int to returning a brei_result instead (default NULL for success). A helper function for converting a neg errno to a result is provided for convenience for now, eventually all these paths should deal with things correctly.
1630 lines
41 KiB
C
1630 lines
41 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-time.h"
|
|
#include "util-strings.h"
|
|
|
|
#include "libei-private.h"
|
|
#include "ei-proto.h"
|
|
|
|
DEFINE_UNREF_CLEANUP_FUNC(ei_region);
|
|
|
|
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_pointer_unref(device->pointer);
|
|
ei_touchscreen_unref(device->touchscreen);
|
|
ei_keyboard_unref(device->keyboard);
|
|
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 *);
|
|
OBJECT_IMPLEMENT_GETTER(ei_device, width, uint32_t);
|
|
OBJECT_IMPLEMENT_GETTER(ei_device, height, uint32_t);
|
|
OBJECT_IMPLEMENT_GETTER(ei_device, id, uint32_t);
|
|
OBJECT_IMPLEMENT_GETTER_AS_REF(ei_device, proto_object, const struct brei_object *);
|
|
|
|
_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));
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_destroy(struct ei_device *device)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
log_debug(ei, "Removed device %#x", ei_device_get_id(device));
|
|
ei_device_removed_by_server(device);
|
|
return NULL;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_name(struct ei_device *device, const char *name)
|
|
{
|
|
ei_device_set_name(device, name);
|
|
return NULL;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_capabilities(struct ei_device *device, uint32_t caps)
|
|
{
|
|
ei_device_set_capabilities(device, caps);
|
|
return NULL;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_type(struct ei_device *device, enum ei_device_type type)
|
|
{
|
|
ei_device_set_type(device, type);
|
|
return NULL;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_dimensions(struct ei_device *device, uint32_t width, uint32_t height)
|
|
{
|
|
ei_device_set_size(device, width, height);
|
|
return NULL;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_region(struct ei_device *device, uint32_t x, uint32_t y,
|
|
uint32_t w, uint32_t h, float scale)
|
|
{
|
|
_unref_(ei_region) *r = ei_region_new();
|
|
ei_region_set_offset(r, x, y);
|
|
ei_region_set_size(r, w, h);
|
|
ei_region_set_physical_scale(r, scale);
|
|
ei_device_add_region(device, r);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_done(struct ei_device *device)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
ei_device_added(device);
|
|
ei_queue_device_added_event(device);
|
|
ei_device_done(device);
|
|
log_debug(ei,
|
|
"Added device %#x '%s' caps: %s%s%s%s seat: %s",
|
|
ei_device_get_id(device), ei_device_get_name(device),
|
|
ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) ? "p" : "",
|
|
ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE) ? "a" : "",
|
|
ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD) ? "k" : "",
|
|
ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH) ? "t" : "",
|
|
ei_seat_get_name(ei_device_get_seat(device)));
|
|
return NULL;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_resumed(struct ei_device *device)
|
|
{
|
|
ei_device_resumed(device);
|
|
ei_queue_device_resumed_event(device);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_paused(struct ei_device *device)
|
|
{
|
|
ei_device_paused(device);
|
|
ei_queue_device_paused_event(device);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#define DISCONNECT_IF_SENDER_CONTEXT(device_) do {\
|
|
struct ei *ei_ = ei_device_get_context(device_); \
|
|
if (ei_is_sender(ei_)) { \
|
|
log_bug_client(ei_, "Invalid event from receiver EIS context. Disconnecting"); \
|
|
return brei_result_new_from_neg_errno(-ECANCELED); \
|
|
} \
|
|
} while(0)
|
|
|
|
|
|
static struct brei_result *
|
|
handle_msg_start_emulating(struct ei_device *device, uint32_t sequence)
|
|
{
|
|
DISCONNECT_IF_SENDER_CONTEXT(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, sequence);
|
|
device->state = EI_DEVICE_STATE_EMULATING;
|
|
break;
|
|
case EI_DEVICE_STATE_REMOVED_FROM_CLIENT:
|
|
case EI_DEVICE_STATE_REMOVED_FROM_SERVER:
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_stop_emulating(struct ei_device *device)
|
|
{
|
|
DISCONNECT_IF_SENDER_CONTEXT(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;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_frame(struct ei_device *device, uint32_t time, uint32_t micros)
|
|
{
|
|
DISCONNECT_IF_SENDER_CONTEXT(device);
|
|
|
|
ei_queue_frame_event(device, ms2us(time) + micros);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_pointer(struct ei_device *device, uint32_t id, uint32_t version)
|
|
{
|
|
if (device->pointer)
|
|
return brei_result_new_from_neg_errno(-EPROTO);
|
|
|
|
device->pointer = ei_pointer_new(device, id, version);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_keyboard(struct ei_device *device, uint32_t id, uint32_t version)
|
|
{
|
|
if (device->keyboard)
|
|
return brei_result_new_from_neg_errno(-EPROTO);
|
|
|
|
device->keyboard = ei_keyboard_new(device, id, version);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_touchscreen(struct ei_device *device, uint32_t id, uint32_t version)
|
|
{
|
|
if (device->touchscreen)
|
|
return brei_result_new_from_neg_errno(-EPROTO);
|
|
|
|
device->touchscreen = ei_touchscreen_new(device, id, version);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const struct ei_device_interface interface = {
|
|
.destroyed = handle_msg_destroy,
|
|
.name = handle_msg_name,
|
|
.capabilities = handle_msg_capabilities,
|
|
.type = handle_msg_type,
|
|
.dimensions = handle_msg_dimensions,
|
|
.region = handle_msg_region,
|
|
.done = handle_msg_done,
|
|
.resumed = handle_msg_resumed,
|
|
.paused = handle_msg_paused,
|
|
.start_emulating = handle_msg_start_emulating,
|
|
.stop_emulating = handle_msg_stop_emulating,
|
|
.frame = handle_msg_frame,
|
|
.pointer = handle_msg_pointer,
|
|
.keyboard = handle_msg_keyboard,
|
|
.touchscreen = handle_msg_touchscreen,
|
|
};
|
|
|
|
const struct ei_device_interface *
|
|
ei_device_get_interface(struct ei_device *device)
|
|
{
|
|
return &interface;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_pointer_rel(struct ei_pointer *pointer, float x, float y)
|
|
{
|
|
struct ei_device *device = ei_pointer_get_device(pointer);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(device);
|
|
|
|
return brei_result_new_from_neg_errno(ei_device_event_pointer_rel(device, x, y));
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_pointer_abs(struct ei_pointer *pointer, float x, float y)
|
|
{
|
|
struct ei_device *device = ei_pointer_get_device(pointer);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(device);
|
|
|
|
return brei_result_new_from_neg_errno(ei_device_event_pointer_abs(device, x, y));
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_pointer_button(struct ei_pointer *pointer,
|
|
uint32_t button, uint32_t state)
|
|
{
|
|
struct ei_device *device = ei_pointer_get_device(pointer);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(device);
|
|
|
|
return brei_result_new_from_neg_errno(ei_device_event_pointer_button(device, button, !!state));
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_pointer_scroll(struct ei_pointer *pointer, float x, float y)
|
|
{
|
|
struct ei_device *device = ei_pointer_get_device(pointer);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(device);
|
|
|
|
return brei_result_new_from_neg_errno(ei_device_event_pointer_scroll(device, x, y));
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_pointer_scroll_discrete(struct ei_pointer *pointer,
|
|
int32_t x, int32_t y)
|
|
{
|
|
struct ei_device *device = ei_pointer_get_device(pointer);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(device);
|
|
|
|
return brei_result_new_from_neg_errno(ei_device_event_pointer_scroll_discrete(device, x, y));
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_pointer_scroll_stop(struct ei_pointer *pointer,
|
|
uint32_t x, uint32_t y, uint32_t is_cancel)
|
|
{
|
|
struct ei_device *device = ei_pointer_get_device(pointer);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(device);
|
|
|
|
if (is_cancel)
|
|
return brei_result_new_from_neg_errno(ei_device_event_pointer_scroll_cancel(device, !!x, !!y));
|
|
else
|
|
return brei_result_new_from_neg_errno(ei_device_event_pointer_scroll_stop(device, !!x, !!y));
|
|
|
|
return brei_result_new_from_neg_errno(-EINVAL);
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_pointer_destroy(struct ei_pointer *pointer)
|
|
{
|
|
struct ei_device *device = ei_pointer_get_device(pointer);
|
|
ei_pointer_unref(steal(&device->pointer));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const struct ei_pointer_interface pointer_interface = {
|
|
.destroyed = handle_msg_pointer_destroy,
|
|
.motion_relative = handle_msg_pointer_rel,
|
|
.motion_absolute = handle_msg_pointer_abs,
|
|
.button = handle_msg_pointer_button,
|
|
.scroll = handle_msg_pointer_scroll,
|
|
.scroll_stop = handle_msg_pointer_scroll_stop,
|
|
.scroll_discrete = handle_msg_pointer_scroll_discrete,
|
|
};
|
|
|
|
const struct ei_pointer_interface *
|
|
ei_device_get_pointer_interface(struct ei_device *device)
|
|
{
|
|
return &pointer_interface;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_keymap(struct ei_keyboard *keyboard, uint32_t keymap_type, uint32_t keymap_sz, int keymap_fd)
|
|
{
|
|
struct ei_device *device = ei_keyboard_get_device(keyboard);
|
|
|
|
ei_device_set_keymap(device, keymap_type, keymap_fd, keymap_sz);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_keyboard_key(struct ei_keyboard *keyboard, uint32_t key, uint32_t state)
|
|
{
|
|
struct ei_device *device = ei_keyboard_get_device(keyboard);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(device);
|
|
|
|
return brei_result_new_from_neg_errno(ei_device_event_keyboard_key(device, key, !!state));
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_keyboard_modifiers(struct ei_keyboard *keyboard, uint32_t depressed,
|
|
uint32_t locked, uint32_t latched, uint32_t group)
|
|
{
|
|
struct ei *ei = ei_keyboard_get_context(keyboard);
|
|
struct ei_device *device = ei_keyboard_get_device(keyboard);
|
|
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD)) {
|
|
log_bug(ei,"Modifier event for non-keyboard");
|
|
return brei_result_new_from_neg_errno(-EPROTO);
|
|
}
|
|
|
|
struct ei_xkb_modifiers mods = {
|
|
.depressed = depressed,
|
|
.latched = latched,
|
|
.locked = locked,
|
|
.group = group,
|
|
};
|
|
|
|
ei_queue_keyboard_modifiers_event(device, &mods);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_keyboard_destroy(struct ei_keyboard *keyboard)
|
|
{
|
|
|
|
struct ei_device *device = ei_keyboard_get_device(keyboard);
|
|
ei_keyboard_unref(steal(&device->keyboard));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const struct ei_keyboard_interface keyboard_interface = {
|
|
.destroyed = handle_msg_keyboard_destroy,
|
|
.keymap = handle_msg_keymap,
|
|
.key = handle_msg_keyboard_key,
|
|
.modifiers = handle_msg_keyboard_modifiers,
|
|
};
|
|
|
|
const struct ei_keyboard_interface *
|
|
ei_device_get_keyboard_interface(struct ei_device *device)
|
|
{
|
|
return &keyboard_interface;
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_touch_down(struct ei_touchscreen *touchscreen,
|
|
uint32_t touchid, float x, float y)
|
|
{
|
|
struct ei_device *device = ei_touchscreen_get_device(touchscreen);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(device);
|
|
|
|
return brei_result_new_from_neg_errno(ei_device_event_touch_down(device, touchid, x, y));
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_touch_motion(struct ei_touchscreen *touchscreen,
|
|
uint32_t touchid, float x, float y)
|
|
{
|
|
struct ei_device *device = ei_touchscreen_get_device(touchscreen);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(device);
|
|
|
|
return brei_result_new_from_neg_errno(ei_device_event_touch_motion(device, touchid, x, y));
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_touch_up(struct ei_touchscreen *touchscreen, uint32_t touchid)
|
|
{
|
|
struct ei_device *device = ei_touchscreen_get_device(touchscreen);
|
|
|
|
DISCONNECT_IF_SENDER_CONTEXT(device);
|
|
|
|
return brei_result_new_from_neg_errno(ei_device_event_touch_up(device, touchid));
|
|
}
|
|
|
|
static struct brei_result *
|
|
handle_msg_touchscreen_destroy(struct ei_touchscreen *touchscreen)
|
|
{
|
|
|
|
struct ei_device *device = ei_touchscreen_get_device(touchscreen);
|
|
ei_touchscreen_unref(steal(&device->touchscreen));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const struct ei_touchscreen_interface touchscreen_interface = {
|
|
.destroyed = handle_msg_touchscreen_destroy,
|
|
.down = handle_msg_touch_down,
|
|
.motion = handle_msg_touch_motion,
|
|
.up = handle_msg_touch_up,
|
|
};
|
|
|
|
const struct ei_touchscreen_interface *
|
|
ei_device_get_touchscreen_interface(struct ei_device *device)
|
|
{
|
|
return &touchscreen_interface;
|
|
}
|
|
|
|
struct ei_device *
|
|
ei_device_new(struct ei_seat *seat, uint32_t deviceid, uint32_t version)
|
|
{
|
|
struct ei_device *device = ei_device_create(&seat->object);
|
|
struct ei *ei = ei_seat_get_context(seat);
|
|
|
|
device->proto_object.id = deviceid,
|
|
device->proto_object.implementation = device;
|
|
device->proto_object.interface = &ei_device_proto_interface;
|
|
device->proto_object.version = version;
|
|
list_init(&device->proto_object.link);
|
|
ei_register_object(ei, &device->proto_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);
|
|
|
|
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 = xdup(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);
|
|
}
|
|
|
|
static int
|
|
ei_device_send_release(struct ei_device *device)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
if (device->pointer)
|
|
ei_pointer_request_release(device->pointer);
|
|
if (device->keyboard)
|
|
ei_keyboard_request_release(device->keyboard);
|
|
if (device->touchscreen)
|
|
ei_touchscreen_request_release(device->touchscreen);
|
|
|
|
int rc = ei_device_request_release(device);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
|
|
_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_device_request_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_device_send_release(device);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
ei_device_removed_by_server(struct ei_device *device)
|
|
{
|
|
struct ei_seat *seat = ei_device_get_seat(device);
|
|
struct ei *ei = ei_device_get_context(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:
|
|
/* in the case of ei_disconnect() we may fake the
|
|
* removal by the server, so we need to also remove the
|
|
* pointer/keyboard/touch interfaces
|
|
*/
|
|
ei_pointer_unref(steal(&device->pointer));
|
|
ei_keyboard_unref(steal(&device->keyboard));
|
|
ei_touchscreen_unref(steal(&device->touchscreen));
|
|
|
|
ei_unregister_object(ei, &device->proto_object);
|
|
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, uint32_t sequence)
|
|
{
|
|
if (device->state != EI_DEVICE_STATE_RESUMED)
|
|
return;
|
|
|
|
assert(!device->send_frame_event);
|
|
|
|
device->state = EI_DEVICE_STATE_EMULATING;
|
|
int rc = ei_device_request_start_emulating(device, sequence);
|
|
if (rc)
|
|
ei_disconnect(ei_device_get_context(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;
|
|
int rc = ei_device_request_stop_emulating(device);
|
|
if (rc)
|
|
ei_disconnect(ei_device_get_context(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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
static int
|
|
ei_send_pointer_rel(struct ei_device *device, double x, double y)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_pointer_request_motion_relative(device->pointer, x, y);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
ei_send_pointer_abs(struct ei_device *device, double x, double y)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_pointer_request_motion_absolute(device->pointer, x, y);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
ei_send_pointer_button(struct ei_device *device, uint32_t button, bool is_press)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_pointer_request_button(device->pointer, button, is_press);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
ei_send_pointer_scroll(struct ei_device *device, double x, double y)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_pointer_request_scroll(device->pointer, x, y);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
ei_send_pointer_scroll_stop(struct ei_device *device, double x, double y)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_pointer_request_scroll_stop(device->pointer, x, y, false);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
ei_send_pointer_scroll_cancel(struct ei_device *device, double x, double y)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_pointer_request_scroll_stop(device->pointer, x, y, true);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
ei_send_pointer_scroll_discrete(struct ei_device *device, int32_t x, int32_t y)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_pointer_request_scroll_discrete(device->pointer, x, y);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
_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);
|
|
}
|
|
|
|
_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);
|
|
}
|
|
|
|
static int
|
|
ei_send_keyboard_key(struct ei_device *device, uint32_t key, bool is_press)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_keyboard_request_key(device->keyboard, key, is_press);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
|
|
_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);
|
|
|
|
static int
|
|
ei_send_touch_down(struct ei_device *device, uint32_t tid,
|
|
double x, double y)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_touchscreen_request_down(device->touchscreen, tid, x, y);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
ei_send_touch_motion(struct ei_device *device, uint32_t tid,
|
|
double x, double y)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_touchscreen_request_motion(device->touchscreen, tid, x, y);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
ei_send_touch_up(struct ei_device *device, uint32_t tid)
|
|
{
|
|
struct ei *ei = ei_device_get_context(device);
|
|
|
|
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
device->send_frame_event = true;
|
|
|
|
int rc = ei_touchscreen_request_up(device->touchscreen, tid);
|
|
if (rc)
|
|
ei_disconnect(ei);
|
|
return rc;
|
|
}
|
|
|
|
|
|
_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;
|
|
|
|
if (!device->send_frame_event)
|
|
return;
|
|
|
|
device->send_frame_event = false;
|
|
|
|
int rc = ei_device_request_frame(device, us2ms(time), time % 1000);
|
|
if (rc)
|
|
ei_disconnect(ei_device_get_context(device));
|
|
return;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|