libei/test/eierpecken.c

1744 lines
48 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: MIT */
2020-08-05 17:12:54 +10:00
/*
* 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 <math.h>
#include <signal.h>
2020-08-05 17:12:54 +10:00
#include <stdlib.h>
#include <stdio.h>
2020-08-05 17:12:54 +10:00
#include <sys/socket.h>
#include <sys/time.h>
2020-08-05 17:12:54 +10:00
#include <munit.h>
#include "eierpecken.h"
#include "util-bits.h"
#include "util-color.h"
#include "util-io.h"
2020-08-05 17:12:54 +10:00
#include "util-mem.h"
#include "util-munit.h"
2020-08-05 17:12:54 +10:00
#include "util-tristate.h"
#include "util-logger.h"
#include "util-object.h"
#include "util-strings.h"
#include "util-time.h"
2020-08-05 17:12:54 +10:00
static bool enable_sigalarm;
2020-08-05 17:12:54 +10:00
DEFINE_TRISTATE(yes, no, unset);
struct peck_log_capture {
bool enabled;
char **debug;
char **info;
char **warning;
char **error;
};
2020-08-05 17:12:54 +10:00
struct peck {
struct object object;
struct ei *ei;
int ei_fd;
2020-08-05 17:12:54 +10:00
struct eis *eis;
uint32_t eis_behavior;
uint32_t ei_behavior;
struct logger *logger;
/* The default seat/devices */
struct eis_seat *eis_seat;
struct eis_device *eis_pointer;
struct eis_device *eis_keyboard;
struct eis_device *eis_abs;
struct eis_device *eis_button;
struct eis_device *eis_scroll;
struct eis_device *eis_touch;
struct ei_seat *ei_seat;
struct ei_device *ei_pointer;
struct ei_device *ei_keyboard;
struct ei_device *ei_abs;
struct ei_device *ei_button;
struct ei_device *ei_scroll;
struct ei_device *ei_touch;
uint64_t now;
struct eis_client *eis_client;
uint32_t indent;
struct sigaction sigact;
bool ei_fatal_bugs;
bool eis_fatal_bugs;
uint64_t ei_time_offset;
uint64_t eis_time_offset;
struct peck_log_capture ei_log_capture;
struct peck_log_capture eis_log_capture;
bool ei_needs_frame;
bool eis_needs_frame;
2020-08-05 17:12:54 +10:00
};
static const uint32_t INDENTATION = 4;
static void
peck_indent(struct peck *peck)
{
peck->indent += INDENTATION;
}
static void
peck_dedent(struct peck *peck)
{
assert(peck->indent >= INDENTATION);
peck->indent -= INDENTATION;
}
static
OBJECT_IMPLEMENT_GETTER(peck, indent, uint32_t);
static uint64_t
peck_ei_now_func(struct ei *ei)
{
static uint64_t offset = 0;
struct peck *peck = ei_get_user_data(ei);
if (peck->ei_time_offset != offset) {
offset = peck->ei_time_offset;
log_debug(peck, "Time is now offset by %" PRIu64"us\n", offset);
}
uint64_t ts = 0;
now(&ts);
return ts + peck->ei_time_offset;
}
static uint64_t
peck_eis_now_func(struct eis *eis)
{
static uint64_t offset = 0;
struct peck *peck = eis_get_user_data(eis);
if (peck->eis_time_offset != offset) {
offset = peck->eis_time_offset;
log_debug(peck, "Time is now offset by %" PRIu64"us\n", offset);
}
uint64_t ts = 0;
now(&ts);
return ts + peck->ei_time_offset;
}
2020-08-05 17:12:54 +10:00
static void
peck_destroy(struct peck *peck)
{
log_debug(peck, "destroying peck context\n");
/* we don't want valgrind to complain about us not handling *all*
* events in a test */
peck_drain_ei(peck);
peck_drain_eis(peck);
log_debug(peck, "final event processing done\n");
eis_client_unref(peck->eis_client);
eis_device_unref(peck->eis_pointer);
eis_device_unref(peck->eis_abs);
eis_device_unref(peck->eis_keyboard);
eis_device_unref(peck->eis_touch);
eis_device_unref(peck->eis_button);
eis_device_unref(peck->eis_scroll);
eis_seat_unref(peck->eis_seat);
ei_device_unref(peck->ei_pointer);
ei_device_unref(peck->ei_abs);
ei_device_unref(peck->ei_keyboard);
ei_device_unref(peck->ei_touch);
ei_device_unref(peck->ei_button);
ei_device_unref(peck->ei_scroll);
ei_seat_unref(peck->ei_seat);
2020-08-05 17:12:54 +10:00
ei_unref(peck->ei);
eis_unref(peck->eis);
logger_unref(peck->logger);
if (enable_sigalarm) {
struct itimerval timer = {0};
setitimer(ITIMER_REAL, &timer, 0);
sigaction(SIGALRM, &peck->sigact, NULL);
}
strv_free(peck->ei_log_capture.debug);
strv_free(peck->ei_log_capture.info);
strv_free(peck->ei_log_capture.warning);
strv_free(peck->ei_log_capture.error);
strv_free(peck->eis_log_capture.debug);
strv_free(peck->eis_log_capture.info);
strv_free(peck->eis_log_capture.warning);
strv_free(peck->eis_log_capture.error);
2020-08-05 17:12:54 +10:00
}
void
peck_ei_add_time_offset(struct peck *peck, uint64_t us)
{
log_debug(peck, "Adding ei time offset of %" PRIu64 "us\n", us);
peck->ei_time_offset += us;
}
void
peck_eis_add_time_offset(struct peck *peck, uint64_t us)
{
log_debug(peck, "Adding EIS time offset of %" PRIu64 "us\n", us);
peck->eis_time_offset += us;
}
static
2020-08-05 17:12:54 +10:00
OBJECT_IMPLEMENT_CREATE(peck);
OBJECT_IMPLEMENT_UNREF(peck);
2020-08-05 17:12:54 +10:00
OBJECT_IMPLEMENT_GETTER(peck, ei, struct ei*);
OBJECT_IMPLEMENT_GETTER(peck, eis, struct eis*);
OBJECT_IMPLEMENT_GETTER(peck, ei_fd, int);
2020-08-05 17:12:54 +10:00
void
peck_drop_ei(struct peck *peck)
{
peck->ei_pointer = ei_device_unref(peck->ei_pointer);
peck->ei_keyboard = ei_device_unref(peck->ei_keyboard);
peck->ei_abs = ei_device_unref(peck->ei_abs);
peck->ei_touch = ei_device_unref(peck->ei_touch);
peck->ei_seat = ei_seat_unref(peck->ei_seat);
peck->ei = NULL;
}
char **
peck_ei_get_log_capture(struct peck *peck, enum ei_log_priority priority)
{
switch (priority) {
case EI_LOG_PRIORITY_ERROR:
return peck->ei_log_capture.error;
case EI_LOG_PRIORITY_WARNING:
return peck->ei_log_capture.warning;
case EI_LOG_PRIORITY_INFO:
return peck->ei_log_capture.info;
case EI_LOG_PRIORITY_DEBUG:
return peck->ei_log_capture.debug;
default:
abort();
}
}
void
peck_ei_enable_log_capture(struct peck *peck)
{
peck->ei_log_capture.enabled = true;
}
void
peck_ei_disable_log_capture(struct peck *peck)
{
peck->ei_log_capture.enabled = true;
}
char **
peck_eis_get_log_capture(struct peck *peck, enum eis_log_priority priority)
{
switch (priority) {
case EIS_LOG_PRIORITY_ERROR:
return peck->eis_log_capture.error;
case EIS_LOG_PRIORITY_WARNING:
return peck->eis_log_capture.warning;
case EIS_LOG_PRIORITY_INFO:
return peck->eis_log_capture.info;
case EIS_LOG_PRIORITY_DEBUG:
return peck->eis_log_capture.debug;
default:
abort();
}
}
void
peck_eis_enable_log_capture(struct peck *peck)
{
peck->eis_log_capture.enabled = true;
}
void
peck_eis_disable_log_capture(struct peck *peck)
{
peck->eis_log_capture.enabled = true;
}
struct eis_client *
peck_eis_get_default_client(struct peck *peck)
{
munit_assert_ptr_not_null(peck->eis_client);
return peck->eis_client;
};
struct eis_seat *
peck_eis_get_default_seat(struct peck *peck)
{
munit_assert_ptr_not_null(peck->eis_seat);
return peck->eis_seat;
};
struct eis_device *
peck_eis_get_default_pointer(struct peck *peck)
{
munit_assert_ptr_not_null(peck->eis_pointer);
return peck->eis_pointer;
};
struct eis_device *
peck_eis_get_default_pointer_absolute(struct peck *peck)
{
munit_assert_ptr_not_null(peck->eis_abs);
return peck->eis_abs;
};
struct eis_device *
peck_eis_get_default_button(struct peck *peck)
{
munit_assert_ptr_not_null(peck->eis_button);
return peck->eis_button;
};
struct eis_device *
peck_eis_get_default_scroll(struct peck *peck)
{
munit_assert_ptr_not_null(peck->eis_scroll);
return peck->eis_scroll;
};
struct eis_device *
peck_eis_get_default_keyboard(struct peck *peck)
{
munit_assert_ptr_not_null(peck->eis_keyboard);
return peck->eis_keyboard;
};
struct eis_device *
peck_eis_get_default_touch(struct peck *peck)
{
munit_assert_ptr_not_null(peck->eis_touch);
return peck->eis_touch;
};
struct ei_seat *
peck_ei_get_default_seat(struct peck *peck)
{
munit_assert_ptr_not_null(peck->ei_seat);
return peck->ei_seat;
}
struct ei_device *
peck_ei_get_default_pointer(struct peck *peck)
{
munit_assert_ptr_not_null(peck->ei_pointer);
return peck->ei_pointer;
};
struct ei_device *
peck_ei_get_default_pointer_absolute(struct peck *peck)
{
munit_assert_ptr_not_null(peck->ei_abs);
return peck->ei_abs;
};
struct ei_device *
peck_ei_get_default_button(struct peck *peck)
{
munit_assert_ptr_not_null(peck->ei_button);
return peck->ei_button;
};
struct ei_device *
peck_ei_get_default_scroll(struct peck *peck)
{
munit_assert_ptr_not_null(peck->ei_scroll);
return peck->ei_scroll;
};
struct ei_device *
peck_ei_get_default_keyboard(struct peck *peck)
{
munit_assert_ptr_not_null(peck->ei_keyboard);
return peck->ei_keyboard;
};
struct ei_device *
peck_ei_get_default_touch(struct peck *peck)
{
munit_assert_ptr_not_null(peck->ei_touch);
return peck->ei_touch;
};
/* Ensures that device frames in tests always have an ascending and fixed
* interval. Every time this is called it adds 10ms to the time offset.
*/
uint64_t
peck_ei_now(struct peck *peck)
{
peck_ei_add_time_offset(peck, ms2us(10));
return ei_now(peck->ei);
}
uint64_t
peck_eis_now(struct peck *peck)
{
peck_eis_add_time_offset(peck, ms2us(10));
return eis_now(peck->eis);
}
void
peck_ei_disable_fatal_bug(struct peck *peck)
{
peck->ei_fatal_bugs = false;
}
void
peck_ei_enable_fatal_bug(struct peck *peck)
{
peck->ei_fatal_bugs = true;
}
void
peck_eis_disable_fatal_bug(struct peck *peck)
{
peck->eis_fatal_bugs = false;
}
void
peck_eis_enable_fatal_bug(struct peck *peck)
{
peck->eis_fatal_bugs = true;
}
static void
peck_capture_log(struct peck_log_capture *capture,
enum ei_log_priority priority,
const char *message)
{
if (!capture->enabled)
return;
switch (priority) {
case EI_LOG_PRIORITY_ERROR:
capture->error = strv_append_strdup(capture->error, message);
break;
case EI_LOG_PRIORITY_WARNING:
capture->info = strv_append_strdup(capture->info, message);
break;
case EI_LOG_PRIORITY_INFO:
capture->info = strv_append_strdup(capture->info, message);
break;
case EI_LOG_PRIORITY_DEBUG:
capture->debug = strv_append_strdup(capture->debug, message);
break;
default:
abort();
}
}
static void
peck_ei_log_handler(struct ei *ei,
enum ei_log_priority priority,
const char *message,
struct ei_log_context *ctx)
{
bool use_color = true;
run_only_once {
use_color = isatty(STDERR_FILENO);
}
const char *prefix = NULL;
switch (priority) {
case EI_LOG_PRIORITY_ERROR: prefix = "ERR "; break;
case EI_LOG_PRIORITY_WARNING: prefix = "WRN "; break;
case EI_LOG_PRIORITY_INFO: prefix = "INF "; break;
case EI_LOG_PRIORITY_DEBUG: prefix = " "; break;
}
struct peck *peck = ei_get_user_data(ei);
fprintf(stderr, "| | ei | | %s |%*s🥚 %s%s%s\n",
prefix, (int)peck_get_indent(peck), " ",
use_color ? ANSI_FG_RGB(150, 125, 100) : "",
message,
use_color ? ansi_colorcode[RESET] : "");
if (peck->ei_fatal_bugs) {
assert(!strstr(message, "🪳"));
assert(!strstr(message, "🪲"));
}
peck_capture_log(&peck->ei_log_capture, priority, message);
}
static void
peck_eis_log_handler(struct eis *eis,
enum eis_log_priority priority,
const char *message,
struct eis_log_context *ctx)
{
bool use_color = true;
run_only_once {
use_color = isatty(STDERR_FILENO);
}
const char *prefix = NULL;
switch (priority) {
case EIS_LOG_PRIORITY_ERROR: prefix = "ERR "; break;
case EIS_LOG_PRIORITY_WARNING: prefix = "WRN "; break;
case EIS_LOG_PRIORITY_INFO: prefix = "INF "; break;
case EIS_LOG_PRIORITY_DEBUG: prefix = " "; break;
}
struct peck *peck = eis_get_user_data(eis);
fprintf(stderr, "| | | EIS | %s |%*s🍨 %s%s%s\n",
prefix, (int)peck_get_indent(peck), " ",
use_color ? ANSI_FG_RGB(150, 200, 125) : "",
message,
use_color ? ansi_colorcode[RESET] : "");
if (peck->eis_fatal_bugs) {
assert(!strstr(message, "🪳"));
assert(!strstr(message, "🪲"));
}
peck_capture_log(&peck->eis_log_capture, (enum ei_log_priority)priority, message);
}
_printf_(7, 0)
static void
peck_log_handler(struct logger *logger,
const char *prefix,
enum logger_priority priority,
const char *file, int lineno, const char *func,
const char *format, va_list args)
{
_cleanup_free_ char *msgtype;
bool use_color = true;
run_only_once {
use_color = isatty(STDERR_FILENO);
}
switch(priority) {
case LOGGER_DEBUG: msgtype = xaprintf("%4d", lineno); break;
case LOGGER_INFO: msgtype = xstrdup("INF"); break;
case LOGGER_WARN: msgtype = xstrdup("WRN"); break;
case LOGGER_ERROR: msgtype = xstrdup("ERR"); break;
default:
msgtype = xstrdup("<invalid msgtype>");
break;
}
struct peck *peck = logger_get_user_data(logger);
fprintf(stderr, "| peck | | | %s |%*s%s",
msgtype,
(int)peck_get_indent(peck), " ",
use_color ? ANSI_FG_RGB(125, 150, 255) : "");
vfprintf(stderr, format, args);
if (use_color)
fprintf(stderr, "%s", ansi_colorcode[RESET]);
}
static void
handle_sigalrm(int signo)
{
/* Nothing to do but this may cause a few EINTR */
}
static struct peck *
new_context(const char *what, va_list args)
2020-08-05 17:12:54 +10:00
{
struct peck *peck = peck_create(NULL);
enum peck_ei_mode ei_mode = PECK_EI_SENDER;
proto: add a device ready request The protocol currently supports a ei_device.done event to notify the ei client that the initial description of the device is complete. For some future use-cases the client may need to futher negotiate properties of the device. For example for tablet tools the client may narrow down capabilities of the tool. The sequence with the new request is thus e.g. -> ei_seat.device -> ei_device.name -> ei_device.interface -> ei_device.interface -> ei_device.done <- ei_device.ready -> ei_device.resumed In libei the request is sent automatically on unref of the DEVICE_ADDED event. This makes clients immediately compatible and for the typical (future) use-case of device configuration. Said configuration will likely be handled in response to the DEVICE_ADDED event anyway. In libeis, a new EIS_EVENT_DEVICE_READY event that is sent when the client sends that same event on the protocol, informing the EIS implementation that this device is ready. For clients that do not support that version the event is emulated immediately after sending ei_device.done. This requires a flag bit to be long-term maintainable. The typical EIS implementation currently calls eis_device_add() immediately followed by eis_device_resume(). This doesn't leave any room to wait for the client's ei_device.ready request. One backwards-compatible solution could be to buffer the eis_device_resume() until the ei_device.ready has been received but this is fraught with hairy corner cases, e.g. if the client is a receiver context we would also have to buffer all events immediately sent to the client. So instead, we have a flag in the context and if set by the caller, we change the internal behavior to match ei_device interface version 3. Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/346>
2025-07-11 09:47:50 +10:00
uint32_t eis_flags = 0;
while (what) {
if (streq(what, "mode"))
ei_mode = va_arg(args, enum peck_ei_mode);
proto: add a device ready request The protocol currently supports a ei_device.done event to notify the ei client that the initial description of the device is complete. For some future use-cases the client may need to futher negotiate properties of the device. For example for tablet tools the client may narrow down capabilities of the tool. The sequence with the new request is thus e.g. -> ei_seat.device -> ei_device.name -> ei_device.interface -> ei_device.interface -> ei_device.done <- ei_device.ready -> ei_device.resumed In libei the request is sent automatically on unref of the DEVICE_ADDED event. This makes clients immediately compatible and for the typical (future) use-case of device configuration. Said configuration will likely be handled in response to the DEVICE_ADDED event anyway. In libeis, a new EIS_EVENT_DEVICE_READY event that is sent when the client sends that same event on the protocol, informing the EIS implementation that this device is ready. For clients that do not support that version the event is emulated immediately after sending ei_device.done. This requires a flag bit to be long-term maintainable. The typical EIS implementation currently calls eis_device_add() immediately followed by eis_device_resume(). This doesn't leave any room to wait for the client's ei_device.ready request. One backwards-compatible solution could be to buffer the eis_device_resume() until the ei_device.ready has been received but this is fraught with hairy corner cases, e.g. if the client is a receiver context we would also have to buffer all events immediately sent to the client. So instead, we have a flag in the context and if set by the caller, we change the internal behavior to match ei_device interface version 3. Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/346>
2025-07-11 09:47:50 +10:00
if (streq(what, "eis-flags"))
eis_flags = va_arg(args, uint32_t);
what = va_arg(args, const char *);
}
proto: add a device ready request The protocol currently supports a ei_device.done event to notify the ei client that the initial description of the device is complete. For some future use-cases the client may need to futher negotiate properties of the device. For example for tablet tools the client may narrow down capabilities of the tool. The sequence with the new request is thus e.g. -> ei_seat.device -> ei_device.name -> ei_device.interface -> ei_device.interface -> ei_device.done <- ei_device.ready -> ei_device.resumed In libei the request is sent automatically on unref of the DEVICE_ADDED event. This makes clients immediately compatible and for the typical (future) use-case of device configuration. Said configuration will likely be handled in response to the DEVICE_ADDED event anyway. In libeis, a new EIS_EVENT_DEVICE_READY event that is sent when the client sends that same event on the protocol, informing the EIS implementation that this device is ready. For clients that do not support that version the event is emulated immediately after sending ei_device.done. This requires a flag bit to be long-term maintainable. The typical EIS implementation currently calls eis_device_add() immediately followed by eis_device_resume(). This doesn't leave any room to wait for the client's ei_device.ready request. One backwards-compatible solution could be to buffer the eis_device_resume() until the ei_device.ready has been received but this is fraught with hairy corner cases, e.g. if the client is a receiver context we would also have to buffer all events immediately sent to the client. So instead, we have a flag in the context and if set by the caller, we change the internal behavior to match ei_device interface version 3. Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/346>
2025-07-11 09:47:50 +10:00
assert(ei_mode == PECK_EI_RECEIVER || ei_mode == PECK_EI_SENDER);
int rc, fd;
struct eis *eis = eis_new(peck);
proto: add a device ready request The protocol currently supports a ei_device.done event to notify the ei client that the initial description of the device is complete. For some future use-cases the client may need to futher negotiate properties of the device. For example for tablet tools the client may narrow down capabilities of the tool. The sequence with the new request is thus e.g. -> ei_seat.device -> ei_device.name -> ei_device.interface -> ei_device.interface -> ei_device.done <- ei_device.ready -> ei_device.resumed In libei the request is sent automatically on unref of the DEVICE_ADDED event. This makes clients immediately compatible and for the typical (future) use-case of device configuration. Said configuration will likely be handled in response to the DEVICE_ADDED event anyway. In libeis, a new EIS_EVENT_DEVICE_READY event that is sent when the client sends that same event on the protocol, informing the EIS implementation that this device is ready. For clients that do not support that version the event is emulated immediately after sending ei_device.done. This requires a flag bit to be long-term maintainable. The typical EIS implementation currently calls eis_device_add() immediately followed by eis_device_resume(). This doesn't leave any room to wait for the client's ei_device.ready request. One backwards-compatible solution could be to buffer the eis_device_resume() until the ei_device.ready has been received but this is fraught with hairy corner cases, e.g. if the client is a receiver context we would also have to buffer all events immediately sent to the client. So instead, we have a flag in the context and if set by the caller, we change the internal behavior to match ei_device interface version 3. Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/346>
2025-07-11 09:47:50 +10:00
if (eis_flags != 0) {
size_t shift = 0;
while (eis_flags) {
if (eis_flags & 0x1)
eis_set_flag(eis, bit(shift));
eis_flags >>= 1;
shift++;
}
}
eis_log_set_handler(eis, peck_eis_log_handler);
eis_log_set_priority(eis, EIS_LOG_PRIORITY_DEBUG);
eis_clock_set_now_func(eis, peck_eis_now_func);
rc = eis_setup_backend_fd(eis);
2020-08-05 17:12:54 +10:00
munit_assert_int(rc, ==, 0);
fd = eis_backend_fd_add_client(eis);
munit_assert_int(fd, >, 0);
peck->eis = eis;
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_NONE);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_FRAME);
2024-12-10 12:36:30 +10:00
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_SYNC);
2020-08-05 17:12:54 +10:00
struct ei *ei = ei_mode == PECK_EI_RECEIVER ? ei_new_receiver(peck) : ei_new_sender(peck);
ei_set_user_data(ei, peck);
ei_log_set_handler(ei, peck_ei_log_handler);
ei_log_set_priority(ei, EI_LOG_PRIORITY_DEBUG);
ei_clock_set_now_func(ei, peck_ei_now_func);
2020-08-05 17:12:54 +10:00
ei_configure_name(ei, "eierpecken test context");
rc = ei_setup_backend_fd(ei, fd);
munit_assert_int(rc, ==, 0);
2020-08-05 17:12:54 +10:00
peck->ei = ei;
peck->ei_fd = fd;
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_CONNECT);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTOSEAT);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTOSTART);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_FRAME);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_SYNC);
2020-08-05 17:12:54 +10:00
peck_ei_enable_fatal_bug(peck);
peck_eis_enable_fatal_bug(peck);
peck->logger = logger_new("peck", peck);
logger_set_handler(peck->logger, peck_log_handler);
2020-08-05 17:12:54 +10:00
logger_set_priority(peck->logger, LOGGER_DEBUG);
if (enable_sigalarm) {
/* We set up SIGALRM to hammer us because libei is used by Xwayland which
* uses that for scheduling and we need to be able to handle that */
struct sigaction act;
sigaddset(&act.sa_mask, SIGALRM);
act.sa_flags = 0;
act.sa_handler = handle_sigalrm;
rc = xerrno(sigaction(SIGALRM, &act, &peck->sigact));
if (rc >= 0) {
struct itimerval timer = {
.it_interval = {
.tv_sec = 0,
.tv_usec = 50,
},
.it_value = {
.tv_sec = 0,
.tv_usec = 50,
}
};
rc = xerrno(setitimer(ITIMER_REAL, &timer, 0));
if (rc < 0) {
log_error(peck, "Failed to enable timer: %s\n", strerror(-rc));
munit_assert_int(rc, >=, 0);
}
} else {
log_error(peck, "Failed to enable SIGALRM: %s\n", strerror(-rc));
munit_assert_int(rc, >=, 0);
}
}
2020-08-05 17:12:54 +10:00
return peck;
}
struct peck *
_peck_new_context(const char *what, ...)
{
va_list args;
va_start(args, what);
struct peck *peck = new_context(what, args);
va_end(args);
return peck;
}
void
2020-08-05 17:12:54 +10:00
peck_enable_eis_behavior(struct peck *peck, enum peck_eis_behavior behavior)
{
switch (behavior) {
case PECK_EIS_BEHAVIOR_NONE:
peck->eis_behavior = 0;
break;
case PECK_EIS_BEHAVIOR_DEFAULT_SEAT:
case PECK_EIS_BEHAVIOR_NO_DEFAULT_SEAT:
flag_set(peck->eis_behavior, behavior);
break;
2020-08-05 17:12:54 +10:00
case PECK_EIS_BEHAVIOR_ACCEPT_ALL:
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT);
2020-08-05 17:12:54 +10:00
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_BIND_SEAT);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_RESUME_DEVICE);
proto: add a device ready request The protocol currently supports a ei_device.done event to notify the ei client that the initial description of the device is complete. For some future use-cases the client may need to futher negotiate properties of the device. For example for tablet tools the client may narrow down capabilities of the tool. The sequence with the new request is thus e.g. -> ei_seat.device -> ei_device.name -> ei_device.interface -> ei_device.interface -> ei_device.done <- ei_device.ready -> ei_device.resumed In libei the request is sent automatically on unref of the DEVICE_ADDED event. This makes clients immediately compatible and for the typical (future) use-case of device configuration. Said configuration will likely be handled in response to the DEVICE_ADDED event anyway. In libeis, a new EIS_EVENT_DEVICE_READY event that is sent when the client sends that same event on the protocol, informing the EIS implementation that this device is ready. For clients that do not support that version the event is emulated immediately after sending ei_device.done. This requires a flag bit to be long-term maintainable. The typical EIS implementation currently calls eis_device_add() immediately followed by eis_device_resume(). This doesn't leave any room to wait for the client's ei_device.ready request. One backwards-compatible solution could be to buffer the eis_device_resume() until the ei_device.ready has been received but this is fraught with hairy corner cases, e.g. if the client is a receiver context we would also have to buffer all events immediately sent to the client. So instead, we have a flag in the context and if set by the caller, we change the internal behavior to match ei_device interface version 3. Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/346>
2025-07-11 09:47:50 +10:00
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_DEVICE_READY);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_CLOSE_DEVICE);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_START_EMULATING);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_STOP_EMULATING);
2020-08-05 17:12:54 +10:00
break;
case PECK_EIS_BEHAVIOR_ADD_DEVICES:
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_POINTER);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_POINTER_ABSOLUTE);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_KEYBOARD);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_TOUCH);
2020-08-05 17:12:54 +10:00
break;
case PECK_EIS_BEHAVIOR_HANDLE_BIND_SEAT:
case PECK_EIS_BEHAVIOR_HANDLE_CLOSE_DEVICE:
case PECK_EIS_BEHAVIOR_HANDLE_FRAME:
2024-12-10 12:36:30 +10:00
case PECK_EIS_BEHAVIOR_HANDLE_SYNC:
case PECK_EIS_BEHAVIOR_HANDLE_START_EMULATING:
case PECK_EIS_BEHAVIOR_HANDLE_STOP_EMULATING:
case PECK_EIS_BEHAVIOR_ADD_POINTER:
case PECK_EIS_BEHAVIOR_ADD_POINTER_ABSOLUTE:
case PECK_EIS_BEHAVIOR_ADD_KEYBOARD:
case PECK_EIS_BEHAVIOR_ADD_TOUCH:
proto: add a device ready request The protocol currently supports a ei_device.done event to notify the ei client that the initial description of the device is complete. For some future use-cases the client may need to futher negotiate properties of the device. For example for tablet tools the client may narrow down capabilities of the tool. The sequence with the new request is thus e.g. -> ei_seat.device -> ei_device.name -> ei_device.interface -> ei_device.interface -> ei_device.done <- ei_device.ready -> ei_device.resumed In libei the request is sent automatically on unref of the DEVICE_ADDED event. This makes clients immediately compatible and for the typical (future) use-case of device configuration. Said configuration will likely be handled in response to the DEVICE_ADDED event anyway. In libeis, a new EIS_EVENT_DEVICE_READY event that is sent when the client sends that same event on the protocol, informing the EIS implementation that this device is ready. For clients that do not support that version the event is emulated immediately after sending ei_device.done. This requires a flag bit to be long-term maintainable. The typical EIS implementation currently calls eis_device_add() immediately followed by eis_device_resume(). This doesn't leave any room to wait for the client's ei_device.ready request. One backwards-compatible solution could be to buffer the eis_device_resume() until the ei_device.ready has been received but this is fraught with hairy corner cases, e.g. if the client is a receiver context we would also have to buffer all events immediately sent to the client. So instead, we have a flag in the context and if set by the caller, we change the internal behavior to match ei_device interface version 3. Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/346>
2025-07-11 09:47:50 +10:00
case PECK_EIS_BEHAVIOR_HANDLE_DEVICE_READY:
flag_set(peck->eis_behavior, behavior);
break;
case PECK_EIS_BEHAVIOR_REJECT_CLIENT:
flag_clear(peck->eis_behavior, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT);
flag_set(peck->eis_behavior, behavior);
break;
case PECK_EIS_BEHAVIOR_ACCEPT_CLIENT:
flag_clear(peck->eis_behavior, PECK_EIS_BEHAVIOR_REJECT_CLIENT);
2020-08-05 17:12:54 +10:00
flag_set(peck->eis_behavior, behavior);
break;
case PECK_EIS_BEHAVIOR_RESUME_DEVICE:
flag_clear(peck->eis_behavior, PECK_EIS_BEHAVIOR_SUSPEND_DEVICE);
flag_set(peck->eis_behavior, behavior);
break;
case PECK_EIS_BEHAVIOR_SUSPEND_DEVICE:
flag_clear(peck->eis_behavior, PECK_EIS_BEHAVIOR_RESUME_DEVICE);
flag_set(peck->eis_behavior, behavior);
break;
2020-08-05 17:12:54 +10:00
}
}
void
peck_disable_eis_behavior(struct peck *peck, enum peck_eis_behavior behavior)
{
switch (behavior) {
case PECK_EIS_BEHAVIOR_NONE:
peck->eis_behavior = 0;
break;
case PECK_EIS_BEHAVIOR_DEFAULT_SEAT:
case PECK_EIS_BEHAVIOR_NO_DEFAULT_SEAT:
flag_clear(peck->eis_behavior, behavior);
break;
case PECK_EIS_BEHAVIOR_ACCEPT_ALL:
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT);
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT);
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_BIND_SEAT);
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_RESUME_DEVICE);
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_CLOSE_DEVICE);
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_START_EMULATING);
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_STOP_EMULATING);
break;
case PECK_EIS_BEHAVIOR_ADD_DEVICES:
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_POINTER);
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_POINTER_ABSOLUTE);
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_KEYBOARD);
peck_disable_eis_behavior(peck, PECK_EIS_BEHAVIOR_ADD_TOUCH);
break;
case PECK_EIS_BEHAVIOR_HANDLE_BIND_SEAT:
case PECK_EIS_BEHAVIOR_HANDLE_CLOSE_DEVICE:
case PECK_EIS_BEHAVIOR_HANDLE_FRAME:
case PECK_EIS_BEHAVIOR_HANDLE_SYNC:
case PECK_EIS_BEHAVIOR_HANDLE_START_EMULATING:
case PECK_EIS_BEHAVIOR_HANDLE_STOP_EMULATING:
case PECK_EIS_BEHAVIOR_ADD_POINTER:
case PECK_EIS_BEHAVIOR_ADD_POINTER_ABSOLUTE:
case PECK_EIS_BEHAVIOR_ADD_KEYBOARD:
case PECK_EIS_BEHAVIOR_ADD_TOUCH:
proto: add a device ready request The protocol currently supports a ei_device.done event to notify the ei client that the initial description of the device is complete. For some future use-cases the client may need to futher negotiate properties of the device. For example for tablet tools the client may narrow down capabilities of the tool. The sequence with the new request is thus e.g. -> ei_seat.device -> ei_device.name -> ei_device.interface -> ei_device.interface -> ei_device.done <- ei_device.ready -> ei_device.resumed In libei the request is sent automatically on unref of the DEVICE_ADDED event. This makes clients immediately compatible and for the typical (future) use-case of device configuration. Said configuration will likely be handled in response to the DEVICE_ADDED event anyway. In libeis, a new EIS_EVENT_DEVICE_READY event that is sent when the client sends that same event on the protocol, informing the EIS implementation that this device is ready. For clients that do not support that version the event is emulated immediately after sending ei_device.done. This requires a flag bit to be long-term maintainable. The typical EIS implementation currently calls eis_device_add() immediately followed by eis_device_resume(). This doesn't leave any room to wait for the client's ei_device.ready request. One backwards-compatible solution could be to buffer the eis_device_resume() until the ei_device.ready has been received but this is fraught with hairy corner cases, e.g. if the client is a receiver context we would also have to buffer all events immediately sent to the client. So instead, we have a flag in the context and if set by the caller, we change the internal behavior to match ei_device interface version 3. Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/346>
2025-07-11 09:47:50 +10:00
case PECK_EIS_BEHAVIOR_HANDLE_DEVICE_READY:
case PECK_EIS_BEHAVIOR_REJECT_CLIENT:
case PECK_EIS_BEHAVIOR_ACCEPT_CLIENT:
case PECK_EIS_BEHAVIOR_RESUME_DEVICE:
case PECK_EIS_BEHAVIOR_SUSPEND_DEVICE:
flag_clear(peck->eis_behavior, behavior);
break;
}
}
2020-08-05 17:12:54 +10:00
void
peck_enable_ei_behavior(struct peck *peck, enum peck_ei_behavior behavior)
{
switch (behavior) {
case PECK_EI_BEHAVIOR_NONE:
peck->ei_behavior = 0;
break;
case PECK_EI_BEHAVIOR_HANDLE_CONNECT:
case PECK_EI_BEHAVIOR_AUTOSEAT:
2020-08-05 17:12:54 +10:00
flag_set(peck->ei_behavior, behavior);
break;
case PECK_EI_BEHAVIOR_AUTODEVICES:
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_CONNECT);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTOSEAT);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_RESUMED);
break;
2020-08-05 17:12:54 +10:00
case PECK_EI_BEHAVIOR_HANDLE_ADDED:
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER_ABSOLUTE);
2020-08-05 17:12:54 +10:00
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_KEYBOARD);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_BUTTON);
peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_SCROLL);
2020-08-05 17:12:54 +10:00
break;
case PECK_EI_BEHAVIOR_AUTOSTART:
2020-08-05 17:12:54 +10:00
case PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER_ABSOLUTE:
2020-08-05 17:12:54 +10:00
case PECK_EI_BEHAVIOR_HANDLE_ADDED_KEYBOARD:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_BUTTON:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_SCROLL:
case PECK_EI_BEHAVIOR_HANDLE_FRAME:
case PECK_EI_BEHAVIOR_HANDLE_SYNC:
case PECK_EI_BEHAVIOR_HANDLE_RESUMED:
case PECK_EI_BEHAVIOR_HANDLE_PAUSED:
flag_set(peck->ei_behavior, behavior);
break;
2020-08-05 17:12:54 +10:00
}
}
void
peck_disable_ei_behavior(struct peck *peck, enum peck_ei_behavior behavior)
{
switch (behavior) {
case PECK_EI_BEHAVIOR_NONE:
abort();
break;
case PECK_EI_BEHAVIOR_HANDLE_CONNECT:
case PECK_EI_BEHAVIOR_AUTOSEAT:
flag_clear(peck->ei_behavior, behavior);
break;
case PECK_EI_BEHAVIOR_AUTODEVICES:
peck_disable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_CONNECT);
peck_disable_ei_behavior(peck, PECK_EI_BEHAVIOR_AUTOSEAT);
peck_disable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED);
peck_disable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_RESUMED);
break;
case PECK_EI_BEHAVIOR_HANDLE_ADDED:
peck_disable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER);
peck_disable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER_ABSOLUTE);
peck_disable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_KEYBOARD);
peck_disable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH);
peck_disable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_BUTTON);
peck_disable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_ADDED_SCROLL);
break;
case PECK_EI_BEHAVIOR_AUTOSTART:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER_ABSOLUTE:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_KEYBOARD:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_BUTTON:
case PECK_EI_BEHAVIOR_HANDLE_ADDED_SCROLL:
case PECK_EI_BEHAVIOR_HANDLE_FRAME:
case PECK_EI_BEHAVIOR_HANDLE_SYNC:
case PECK_EI_BEHAVIOR_HANDLE_RESUMED:
case PECK_EI_BEHAVIOR_HANDLE_PAUSED:
flag_clear(peck->ei_behavior, behavior);
break;
}
}
static inline void
peck_create_eis_seat(struct peck *peck, struct eis_client *client)
{
_unref_(eis_seat) *seat = eis_client_new_seat(client, "peck default seat");
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_POINTER);
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_POINTER_ABSOLUTE);
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_KEYBOARD);
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_TOUCH);
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_BUTTON);
eis_seat_configure_capability(seat, EIS_DEVICE_CAP_SCROLL);
log_debug(peck, "EIS adding seat: '%s'\n", eis_seat_get_name(seat));
eis_seat_add(seat);
peck->eis_seat = eis_seat_ref(seat);
}
2020-08-05 17:12:54 +10:00
static inline void
peck_handle_eis_connect(struct peck *peck, struct eis_event *e)
{
struct eis_client *client = eis_event_get_client(e);
2020-08-05 17:12:54 +10:00
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT)) {
log_debug(peck, "EIS accepting client: '%s'\n", eis_client_get_name(client));
peck->eis_client = eis_client_ref(client);
2020-08-05 17:12:54 +10:00
eis_client_connect(client);
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_DEFAULT_SEAT))
peck_create_eis_seat(peck, client);
2020-08-05 17:12:54 +10:00
} else {
log_debug(peck, "EIS disconnecting client: '%s'\n", eis_client_get_name(client));
2020-08-05 17:12:54 +10:00
eis_client_disconnect(client);
}
}
static inline struct eis_device *
peck_eis_create_pointer(struct peck *peck, struct eis_seat *seat, const char *name)
{
struct eis_device *device = eis_seat_new_device(seat);
eis_device_configure_name(device, name);
eis_device_configure_capability(device, EIS_DEVICE_CAP_POINTER);
eis_device_configure_capability(device, EIS_DEVICE_CAP_BUTTON);
eis_device_configure_capability(device, EIS_DEVICE_CAP_SCROLL);
eis_device_add(device);
return device;
}
static inline struct eis_device *
peck_eis_create_pointer_absolute(struct peck *peck, struct eis_seat *seat, const char *name)
{
struct eis_device *device = eis_seat_new_device(seat);
_unref_(eis_region) *region = eis_device_new_region(device);
eis_region_set_offset(region, 0, 0);
eis_region_set_size(region, 1920, 1080);
eis_device_configure_name(device, name);
eis_device_configure_capability(device, EIS_DEVICE_CAP_POINTER_ABSOLUTE);
eis_device_configure_capability(device, EIS_DEVICE_CAP_BUTTON);
eis_device_configure_capability(device, EIS_DEVICE_CAP_SCROLL);
eis_region_add(region);
eis_device_add(device);
return device;
}
static inline struct eis_device *
peck_eis_create_keyboard(struct peck *peck, struct eis_seat *seat, const char *name)
{
struct eis_device *device = eis_seat_new_device(seat);
eis_device_configure_name(device, name);
eis_device_configure_capability(device, EIS_DEVICE_CAP_KEYBOARD);
eis_device_add(device);
return device;
}
static inline struct eis_device *
peck_eis_create_touch(struct peck *peck, struct eis_seat *seat, const char *name)
{
struct eis_device *device = eis_seat_new_device(seat);
_unref_(eis_region) *region = eis_device_new_region(device);
eis_region_set_offset(region, 0, 0);
eis_region_set_size(region, 1920, 1080);
eis_device_configure_name(device, name);
eis_device_configure_capability(device, EIS_DEVICE_CAP_TOUCH);
eis_region_add(region);
eis_device_add(device);
return device;
}
static inline void
peck_handle_eis_seat_bind(struct peck *peck, struct eis_event *e)
{
struct eis_seat *seat = eis_event_get_seat(e);
log_debug(peck, "EIS binding seat: '%s'\n", eis_seat_get_name(seat));
if (eis_event_seat_has_capability(e, EIS_DEVICE_CAP_POINTER)) {
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_ADD_POINTER) && !peck->eis_pointer) {
log_debug(peck, "EIS creating default pointer\n");
_unref_(eis_device) *ptr = peck_eis_create_pointer(peck, seat, "default pointer");
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_RESUME_DEVICE))
eis_device_resume(ptr);
peck->eis_pointer = eis_device_ref(ptr);
}
} else {
if (peck->eis_pointer) {
log_debug(peck, "EIS removing default pointer\n");
_unref_(eis_device) *ptr = steal(&peck->eis_pointer);
eis_device_remove(ptr);
}
2020-08-05 17:12:54 +10:00
}
if (eis_event_seat_has_capability(e, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) {
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_ADD_POINTER_ABSOLUTE) && !peck->eis_abs) {
log_debug(peck, "EIS creating default abs pointer\n");
_unref_(eis_device) *abs = peck_eis_create_pointer_absolute(peck, seat, "default abs");
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_RESUME_DEVICE))
eis_device_resume(abs);
peck->eis_abs = eis_device_ref(abs);
}
} else {
if (peck->eis_abs) {
log_debug(peck, "EIS removing default abs\n");
_unref_(eis_device) *abs = steal(&peck->eis_abs);
eis_device_remove(abs);
}
}
if (eis_event_seat_has_capability(e, EIS_DEVICE_CAP_KEYBOARD)) {
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_ADD_KEYBOARD) && !peck->eis_keyboard) {
log_debug(peck, "EIS creating default keyboard\n");
_unref_(eis_device) *kbd = peck_eis_create_keyboard(peck, seat, "default keyboard");
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_RESUME_DEVICE))
eis_device_resume(kbd);
peck->eis_keyboard = eis_device_ref(kbd);
}
} else {
if (peck->eis_keyboard) {
2023-04-27 15:13:57 +10:00
log_debug(peck, "EIS removing default keyboard\n");
_unref_(eis_device) *kbd = steal(&peck->eis_keyboard);
eis_device_remove(kbd);
}
}
if (eis_event_seat_has_capability(e, EIS_DEVICE_CAP_TOUCH)) {
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_ADD_TOUCH) && !peck->eis_touch) {
log_debug(peck, "EIS creating default touch\n");
_unref_(eis_device) *touch = peck_eis_create_touch(peck, seat, "default touch");
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_RESUME_DEVICE))
eis_device_resume(touch);
peck->eis_touch = eis_device_ref(touch);
}
} else {
if (peck->eis_touch) {
log_debug(peck, "EIS removing default touch\n");
_unref_(eis_device) *touch = steal(&peck->eis_touch);
eis_device_remove(touch);
}
2020-08-05 17:12:54 +10:00
}
/* Removing all caps means removing the seat */
if (!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_POINTER) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_POINTER_ABSOLUTE) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_KEYBOARD) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_BUTTON) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_SCROLL) &&
!eis_event_seat_has_capability(e, EIS_DEVICE_CAP_TOUCH))
eis_seat_remove(seat);
}
static inline void
peck_eis_device_remove(struct peck *peck, struct eis_device *device)
{
eis_device_remove(device);
if (device == peck->eis_pointer)
peck->eis_pointer = eis_device_unref(device);
if (device == peck->eis_abs)
peck->eis_abs = eis_device_unref(device);
if (device == peck->eis_keyboard)
peck->eis_keyboard = eis_device_unref(device);
if (device == peck->eis_touch)
peck->eis_touch = eis_device_unref(device);
if (device == peck->eis_button)
peck->eis_button = eis_device_unref(device);
if (device == peck->eis_scroll)
peck->eis_scroll = eis_device_unref(device);
}
bool
_peck_dispatch_eis(struct peck *peck, int lineno)
2020-08-05 17:12:54 +10:00
{
struct eis *eis = peck->eis;
bool had_event = false;
static uint64_t last_timestamp;
2020-08-05 17:12:54 +10:00
log_debug(peck, "EIS Dispatch, line %d\n", lineno);
peck_indent(peck);
while (eis) {
2020-08-05 17:12:54 +10:00
eis_dispatch(eis);
tristate process_event = tristate_unset;
_unref_(eis_event) *e = eis_peek_event(eis);
2020-08-05 17:12:54 +10:00
if (!e)
break;
switch (eis_event_get_type(e)) {
case EIS_EVENT_CLIENT_CONNECT:
case EIS_EVENT_CLIENT_DISCONNECT:
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT) ||
flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_REJECT_CLIENT))
process_event = tristate_yes;
break;
case EIS_EVENT_SEAT_BIND:
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_HANDLE_BIND_SEAT))
process_event = tristate_yes;
else
process_event = tristate_no;
break;
case EIS_EVENT_DEVICE_CLOSED:
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_HANDLE_CLOSE_DEVICE))
2020-08-05 17:12:54 +10:00
process_event = tristate_yes;
else
process_event = tristate_no;
2020-08-05 17:12:54 +10:00
break;
proto: add a device ready request The protocol currently supports a ei_device.done event to notify the ei client that the initial description of the device is complete. For some future use-cases the client may need to futher negotiate properties of the device. For example for tablet tools the client may narrow down capabilities of the tool. The sequence with the new request is thus e.g. -> ei_seat.device -> ei_device.name -> ei_device.interface -> ei_device.interface -> ei_device.done <- ei_device.ready -> ei_device.resumed In libei the request is sent automatically on unref of the DEVICE_ADDED event. This makes clients immediately compatible and for the typical (future) use-case of device configuration. Said configuration will likely be handled in response to the DEVICE_ADDED event anyway. In libeis, a new EIS_EVENT_DEVICE_READY event that is sent when the client sends that same event on the protocol, informing the EIS implementation that this device is ready. For clients that do not support that version the event is emulated immediately after sending ei_device.done. This requires a flag bit to be long-term maintainable. The typical EIS implementation currently calls eis_device_add() immediately followed by eis_device_resume(). This doesn't leave any room to wait for the client's ei_device.ready request. One backwards-compatible solution could be to buffer the eis_device_resume() until the ei_device.ready has been received but this is fraught with hairy corner cases, e.g. if the client is a receiver context we would also have to buffer all events immediately sent to the client. So instead, we have a flag in the context and if set by the caller, we change the internal behavior to match ei_device interface version 3. Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/346>
2025-07-11 09:47:50 +10:00
case EIS_EVENT_DEVICE_READY:
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_HANDLE_DEVICE_READY))
process_event = tristate_yes;
else
process_event = tristate_no;
break;
case EIS_EVENT_PONG:
break;
2024-12-10 12:36:30 +10:00
case EIS_EVENT_SYNC:
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_HANDLE_SYNC))
process_event = tristate_yes;
break;
case EIS_EVENT_FRAME:
/* Ensure we only expect frames when we expect them */
munit_assert_true(peck->eis_needs_frame);
peck->eis_needs_frame = false;
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_HANDLE_FRAME))
process_event = tristate_yes;
break;
case EIS_EVENT_DEVICE_START_EMULATING:
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_HANDLE_START_EMULATING))
process_event = tristate_yes;
break;
case EIS_EVENT_DEVICE_STOP_EMULATING:
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_HANDLE_STOP_EMULATING))
process_event = tristate_yes;
break;
case EIS_EVENT_POINTER_MOTION:
case EIS_EVENT_POINTER_MOTION_ABSOLUTE:
case EIS_EVENT_BUTTON_BUTTON:
case EIS_EVENT_SCROLL_DELTA:
case EIS_EVENT_SCROLL_STOP:
case EIS_EVENT_SCROLL_CANCEL:
case EIS_EVENT_SCROLL_DISCRETE:
case EIS_EVENT_KEYBOARD_KEY:
case EIS_EVENT_TOUCH_DOWN:
case EIS_EVENT_TOUCH_UP:
case EIS_EVENT_TOUCH_MOTION:
peck->eis_needs_frame = true;
2020-08-05 17:12:54 +10:00
break;
}
log_debug(peck, "EIS received %s.... %s\n",
peck_eis_event_name(e),
tristate_is_yes(process_event) ?
"handling ourselves" : "punting to caller");
if (!tristate_is_yes(process_event))
2020-08-05 17:12:54 +10:00
break;
had_event = true;
2020-08-05 17:12:54 +10:00
/* manual unref, _cleanup_ will take care of the real event */
eis_event_unref(e);
e = eis_get_event(eis);
switch (eis_event_get_type(e)) {
case EIS_EVENT_CLIENT_CONNECT:
peck_handle_eis_connect(peck, e);
break;
case EIS_EVENT_CLIENT_DISCONNECT:
log_debug(peck, "EIS disconnecting client: %s\n",
2020-08-05 17:12:54 +10:00
eis_client_get_name(eis_event_get_client(e)));
eis_client_disconnect(eis_event_get_client(e));
break;
case EIS_EVENT_SEAT_BIND:
peck_handle_eis_seat_bind(peck, e);
2020-08-05 17:12:54 +10:00
break;
case EIS_EVENT_DEVICE_CLOSED:
peck_eis_device_remove(peck, eis_event_get_device(e));
break;
case EIS_EVENT_FRAME: {
uint64_t timestamp = eis_event_get_time(e);
uint64_t ts_now = 0;
munit_assert_int(now(&ts_now), ==, 0);
munit_assert_int64(last_timestamp, <, timestamp);
munit_assert_int64(last_timestamp, <=, ts_now);
last_timestamp = timestamp;
break;
}
2020-08-05 17:12:54 +10:00
default:
break;
}
}
peck_dedent(peck);
return had_event;
2020-08-05 17:12:54 +10:00
}
static inline tristate
peck_check_ei_added(struct peck *peck, struct ei_event *e)
{
struct ei_device *device = ei_event_get_device(e);
if (ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) &&
flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER))
return tristate_yes;
if (ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE) &&
flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_ADDED_POINTER_ABSOLUTE))
return tristate_yes;
2020-08-05 17:12:54 +10:00
if (ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD) &&
flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_ADDED_KEYBOARD))
return tristate_yes;
if (ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH) &&
2020-08-05 17:12:54 +10:00
flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_ADDED_TOUCH))
return tristate_yes;
if (ei_device_has_capability(device, EI_DEVICE_CAP_BUTTON) &&
flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_ADDED_BUTTON))
return tristate_yes;
if (ei_device_has_capability(device, EI_DEVICE_CAP_SCROLL) &&
flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_ADDED_SCROLL))
return tristate_yes;
2020-08-05 17:12:54 +10:00
return tristate_unset;
}
bool
_peck_dispatch_ei(struct peck *peck, int lineno)
2020-08-05 17:12:54 +10:00
{
struct ei *ei = peck->ei;
bool had_event = false;
2020-08-05 17:12:54 +10:00
log_debug(peck, "ei dispatch, line %d\n", lineno);
peck_indent(peck);
while (ei) {
2020-08-05 17:12:54 +10:00
ei_dispatch(ei);
tristate process_event = tristate_no;
_unref_(ei_event) *e = ei_peek_event(ei);
2020-08-05 17:12:54 +10:00
if (!e)
break;
switch (ei_event_get_type(e)) {
case EI_EVENT_CONNECT:
if (flag_is_set(peck->ei_behavior,
PECK_EI_BEHAVIOR_HANDLE_CONNECT))
2020-08-05 17:12:54 +10:00
process_event = tristate_yes;
break;
case EI_EVENT_DISCONNECT:
break;
case EI_EVENT_SEAT_ADDED:
if (peck->ei_seat == NULL &&
flag_is_set(peck->ei_behavior,
PECK_EI_BEHAVIOR_AUTOSEAT))
process_event = tristate_yes;
break;
case EI_EVENT_SEAT_REMOVED:
break;
2020-08-05 17:12:54 +10:00
case EI_EVENT_DEVICE_ADDED:
process_event = peck_check_ei_added(peck, e);
break;
case EI_EVENT_DEVICE_REMOVED:
break;
case EI_EVENT_DEVICE_RESUMED:
if (flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_RESUMED))
process_event = tristate_yes;
break;
case EI_EVENT_DEVICE_PAUSED:
if (flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_PAUSED))
process_event = tristate_yes;
break;
case EI_EVENT_PONG:
break;
case EI_EVENT_SYNC:
if (flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_SYNC))
process_event = tristate_yes;
break;
case EI_EVENT_FRAME:
/* Ensure we only expect frames when we expect them */
munit_assert_true(peck->ei_needs_frame);
peck->ei_needs_frame = false;
if (flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_FRAME))
process_event = tristate_yes;
break;
case EI_EVENT_DEVICE_START_EMULATING:
case EI_EVENT_DEVICE_STOP_EMULATING:
break;
case EI_EVENT_POINTER_MOTION:
case EI_EVENT_POINTER_MOTION_ABSOLUTE:
case EI_EVENT_BUTTON_BUTTON:
case EI_EVENT_SCROLL_DELTA:
case EI_EVENT_SCROLL_STOP:
case EI_EVENT_SCROLL_CANCEL:
case EI_EVENT_SCROLL_DISCRETE:
case EI_EVENT_KEYBOARD_KEY:
case EI_EVENT_KEYBOARD_MODIFIERS:
case EI_EVENT_TOUCH_DOWN:
case EI_EVENT_TOUCH_UP:
case EI_EVENT_TOUCH_MOTION:
peck->ei_needs_frame = true;
2020-08-05 17:12:54 +10:00
break;
}
log_debug(peck, "ei event %s.... %s\n",
peck_ei_event_name(e),
tristate_is_yes(process_event) ?
"handling ourselves" : "punting to caller");
2020-08-05 17:12:54 +10:00
if (!tristate_is_yes(process_event))
break;
had_event = true;
2020-08-05 17:12:54 +10:00
/* manual unref, _cleanup_ will take care of the real event */
ei_event_unref(e);
e = ei_get_event(ei);
switch (ei_event_get_type(e)) {
case EI_EVENT_CONNECT:
log_debug(peck, "ei is connected\n");
2020-08-05 17:12:54 +10:00
/* Nothing to do here */
break;
case EI_EVENT_SEAT_ADDED:
{
struct ei_seat *seat = ei_event_get_seat(e);
munit_assert_ptr_null(peck->ei_seat);
peck->ei_seat = ei_seat_ref(seat);
log_debug(peck, "default seat: %s\n", ei_seat_get_name(peck->ei_seat));
ei_seat_bind_capabilities(seat,
EI_DEVICE_CAP_POINTER,
EI_DEVICE_CAP_POINTER_ABSOLUTE,
EI_DEVICE_CAP_KEYBOARD,
EI_DEVICE_CAP_TOUCH,
EI_DEVICE_CAP_BUTTON,
EI_DEVICE_CAP_SCROLL, NULL);
break;
}
2020-08-05 17:12:54 +10:00
case EI_EVENT_DEVICE_ADDED:
{
struct ei_device *device = ei_event_get_device(e);
if (!peck->ei_pointer && ei_device_has_capability(device, EI_DEVICE_CAP_POINTER))
peck->ei_pointer = ei_device_ref(device);
if (!peck->ei_abs && ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE))
peck->ei_abs = ei_device_ref(device);
if (!peck->ei_keyboard && ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD))
peck->ei_keyboard = ei_device_ref(device);
if (!peck->ei_touch && ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH))
peck->ei_touch = ei_device_ref(device);
if (!peck->ei_button && ei_device_has_capability(device, EI_DEVICE_CAP_BUTTON))
peck->ei_button = ei_device_ref(device);
if (!peck->ei_scroll && ei_device_has_capability(device, EI_DEVICE_CAP_SCROLL))
peck->ei_scroll = ei_device_ref(device);
break;
}
case EI_EVENT_DEVICE_RESUMED:
if (flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_AUTOSTART)) {
static uint32_t sequence;
struct ei_device *device = ei_event_get_device(e);
if (ei_is_sender(ei_device_get_context(device)))
ei_device_start_emulating(device, ++sequence);
}
break;
case EI_EVENT_DEVICE_PAUSED:
2020-08-05 17:12:54 +10:00
/* Nothing to do here */
break;
default:
break;
}
}
peck_dedent(peck);
return had_event;
}
void
_peck_dispatch_until_stable(struct peck *peck, const char *func, int lineno)
{
int eis = 0, ei = 0;
int loop_counter = 0;
log_debug(peck, "dispatching until stable (%s:%d) {\n", func, lineno);
peck_indent(peck);
/* we go two dispatch loops for both ei and EIS before we say it's
* stable. With the DEVICE_REGION/KEYMAP/DONE event split we may
* get events on the wire that don't result in an actual event
* yet, so we can get stuck if we just wait for one.
*/
do {
log_debug(peck, "entering dispatch loop %d {\n", loop_counter++);
peck_indent(peck);
eis = _peck_dispatch_eis(peck, lineno) ? 0 : eis + 1;
ei = _peck_dispatch_ei(peck, lineno) ? 0 : ei + 1;
peck_dedent(peck);
log_debug(peck, "} done: ei %d|eis %d\n", ei, eis);
} while (ei <= 2 || eis <= 2);
peck_dedent(peck);
log_debug(peck, "} stable (%s:%d)\n", func, lineno);
2020-08-05 17:12:54 +10:00
}
void
peck_drain_eis(struct peck *peck)
2020-08-05 17:12:54 +10:00
{
struct eis *eis = peck->eis;
if (!eis)
return;
2020-08-05 17:12:54 +10:00
eis_dispatch(eis);
while (true) {
_unref_(eis_event) *e = eis_get_event(eis);
2020-08-05 17:12:54 +10:00
if (e == NULL)
break;
}
peck->eis_needs_frame = false;
2020-08-05 17:12:54 +10:00
}
void
peck_drain_ei(struct peck *peck)
2020-08-05 17:12:54 +10:00
{
struct ei *ei = peck->ei;
if (!ei)
return;
2020-08-05 17:12:54 +10:00
ei_dispatch(ei);
while (true) {
_unref_(ei_event) *e = ei_get_event(ei);
2020-08-05 17:12:54 +10:00
if (e == NULL)
break;
}
peck->ei_needs_frame = false;
2020-08-05 17:12:54 +10:00
}
void
_peck_assert_no_ei_events(struct ei *ei, int lineno)
{
struct peck *peck = ei_get_user_data(ei);
ei_dispatch(ei);
while (true) {
_unref_(ei_event) *e = ei_get_event(ei);
if (!e)
return;
if (peck && flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_FRAME) &&
ei_event_get_type(e) == EI_EVENT_FRAME) {
log_debug(peck, "Skipping over frame event\n");
continue;
}
if (peck && flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_SYNC) &&
ei_event_get_type(e) == EI_EVENT_SYNC) {
log_debug(peck, "Skipping over sync event\n");
continue;
}
munit_errorf("Expected empty event queue, have: %s, line %d\n",
peck_ei_event_name(e), lineno);
}
}
void
_peck_assert_no_eis_events(struct eis *eis, int lineno)
{
struct peck *peck = eis_get_user_data(eis);
eis_dispatch(eis);
while (true) {
_unref_(eis_event) *e = eis_get_event(eis);
if (!e)
return;
2024-12-10 12:36:30 +10:00
if (peck) {
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_HANDLE_FRAME) &&
eis_event_get_type(e) == EIS_EVENT_FRAME) {
log_debug(peck, "Skipping over frame event\n");
continue;
}
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_HANDLE_SYNC) &&
eis_event_get_type(e) == EIS_EVENT_SYNC) {
log_debug(peck, "Skipping over sync event\n");
continue;
}
}
munit_errorf("Expected empty event queue, have: %s, line %d\n",
peck_eis_event_name(e), lineno);
}
}
struct ei_event *
_peck_ei_next_event(struct ei *ei, enum ei_event_type type, int lineno)
{
struct ei_event *event = ei_get_event(ei);
struct peck *peck = ei_get_user_data(ei);
if (flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_FRAME)) {
while (event && ei_event_get_type(event) == EI_EVENT_FRAME) {
ei_event_unref(event);
log_debug(peck, "Skipping over frame event\n");
event = ei_get_event(ei);
}
}
if (flag_is_set(peck->ei_behavior, PECK_EI_BEHAVIOR_HANDLE_SYNC)) {
while (event && ei_event_get_type(event) == EI_EVENT_SYNC) {
ei_event_unref(event);
log_debug(peck, "Skipping over sync event\n");
event = ei_get_event(ei);
}
}
if (!event)
munit_errorf("Expected ei event type %s, got none, line %d\n",
peck_ei_event_type_name(type),
lineno);
if (!streq(peck_ei_event_name(event),
peck_ei_event_type_name(type)))
munit_errorf("Expected ei event type %s, got %s, line %d\n",
peck_ei_event_type_name(type),
peck_ei_event_name(event),
lineno);
log_debug(peck, "ei passing %s to caller, line %d\n", peck_ei_event_name(event), lineno);
return event;
}
struct eis_event *
_peck_eis_next_event(struct eis *eis, enum eis_event_type type, int lineno)
{
struct eis_event *event = eis_get_event(eis);
struct peck *peck = eis_get_user_data(eis);
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_HANDLE_FRAME)) {
while (event && eis_event_get_type(event) == EIS_EVENT_FRAME) {
eis_event_unref(event);
log_debug(peck, "Skipping over frame event\n");
event = eis_get_event(eis);
}
}
2024-12-10 12:36:30 +10:00
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_HANDLE_SYNC)) {
while (event && eis_event_get_type(event) == EIS_EVENT_SYNC) {
eis_event_unref(event);
log_debug(peck, "Skipping over sync event\n");
event = eis_get_event(eis);
}
}
if (!event)
munit_errorf("Expected EIS event type %s, got none, line %d\n",
peck_eis_event_type_name(type),
lineno);
if (eis_event_get_type(event) != type) {
munit_errorf("Expected EIS event type %s, got %s, line %d\n",
peck_eis_event_type_name(type),
peck_eis_event_type_name(eis_event_get_type(event)),
lineno);
}
log_debug(peck, "EIS passing %s to caller, line %d\n", peck_eis_event_name(event), lineno);
return event;
}
struct eis_event *
_peck_eis_touch_event(struct eis *eis, enum eis_event_type type, double x, double y, int lineno)
{
assert(type == EIS_EVENT_TOUCH_DOWN || type == EIS_EVENT_TOUCH_MOTION);
struct eis_event *event = _peck_eis_next_event(eis, type, lineno);
double ex = eis_event_touch_get_x(event);
double ey = eis_event_touch_get_y(event);
if (fabs(ex - x) > 1e-3 || fabs(ey - y) > 1e-3) {
munit_errorf("Touch coordinate mismatch: have (%.2f/%.2f) need (%.2f/%.2f)\n",
ex, ey, x, y);
}
return event;
}
struct ei_event *
_peck_ei_touch_event(struct ei *ei, enum ei_event_type type, double x, double y, int lineno)
{
assert(type == EI_EVENT_TOUCH_DOWN || type == EI_EVENT_TOUCH_MOTION);
struct ei_event *event = _peck_ei_next_event(ei, type, lineno);
double ex = ei_event_touch_get_x(event);
double ey = ei_event_touch_get_y(event);
if (fabs(ex - x) > 1e-3 || fabs(ey - y) > 1e-3) {
munit_errorf("Touch coordinate mismatch: have (%.2f/%.2f) need (%.2f/%.2f)\n",
ex, ey, x, y);
}
return event;
}
const char *
peck_ei_event_name(struct ei_event *e)
{
return peck_ei_event_type_name(ei_event_get_type(e));
}
const char *
peck_ei_event_type_name(enum ei_event_type type)
{
#define CASE_STRING(_name) \
case EI_EVENT_##_name: return #_name
switch (type) {
CASE_STRING(CONNECT);
CASE_STRING(DISCONNECT);
CASE_STRING(SEAT_ADDED);
CASE_STRING(SEAT_REMOVED);
CASE_STRING(DEVICE_ADDED);
CASE_STRING(DEVICE_REMOVED);
CASE_STRING(DEVICE_PAUSED);
CASE_STRING(DEVICE_RESUMED);
CASE_STRING(KEYBOARD_MODIFIERS);
CASE_STRING(PONG);
CASE_STRING(SYNC);
CASE_STRING(FRAME);
CASE_STRING(DEVICE_START_EMULATING);
CASE_STRING(DEVICE_STOP_EMULATING);
CASE_STRING(POINTER_MOTION);
CASE_STRING(POINTER_MOTION_ABSOLUTE);
CASE_STRING(BUTTON_BUTTON);
CASE_STRING(SCROLL_DELTA);
CASE_STRING(SCROLL_STOP);
CASE_STRING(SCROLL_CANCEL);
CASE_STRING(SCROLL_DISCRETE);
CASE_STRING(KEYBOARD_KEY);
CASE_STRING(TOUCH_DOWN);
CASE_STRING(TOUCH_UP);
CASE_STRING(TOUCH_MOTION);
}
#undef CASE_STRING
assert(!"Unhandled ei event type");
}
const char *
peck_eis_event_name(struct eis_event *e)
{
return peck_eis_event_type_name(eis_event_get_type(e));
}
const char *
peck_eis_event_type_name(enum eis_event_type type)
{
#define CASE_STRING(_name) \
case EIS_EVENT_##_name: return #_name
switch (type) {
CASE_STRING(CLIENT_CONNECT);
CASE_STRING(CLIENT_DISCONNECT);
CASE_STRING(SEAT_BIND);
CASE_STRING(DEVICE_CLOSED);
CASE_STRING(PONG);
2024-12-10 12:36:30 +10:00
CASE_STRING(SYNC);
CASE_STRING(DEVICE_START_EMULATING);
CASE_STRING(DEVICE_STOP_EMULATING);
proto: add a device ready request The protocol currently supports a ei_device.done event to notify the ei client that the initial description of the device is complete. For some future use-cases the client may need to futher negotiate properties of the device. For example for tablet tools the client may narrow down capabilities of the tool. The sequence with the new request is thus e.g. -> ei_seat.device -> ei_device.name -> ei_device.interface -> ei_device.interface -> ei_device.done <- ei_device.ready -> ei_device.resumed In libei the request is sent automatically on unref of the DEVICE_ADDED event. This makes clients immediately compatible and for the typical (future) use-case of device configuration. Said configuration will likely be handled in response to the DEVICE_ADDED event anyway. In libeis, a new EIS_EVENT_DEVICE_READY event that is sent when the client sends that same event on the protocol, informing the EIS implementation that this device is ready. For clients that do not support that version the event is emulated immediately after sending ei_device.done. This requires a flag bit to be long-term maintainable. The typical EIS implementation currently calls eis_device_add() immediately followed by eis_device_resume(). This doesn't leave any room to wait for the client's ei_device.ready request. One backwards-compatible solution could be to buffer the eis_device_resume() until the ei_device.ready has been received but this is fraught with hairy corner cases, e.g. if the client is a receiver context we would also have to buffer all events immediately sent to the client. So instead, we have a flag in the context and if set by the caller, we change the internal behavior to match ei_device interface version 3. Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/346>
2025-07-11 09:47:50 +10:00
CASE_STRING(DEVICE_READY);
CASE_STRING(POINTER_MOTION);
CASE_STRING(POINTER_MOTION_ABSOLUTE);
CASE_STRING(BUTTON_BUTTON);
CASE_STRING(SCROLL_DELTA);
CASE_STRING(SCROLL_STOP);
CASE_STRING(SCROLL_CANCEL);
CASE_STRING(SCROLL_DISCRETE);
CASE_STRING(KEYBOARD_KEY);
CASE_STRING(TOUCH_DOWN);
CASE_STRING(TOUCH_UP);
CASE_STRING(TOUCH_MOTION);
CASE_STRING(FRAME);
}
#undef CASE_STRING
assert(!"Unhandled EIS event type");
}
void
_peck_mark(struct peck *peck, const char *func, int line)
{
static uint32_t mark = 0;
log_debug(peck, "mark %3d: line %d in %s()\n", mark++, line, func);
}
void _peck_debug(struct peck *peck, const char *func, int line, const char *format, ...)
{
va_list args;
va_start(args, format);
log_msg_va(peck->logger, LOGGER_DEBUG, "unused", line, func, format, args);
va_end(args);
}
MUNIT_GLOBAL_SETUP(init_sigalarm)
{
int argc = setup->argc;
char **argv = setup->argv;
for (int i = 0; i < argc; i++) {
if (streq(argv[i], "--enable-sigalrm") || streq(argv[i], "--enable-sigalarm")) {
enable_sigalarm = true;
for (int j = i + 1; j < setup->argc; j++) {
setup->argv[i] = setup->argv[j];
}
setup->argc--;
break;
}
}
}