mirror of
https://gitlab.freedesktop.org/libinput/libei.git
synced 2026-01-11 07:20:15 +01:00
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-bits.h"
|
|
#include "util-macros.h"
|
|
#include "util-mem.h"
|
|
#include "util-io.h"
|
|
#include "util-strings.h"
|
|
|
|
#include "libei-private.h"
|
|
|
|
static const char *
|
|
ei_device_state_to_string(enum ei_device_state state)
|
|
{
|
|
switch (state) {
|
|
CASE_RETURN_STRING(EI_DEVICE_STATE_NEW);
|
|
CASE_RETURN_STRING(EI_DEVICE_STATE_CONNECTING);
|
|
CASE_RETURN_STRING(EI_DEVICE_STATE_SUSPENDED);
|
|
CASE_RETURN_STRING(EI_DEVICE_STATE_RESUMED);
|
|
CASE_RETURN_STRING(EI_DEVICE_STATE_REMOVED);
|
|
}
|
|
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 %d: %s → %s\n",
|
|
device->id, ei_device_state_to_string(old_state),
|
|
ei_device_state_to_string(state));
|
|
}
|
|
|
|
static void
|
|
ei_device_destroy(struct ei_device *device)
|
|
{
|
|
free(device->name);
|
|
xclose(device->keymap.fd);
|
|
}
|
|
|
|
_public_
|
|
OBJECT_IMPLEMENT_REF(ei_device);
|
|
_public_
|
|
OBJECT_IMPLEMENT_UNREF(ei_device);
|
|
#define _cleanup_ei_device_ _cleanup_(ei_device_cleanup)
|
|
|
|
static
|
|
OBJECT_IMPLEMENT_CREATE(ei_device);
|
|
static
|
|
OBJECT_IMPLEMENT_PARENT(ei_device, ei);
|
|
_public_
|
|
OBJECT_IMPLEMENT_GETTER(ei_device, name, const char *);
|
|
_public_
|
|
OBJECT_IMPLEMENT_GETTER(ei_device, user_data, void *);
|
|
_public_
|
|
OBJECT_IMPLEMENT_SETTER(ei_device, user_data, void *);
|
|
|
|
_public_ struct ei*
|
|
ei_device_get_context(struct ei_device *device)
|
|
{
|
|
assert(device);
|
|
return ei_device_parent(device);
|
|
}
|
|
|
|
_public_
|
|
struct ei_device *
|
|
ei_device_new(struct ei *ei)
|
|
{
|
|
/* device IDs are managed by the client, the server merely accepts
|
|
* them and fails where they're being reused. */
|
|
static uint32_t deviceid = 0;
|
|
struct ei_device *device = ei_device_create(&ei->object);
|
|
|
|
device->capabilities = 0;
|
|
device->id = ++deviceid;
|
|
device->state = EI_DEVICE_STATE_NEW;
|
|
device->name = xaprintf("unnamed device %d", device->id);
|
|
device->keymap.source = EI_KEYMAP_SOURCE_SERVER;
|
|
device->keymap.fd = -1;
|
|
device->keymap.type = EI_KEYMAP_TYPE_NONE;
|
|
device->keymap.size = 0;
|
|
|
|
return device;
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_configure_name(struct ei_device *device, const char *name)
|
|
{
|
|
if (device->state != EI_DEVICE_STATE_NEW)
|
|
return;
|
|
|
|
free(device->name);
|
|
device->name = xstrdup(name);
|
|
}
|
|
|
|
_public_ bool
|
|
ei_device_configure_capability(struct ei_device *device,
|
|
enum ei_device_capability cap)
|
|
{
|
|
if (device->state != EI_DEVICE_STATE_NEW)
|
|
return false;
|
|
|
|
switch (cap) {
|
|
case EI_DEVICE_CAP_POINTER:
|
|
case EI_DEVICE_CAP_POINTER_ABSOLUTE:
|
|
case EI_DEVICE_CAP_KEYBOARD:
|
|
case EI_DEVICE_CAP_TOUCH:
|
|
device->capabilities |= bit(cap);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_pointer_configure_range(struct ei_device *device,
|
|
uint32_t width,
|
|
uint32_t height)
|
|
{
|
|
if (device->state != EI_DEVICE_STATE_NEW ||
|
|
!flag_is_set(device->capabilities, EI_DEVICE_CAP_POINTER_ABSOLUTE))
|
|
return;
|
|
|
|
if (width == 0 || height == 0)
|
|
return;
|
|
|
|
device->abs.dim.width = width;
|
|
device->abs.dim.height = height;
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_touch_configure_range(struct ei_device *device,
|
|
uint32_t width,
|
|
uint32_t height)
|
|
{
|
|
if (device->state != EI_DEVICE_STATE_NEW ||
|
|
!flag_is_set(device->capabilities, EI_DEVICE_CAP_TOUCH))
|
|
return;
|
|
|
|
if (width == 0 || height == 0)
|
|
return;
|
|
|
|
device->touch.dim.width = width;
|
|
device->touch.dim.height = height;
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_keyboard_configure_keymap(struct ei_device *device,
|
|
enum ei_keymap_type type,
|
|
int fd, size_t size)
|
|
{
|
|
if (device->state != EI_DEVICE_STATE_NEW ||
|
|
!flag_is_set(device->capabilities, EI_DEVICE_CAP_KEYBOARD))
|
|
return;
|
|
|
|
if (fd < 0)
|
|
return;
|
|
|
|
/*
|
|
* This can be a quiet error because it should never happen
|
|
* anyway and if it does, the device will just have a -1 keymap
|
|
* after being accepted by the server.
|
|
*/
|
|
int newfd = dup(fd);
|
|
if (newfd < 0)
|
|
return;
|
|
|
|
device->keymap.source = EI_KEYMAP_SOURCE_CLIENT;
|
|
device->keymap.fd = newfd;
|
|
device->keymap.type = type;
|
|
device->keymap.size = size;
|
|
}
|
|
|
|
void
|
|
ei_device_set_keymap(struct ei_device *device,
|
|
enum ei_keymap_type type,
|
|
int keymap_fd, size_t size)
|
|
{
|
|
device->keymap.source = EI_KEYMAP_SOURCE_SERVER;
|
|
device->keymap.fd = xclose(device->keymap.fd);
|
|
device->keymap.type = type;
|
|
device->keymap.size = size;
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_add(struct ei_device *device)
|
|
{
|
|
if (ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE) &&
|
|
(device->abs.dim.width == 0 || device->abs.dim.height == 0))
|
|
log_bug(ei_device_get_context(device),
|
|
"device %s is missing an abs pointer range\n",
|
|
device->name);
|
|
|
|
ei_add_device(device);
|
|
ei_device_set_state(device, EI_DEVICE_STATE_CONNECTING);
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_remove(struct ei_device *device)
|
|
{
|
|
switch (device->state) {
|
|
case EI_DEVICE_STATE_NEW:
|
|
case EI_DEVICE_STATE_REMOVED:
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ei_device_set_state(device, EI_DEVICE_STATE_REMOVED);
|
|
ei_remove_device(device);
|
|
}
|
|
|
|
void
|
|
ei_device_resumed(struct ei_device *device)
|
|
{
|
|
ei_device_set_state(device, EI_DEVICE_STATE_RESUMED);
|
|
}
|
|
|
|
void
|
|
ei_device_suspended(struct ei_device *device)
|
|
{
|
|
ei_device_set_state(device, EI_DEVICE_STATE_SUSPENDED);
|
|
}
|
|
|
|
void
|
|
ei_device_added(struct ei_device *device)
|
|
{
|
|
ei_device_suspended(device);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
_public_ uint32_t
|
|
ei_device_pointer_get_width(struct ei_device *device)
|
|
{
|
|
return device->abs.dim.width;
|
|
}
|
|
|
|
_public_ uint32_t
|
|
ei_device_pointer_get_height(struct ei_device *device)
|
|
{
|
|
return device->abs.dim.height;
|
|
}
|
|
|
|
_public_ uint32_t
|
|
ei_device_touch_get_width(struct ei_device *device)
|
|
{
|
|
return device->touch.dim.width;
|
|
}
|
|
|
|
_public_ uint32_t
|
|
ei_device_touch_get_height(struct ei_device *device)
|
|
{
|
|
return device->touch.dim.height;
|
|
}
|
|
|
|
_public_ enum ei_keymap_source
|
|
ei_device_keyboard_get_keymap_source(struct ei_device *device)
|
|
{
|
|
return device->keymap.source;
|
|
}
|
|
|
|
_public_ enum ei_keymap_type
|
|
ei_device_keyboard_get_keymap_type(struct ei_device *device)
|
|
{
|
|
return device->keymap.type;
|
|
}
|
|
|
|
_public_ int
|
|
ei_device_keyboard_get_keymap(struct ei_device *device)
|
|
{
|
|
return device->keymap.fd;
|
|
}
|
|
|
|
_public_ size_t
|
|
ei_device_keyboard_get_keymap_size(struct ei_device *device)
|
|
{
|
|
return device->keymap.size;
|
|
}
|
|
|
|
_public_ void
|
|
ei_device_pointer_motion(struct ei_device *device,
|
|
double x, double y)
|
|
{
|
|
if (!ei_device_has_capability(device, EI_DEVICE_CAP_POINTER))
|
|
return;
|
|
|
|
if (device->state != EI_DEVICE_STATE_RESUMED)
|
|
return;
|
|
|
|
ei_pointer_rel(device, x, y);
|
|
}
|
|
|
|
_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))
|
|
return;
|
|
|
|
if (device->state != EI_DEVICE_STATE_RESUMED)
|
|
return;
|
|
|
|
if (x < 0 || x >= device->abs.dim.width ||
|
|
y < 0 || y >= device->abs.dim.height)
|
|
return;
|
|
|
|
ei_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))
|
|
return;
|
|
|
|
if (device->state != EI_DEVICE_STATE_RESUMED)
|
|
return;
|
|
|
|
ei_pointer_button(device, button, is_press);
|
|
}
|
|
|
|
_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))
|
|
return;
|
|
|
|
if (device->state != EI_DEVICE_STATE_RESUMED)
|
|
return;
|
|
|
|
ei_keyboard_key(device, key, is_press);
|
|
}
|
|
|
|
|
|
|
|
#ifdef _enable_tests_
|
|
#include <munit.h>
|
|
|
|
static MunitResult
|
|
test_device_new(const MunitParameter params[], void *user_data)
|
|
{
|
|
struct ei ei = {0};
|
|
struct ei_device *d = ei_device_new(&ei);
|
|
|
|
munit_assert_int(d->id, >, 0);
|
|
munit_assert_int(d->capabilities, ==, 0);
|
|
munit_assert_int(d->state, ==, EI_DEVICE_STATE_NEW);
|
|
|
|
struct ei_device *unrefd = ei_device_unref(d);
|
|
munit_assert_ptr_null(unrefd);
|
|
return MUNIT_OK;
|
|
}
|
|
|
|
static MunitResult
|
|
test_device_ids(const MunitParameter params[], void *user_data)
|
|
{
|
|
struct ei ei = {0};
|
|
_cleanup_ei_device_ struct ei_device *d1 = ei_device_new(&ei);
|
|
_cleanup_ei_device_ struct ei_device *d2 = ei_device_new(&ei);
|
|
_cleanup_ei_device_ struct ei_device *d3 = ei_device_new(&ei);
|
|
|
|
munit_assert_int(d1->id, <, d2->id);
|
|
munit_assert_int(d1->id, <, d3->id);
|
|
munit_assert_int(d2->id, <, d3->id);
|
|
|
|
return MUNIT_OK;
|
|
}
|
|
|
|
static MunitResult
|
|
test_device_ref_unref(const MunitParameter params[], void *user_data)
|
|
{
|
|
struct ei ei = {0};
|
|
struct ei_device *d = ei_device_new(&ei);
|
|
|
|
munit_assert_int(d->object.refcount, ==, 1);
|
|
|
|
struct ei_device *refd = ei_device_ref(d);
|
|
munit_assert_ptr_equal(d, refd);
|
|
munit_assert_int(d->object.refcount, ==, 2);
|
|
|
|
struct ei_device *unrefd = ei_device_unref(d);
|
|
munit_assert_ptr_null(unrefd);
|
|
munit_assert_int(d->object.refcount, ==, 1);
|
|
|
|
unrefd = ei_device_unref(d);
|
|
munit_assert_ptr_null(unrefd);
|
|
|
|
return MUNIT_OK;
|
|
}
|
|
|
|
static MunitResult
|
|
test_device_cap(const MunitParameter params[], void *user_data)
|
|
{
|
|
struct ei ei = {0};
|
|
_cleanup_ei_device_ struct ei_device *d = ei_device_new(&ei);
|
|
|
|
munit_assert(ei_device_configure_capability(d, EI_DEVICE_CAP_POINTER));
|
|
/* twice is fine */
|
|
munit_assert(ei_device_configure_capability(d, EI_DEVICE_CAP_POINTER));
|
|
munit_assert(ei_device_has_capability(d, EI_DEVICE_CAP_POINTER));
|
|
|
|
munit_assert(ei_device_configure_capability(d, EI_DEVICE_CAP_POINTER_ABSOLUTE));
|
|
munit_assert(ei_device_has_capability(d, EI_DEVICE_CAP_POINTER_ABSOLUTE));
|
|
|
|
munit_assert(ei_device_configure_capability(d, EI_DEVICE_CAP_KEYBOARD));
|
|
munit_assert(ei_device_has_capability(d, EI_DEVICE_CAP_KEYBOARD));
|
|
|
|
munit_assert(ei_device_configure_capability(d, EI_DEVICE_CAP_TOUCH));
|
|
munit_assert(ei_device_has_capability(d, EI_DEVICE_CAP_TOUCH));
|
|
|
|
/* Invalid caps */
|
|
munit_assert(!ei_device_configure_capability(d, 0));
|
|
munit_assert(!ei_device_has_capability(d, 0));
|
|
munit_assert(!ei_device_configure_capability(d, EI_DEVICE_CAP_TOUCH + 1));
|
|
munit_assert(!ei_device_has_capability(d, EI_DEVICE_CAP_TOUCH + 1));
|
|
|
|
return MUNIT_OK;
|
|
}
|
|
|
|
static MunitResult
|
|
test_device_context(const MunitParameter params[], void *user_data)
|
|
{
|
|
struct ei ei = {0};
|
|
_cleanup_ei_device_ struct ei_device *d = ei_device_new(&ei);
|
|
|
|
munit_assert_ptr_equal(d->object.parent, &ei);
|
|
|
|
return MUNIT_OK;
|
|
}
|
|
|
|
static MunitResult
|
|
test_device_pointer_ranges(const MunitParameter params[], void *user_data)
|
|
{
|
|
struct ei ei = {0};
|
|
_cleanup_ei_device_ struct ei_device *d = ei_device_new(&ei);
|
|
|
|
/* Missing the cap */
|
|
ei_device_pointer_configure_range(d, 1920, 1200);
|
|
munit_assert_int(ei_device_pointer_get_width(d), ==, 0);
|
|
munit_assert_int(ei_device_pointer_get_height(d), ==, 0);
|
|
|
|
munit_assert(ei_device_configure_capability(d, EI_DEVICE_CAP_POINTER_ABSOLUTE));
|
|
|
|
ei_device_pointer_configure_range(d, 1024, 768);
|
|
munit_assert_int(ei_device_pointer_get_width(d), ==, 1024);
|
|
munit_assert_int(ei_device_pointer_get_height(d), ==, 768);
|
|
|
|
/* Twice is fine before adding the device */
|
|
ei_device_pointer_configure_range(d, 640, 480);
|
|
munit_assert_int(ei_device_pointer_get_width(d), ==, 640);
|
|
munit_assert_int(ei_device_pointer_get_height(d), ==, 480);
|
|
|
|
/* But zero is silently rejected */
|
|
ei_device_pointer_configure_range(d, 0, 480);
|
|
munit_assert_int(ei_device_pointer_get_width(d), ==, 640);
|
|
munit_assert_int(ei_device_pointer_get_height(d), ==, 480);
|
|
ei_device_pointer_configure_range(d, 640, 0);
|
|
munit_assert_int(ei_device_pointer_get_width(d), ==, 640);
|
|
munit_assert_int(ei_device_pointer_get_height(d), ==, 480);
|
|
|
|
return MUNIT_OK;
|
|
}
|
|
|
|
static MunitResult
|
|
test_device_touch_ranges(const MunitParameter params[], void *user_data)
|
|
{
|
|
struct ei ei = {0};
|
|
_cleanup_ei_device_ struct ei_device *d = ei_device_new(&ei);
|
|
|
|
/* Missing the cap */
|
|
ei_device_touch_configure_range(d, 1920, 1200);
|
|
munit_assert_int(ei_device_touch_get_width(d), ==, 0);
|
|
munit_assert_int(ei_device_touch_get_height(d), ==, 0);
|
|
|
|
munit_assert(ei_device_configure_capability(d, EI_DEVICE_CAP_TOUCH));
|
|
|
|
ei_device_touch_configure_range(d, 1024, 768);
|
|
munit_assert_int(ei_device_touch_get_width(d), ==, 1024);
|
|
munit_assert_int(ei_device_touch_get_height(d), ==, 768);
|
|
|
|
/* Twice is fine before adding the device */
|
|
ei_device_touch_configure_range(d, 640, 480);
|
|
munit_assert_int(ei_device_touch_get_width(d), ==, 640);
|
|
munit_assert_int(ei_device_touch_get_height(d), ==, 480);
|
|
|
|
/* But zero is silently rejected */
|
|
ei_device_touch_configure_range(d, 0, 480);
|
|
munit_assert_int(ei_device_touch_get_width(d), ==, 640);
|
|
munit_assert_int(ei_device_touch_get_height(d), ==, 480);
|
|
ei_device_touch_configure_range(d, 640, 0);
|
|
munit_assert_int(ei_device_touch_get_width(d), ==, 640);
|
|
munit_assert_int(ei_device_touch_get_height(d), ==, 480);
|
|
|
|
return MUNIT_OK;
|
|
}
|
|
|
|
#define TEST(_func) \
|
|
{ .name = #_func, .test = _func }
|
|
static MunitTest ei_device_tests[] = {
|
|
TEST(test_device_new),
|
|
TEST(test_device_ids),
|
|
TEST(test_device_ref_unref),
|
|
TEST(test_device_cap),
|
|
TEST(test_device_context),
|
|
TEST(test_device_pointer_ranges),
|
|
TEST(test_device_touch_ranges),
|
|
{ NULL },
|
|
};
|
|
|
|
static const MunitSuite libei_device_suite
|
|
__attribute__((used))
|
|
__attribute__((section("test_section"))) = {
|
|
"/device/",
|
|
ei_device_tests,
|
|
NULL,
|
|
1,
|
|
MUNIT_SUITE_OPTION_NONE,
|
|
};
|
|
#endif
|