libei/test/eierpecken.c
Peter Hutterer 756f74ec73 test: declare the need_frame check as part of the context
Reproducible with something that produces a frame event:

   // queues e.g. pointer motion + frame
   peck_eis_dispatch_until_stable()
   with_server(peck) {
       // process the motion only
   }
   peck_eis_dispatch_until_stable()

The second peck_eis_dispatch_until_stable() triggers an assertion
because we still have the unhandled frame event pending but need_frame
was reset to false.

Keep this as a field in peck so it remembers across invocations.

Part-of: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/369>
2025-12-15 05:29:54 +00:00

1743 lines
48 KiB
C

/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2020 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <math.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <munit.h>
#include "eierpecken.h"
#include "util-bits.h"
#include "util-color.h"
#include "util-io.h"
#include "util-mem.h"
#include "util-munit.h"
#include "util-tristate.h"
#include "util-logger.h"
#include "util-object.h"
#include "util-strings.h"
#include "util-time.h"
static bool enable_sigalarm;
DEFINE_TRISTATE(yes, no, unset);
struct peck_log_capture {
bool enabled;
char **debug;
char **info;
char **warning;
char **error;
};
struct peck {
struct object object;
struct ei *ei;
int ei_fd;
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;
};
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;
}
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);
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);
}
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
OBJECT_IMPLEMENT_CREATE(peck);
OBJECT_IMPLEMENT_UNREF(peck);
OBJECT_IMPLEMENT_GETTER(peck, ei, struct ei*);
OBJECT_IMPLEMENT_GETTER(peck, eis, struct eis*);
OBJECT_IMPLEMENT_GETTER(peck, ei_fd, int);
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)
{
struct peck *peck = peck_create(NULL);
enum peck_ei_mode ei_mode = PECK_EI_SENDER;
uint32_t eis_flags = 0;
while (what) {
if (streq(what, "mode"))
ei_mode = va_arg(args, enum peck_ei_mode);
if (streq(what, "eis-flags"))
eis_flags = va_arg(args, uint32_t);
what = va_arg(args, const char *);
}
assert(ei_mode == PECK_EI_RECEIVER || ei_mode == PECK_EI_SENDER);
int rc, fd;
struct eis *eis = eis_new(peck);
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);
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);
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_HANDLE_SYNC);
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);
ei_configure_name(ei, "eierpecken test context");
rc = ei_setup_backend_fd(ei, fd);
munit_assert_int(rc, ==, 0);
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);
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);
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);
}
}
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
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;
case PECK_EIS_BEHAVIOR_ACCEPT_ALL:
peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_DEFAULT_SEAT);
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);
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);
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);
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:
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);
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;
}
}
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:
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;
}
}
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:
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;
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);
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);
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_set(peck->ei_behavior, behavior);
break;
}
}
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);
}
static inline void
peck_handle_eis_connect(struct peck *peck, struct eis_event *e)
{
struct eis_client *client = eis_event_get_client(e);
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);
eis_client_connect(client);
if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_DEFAULT_SEAT))
peck_create_eis_seat(peck, client);
} else {
log_debug(peck, "EIS disconnecting client: '%s'\n", eis_client_get_name(client));
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);
}
}
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) {
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);
}
}
/* 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)
{
struct eis *eis = peck->eis;
bool had_event = false;
static uint64_t last_timestamp;
log_debug(peck, "EIS Dispatch, line %d\n", lineno);
peck_indent(peck);
while (eis) {
eis_dispatch(eis);
tristate process_event = tristate_unset;
_unref_(eis_event) *e = eis_peek_event(eis);
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))
process_event = tristate_yes;
else
process_event = tristate_no;
break;
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;
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;
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))
break;
had_event = true;
/* 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",
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);
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;
}
default:
break;
}
}
peck_dedent(peck);
return had_event;
}
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;
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) &&
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;
return tristate_unset;
}
bool
_peck_dispatch_ei(struct peck *peck, int lineno)
{
struct ei *ei = peck->ei;
bool had_event = false;
log_debug(peck, "ei dispatch, line %d\n", lineno);
peck_indent(peck);
while (ei) {
ei_dispatch(ei);
tristate process_event = tristate_no;
_unref_(ei_event) *e = ei_peek_event(ei);
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))
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;
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;
break;
}
log_debug(peck, "ei event %s.... %s\n",
peck_ei_event_name(e),
tristate_is_yes(process_event) ?
"handling ourselves" : "punting to caller");
if (!tristate_is_yes(process_event))
break;
had_event = true;
/* 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");
/* 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;
}
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:
/* 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);
}
void
peck_drain_eis(struct peck *peck)
{
struct eis *eis = peck->eis;
if (!eis)
return;
eis_dispatch(eis);
while (true) {
_unref_(eis_event) *e = eis_get_event(eis);
if (e == NULL)
break;
}
peck->eis_needs_frame = false;
}
void
peck_drain_ei(struct peck *peck)
{
struct ei *ei = peck->ei;
if (!ei)
return;
ei_dispatch(ei);
while (true) {
_unref_(ei_event) *e = ei_get_event(ei);
if (e == NULL)
break;
}
peck->ei_needs_frame = false;
}
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;
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);
}
}
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);
CASE_STRING(SYNC);
CASE_STRING(DEVICE_START_EMULATING);
CASE_STRING(DEVICE_STOP_EMULATING);
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;
}
}
}