mirror of
https://gitlab.freedesktop.org/libinput/libei.git
synced 2025-12-28 12:00:08 +01:00
This effectively provides the EIS implementation with a notification that the client will actually send events in the near future. To be used by e.g. synergy-like clients when the pointer enters the logical screen so that the EIS implementation can flash a warning or something. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
574 lines
14 KiB
C
574 lines
14 KiB
C
/*
|
|
* 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"
|
|
|
|
_public_
|
|
OBJECT_IMPLEMENT_REF(eis_keymap);
|
|
_public_
|
|
OBJECT_IMPLEMENT_UNREF(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\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (device->keymap) {
|
|
log_bug_client(eis_device_get_context(device),
|
|
"%s: only one keymap can only be assigned and only once\n", __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;
|
|
|
|
list_for_each_safe(r, &device->regions, link)
|
|
eis_region_unref(r);
|
|
|
|
/* regions_new does not own a ref */
|
|
|
|
eis_keymap_unref(device->keymap);
|
|
free(device->name);
|
|
}
|
|
|
|
_public_
|
|
OBJECT_IMPLEMENT_REF(eis_device);
|
|
_public_
|
|
OBJECT_IMPLEMENT_UNREF(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_ struct eis_seat *
|
|
eis_device_get_seat(struct eis_device *device)
|
|
{
|
|
return eis_device_parent(device);
|
|
}
|
|
|
|
_public_ void
|
|
eis_device_set_name(struct eis_device *device, const char *name)
|
|
{
|
|
if (device->state != EIS_DEVICE_STATE_NEW) {
|
|
log_bug_client(eis_device_get_context(device),
|
|
"%s: device already (dis)connected\n", __func__);
|
|
return;
|
|
}
|
|
|
|
free(device->name);
|
|
device->name = xstrdup(name);
|
|
}
|
|
|
|
_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;
|
|
list_init(&device->regions);
|
|
list_init(&device->regions_new);
|
|
|
|
list_append(&seat->devices, &device->link);
|
|
|
|
return eis_device_ref(device);
|
|
}
|
|
|
|
_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_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\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (!device->capabilities) {
|
|
log_bug_client(eis_device_get_context(device),
|
|
"%s: adding device without capabilities\n", __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;
|
|
|
|
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;
|
|
}
|
|
|
|
int
|
|
eis_device_frame(struct eis_device *device)
|
|
{
|
|
if (device->state != EIS_DEVICE_STATE_RESUMED)
|
|
return -EINVAL;
|
|
|
|
eis_queue_frame_event(device);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
eis_device_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\n", __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_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\n", __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_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\n", __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_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\n", __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_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\n", __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_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\n", __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_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\n", __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_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\n", __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_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\n", __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_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\n", __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_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\n", __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:
|
|
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_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_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\n", __func__);
|
|
return;
|
|
}
|
|
|
|
eis_client_keyboard_modifiers(eis_device_get_client(device),
|
|
device, depressed, latched, locked, group);
|
|
}
|