mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-10 09:11:31 +00:00
[client] input: add display server behaviour tests
Extract input policy from the Wayland and X11 protocol handlers. Exercise both implementations against literal Wayland event traces.
This commit is contained in:
@@ -19,6 +19,7 @@ add_library(displayserver_Wayland STATIC
|
||||
icon.c
|
||||
idle.c
|
||||
input.c
|
||||
input_event.c
|
||||
output.c
|
||||
poll.c
|
||||
presentation.c
|
||||
|
||||
@@ -240,7 +240,7 @@ void waylandSetPointer(LG_DSPointer pointer)
|
||||
wlWm.cursorHotY = wlWm.cursorHot[pointer].y;
|
||||
MTRACE("set id=%d surface=%p hot=%d,%d scale=%d entered=%d",
|
||||
pointer, (void *)wlWm.cursor, wlWm.cursorHotX, wlWm.cursorHotY,
|
||||
wlWm.cursorScale, wlWm.pointerInSurface);
|
||||
wlWm.cursorScale, wlWm.input.pointerInSurface);
|
||||
if (wlWm.pointer)
|
||||
wl_pointer_set_cursor(wlWm.pointer, wlWm.pointerEnterSerial, wlWm.cursor, wlWm.cursorHotX, wlWm.cursorHotY);
|
||||
}
|
||||
|
||||
@@ -32,9 +32,6 @@
|
||||
#include "app.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
const double WL_SCROLL_STEP = 15.0;
|
||||
const double WL_HALF_SCROLL_STEP = WL_SCROLL_STEP / 2.0;
|
||||
|
||||
#define MTRACE(fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
@@ -59,6 +56,93 @@ static uint32_t proxyId(void * proxy)
|
||||
return proxy ? wl_proxy_get_id(proxy) : 0;
|
||||
}
|
||||
|
||||
static void inputPosition(void * opaque, double x, double y)
|
||||
{
|
||||
(void)opaque;
|
||||
app_updateCursorPos(x, y);
|
||||
}
|
||||
|
||||
static void inputRelative(void * opaque, double x, double y,
|
||||
double rawX, double rawY)
|
||||
{
|
||||
(void)opaque;
|
||||
app_handleMouseRelative(x, y, rawX, rawY);
|
||||
}
|
||||
|
||||
static void inputButton(void * opaque, unsigned int button, bool pressed)
|
||||
{
|
||||
(void)opaque;
|
||||
if (pressed)
|
||||
app_handleButtonPress(button);
|
||||
else
|
||||
app_handleButtonRelease(button);
|
||||
}
|
||||
|
||||
static void inputWheel(void * opaque, double motion)
|
||||
{
|
||||
(void)opaque;
|
||||
app_handleWheelMotion(motion);
|
||||
}
|
||||
|
||||
static void inputKey(void * opaque, int scancode, bool pressed)
|
||||
{
|
||||
(void)opaque;
|
||||
if (pressed)
|
||||
app_handleKeyPress(scancode);
|
||||
else
|
||||
app_handleKeyRelease(scancode);
|
||||
}
|
||||
|
||||
static void inputText(void * opaque, const char * text)
|
||||
{
|
||||
(void)opaque;
|
||||
/* Test overlay state after the key callback, matching the native event
|
||||
* handler's ordering. */
|
||||
if (app_isOverlayMode())
|
||||
app_handleKeyboardTyped(text);
|
||||
}
|
||||
|
||||
static void inputModifiers(void * opaque, bool ctrl, bool shift, bool alt,
|
||||
bool super)
|
||||
{
|
||||
(void)opaque;
|
||||
app_handleKeyboardModifiers(ctrl, shift, alt, super);
|
||||
}
|
||||
|
||||
static void inputLEDs(void * opaque, bool numLock, bool capsLock,
|
||||
bool scrollLock)
|
||||
{
|
||||
(void)opaque;
|
||||
app_handleKeyboardLEDs(numLock, capsLock, scrollLock);
|
||||
}
|
||||
|
||||
static void inputEnter(void * opaque, bool entered)
|
||||
{
|
||||
(void)opaque;
|
||||
app_handleEnterEvent(entered);
|
||||
}
|
||||
|
||||
static void inputFocus(void * opaque, bool focused)
|
||||
{
|
||||
(void)opaque;
|
||||
if (!focused)
|
||||
waylandCBInvalidate();
|
||||
app_handleFocusEvent(focused);
|
||||
}
|
||||
|
||||
static const LG_DSInputSink inputSink = {
|
||||
.position = inputPosition,
|
||||
.relative = inputRelative,
|
||||
.button = inputButton,
|
||||
.wheel = inputWheel,
|
||||
.key = inputKey,
|
||||
.text = inputText,
|
||||
.modifiers = inputModifiers,
|
||||
.leds = inputLEDs,
|
||||
.enter = inputEnter,
|
||||
.focus = inputFocus,
|
||||
};
|
||||
|
||||
// Mouse-handling listeners.
|
||||
|
||||
static void pointerMotionHandler(void * data, struct wl_pointer * pointer,
|
||||
@@ -68,7 +152,7 @@ static void pointerMotionHandler(void * data, struct wl_pointer * pointer,
|
||||
wl_fixed_to_double(syW));
|
||||
MTRACE("abs time=%u pos=%.3f,%.3f", time, wlWm.motion.x,
|
||||
wlWm.motion.y);
|
||||
app_updateCursorPos(wlWm.motion.x, wlWm.motion.y);
|
||||
wlInputPointerMotion(&wlWm.input, wlWm.motion.x, wlWm.motion.y);
|
||||
}
|
||||
|
||||
static void pointerEnterHandler(void * data, struct wl_pointer * pointer,
|
||||
@@ -79,18 +163,15 @@ static void pointerEnterHandler(void * data, struct wl_pointer * pointer,
|
||||
surface == wlWm.surface, (void *)surface, serial,
|
||||
wl_fixed_to_double(sxW), wl_fixed_to_double(syW));
|
||||
|
||||
if (surface != wlWm.surface)
|
||||
if (!wlInputPointerEnter(&wlWm.input, surface == wlWm.surface))
|
||||
return;
|
||||
|
||||
wlWm.pointerInSurface = true;
|
||||
app_handleEnterEvent(true);
|
||||
|
||||
wl_pointer_set_cursor(pointer, serial, wlWm.cursor, wlWm.cursorHotX, wlWm.cursorHotY);
|
||||
wlWm.pointerEnterSerial = serial;
|
||||
|
||||
wlMotionAbs(&wlWm.motion, wl_fixed_to_double(sxW),
|
||||
wl_fixed_to_double(syW));
|
||||
app_updateCursorPos(wlWm.motion.x, wlWm.motion.y);
|
||||
wlInputPointerMotion(&wlWm.input, wlWm.motion.x, wlWm.motion.y);
|
||||
}
|
||||
|
||||
static void pointerLeaveHandler(void * data, struct wl_pointer * pointer,
|
||||
@@ -99,76 +180,21 @@ static void pointerLeaveHandler(void * data, struct wl_pointer * pointer,
|
||||
MTRACE("leave main=%d surface=%p serial=%u",
|
||||
surface == wlWm.surface, (void *)surface, serial);
|
||||
|
||||
if (surface != wlWm.surface)
|
||||
return;
|
||||
|
||||
wlWm.pointerInSurface = false;
|
||||
app_handleEnterEvent(false);
|
||||
wlInputPointerLeave(&wlWm.input, surface == wlWm.surface);
|
||||
}
|
||||
|
||||
static void pointerAxisHandler(void * data, struct wl_pointer * pointer,
|
||||
uint32_t serial, uint32_t axis, wl_fixed_t value)
|
||||
{
|
||||
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
|
||||
return;
|
||||
|
||||
double delta = wl_fixed_to_double(value);
|
||||
|
||||
wlWm.scrollState += delta;
|
||||
|
||||
while (wlWm.scrollState > WL_HALF_SCROLL_STEP)
|
||||
{
|
||||
app_handleButtonPress(5 /* SPICE_MOUSE_BUTTON_DOWN */);
|
||||
app_handleButtonRelease(5 /* SPICE_MOUSE_BUTTON_DOWN */);
|
||||
wlWm.scrollState -= WL_SCROLL_STEP;
|
||||
}
|
||||
|
||||
while (wlWm.scrollState < -WL_HALF_SCROLL_STEP)
|
||||
{
|
||||
app_handleButtonPress(4 /* SPICE_MOUSE_BUTTON_UP */);
|
||||
app_handleButtonRelease(4 /* SPICE_MOUSE_BUTTON_UP */);
|
||||
wlWm.scrollState += WL_SCROLL_STEP;
|
||||
}
|
||||
|
||||
app_handleWheelMotion(delta / WL_SCROLL_STEP);
|
||||
}
|
||||
|
||||
static int mapWaylandButton(uint32_t button)
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case BTN_LEFT:
|
||||
return 1;
|
||||
case BTN_MIDDLE:
|
||||
return 2;
|
||||
case BTN_RIGHT:
|
||||
return 3;
|
||||
case BTN_SIDE:
|
||||
return 6;
|
||||
case BTN_EXTRA:
|
||||
return 7;
|
||||
case BTN_FORWARD:
|
||||
return 8;
|
||||
case BTN_BACK:
|
||||
return 9;
|
||||
case BTN_TASK:
|
||||
return 10;
|
||||
}
|
||||
|
||||
return 0;
|
||||
wlInputPointerAxis(&wlWm.input,
|
||||
axis == WL_POINTER_AXIS_VERTICAL_SCROLL, wl_fixed_to_double(value));
|
||||
}
|
||||
|
||||
static void pointerButtonHandler(void *data, struct wl_pointer *pointer,
|
||||
uint32_t serial, uint32_t time, uint32_t button, uint32_t stateW)
|
||||
{
|
||||
button = mapWaylandButton(button);
|
||||
if (!button)
|
||||
return;
|
||||
|
||||
if (stateW == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
app_handleButtonPress(button);
|
||||
else
|
||||
app_handleButtonRelease(button);
|
||||
wlInputPointerButton(&wlWm.input, button,
|
||||
stateW == WL_POINTER_BUTTON_STATE_PRESSED);
|
||||
}
|
||||
|
||||
static const struct wl_pointer_listener pointerListener = {
|
||||
@@ -223,21 +249,18 @@ static void relativePointerMotionHandler(void * data,
|
||||
wl_fixed_t dxW, wl_fixed_t dyW, wl_fixed_t dxUnaccelW,
|
||||
wl_fixed_t dyUnaccelW)
|
||||
{
|
||||
if (!atomic_load_explicit(&wlWm.lockActive, memory_order_acquire))
|
||||
return;
|
||||
|
||||
const bool active =
|
||||
atomic_load_explicit(&wlWm.lockActive, memory_order_acquire);
|
||||
const double dx = wl_fixed_to_double(dxW);
|
||||
const double dy = wl_fixed_to_double(dyW);
|
||||
const double rawX = wl_fixed_to_double(dxUnaccelW);
|
||||
const double rawY = wl_fixed_to_double(dyUnaccelW);
|
||||
if (active)
|
||||
MTRACE("rel time=%u:%u delta=%.3f,%.3f raw=%.3f,%.3f "
|
||||
"pos=%.3f,%.3f", timeHi, timeLo, dx, dy,
|
||||
wl_fixed_to_double(dxUnaccelW), wl_fixed_to_double(dyUnaccelW),
|
||||
"pos=%.3f,%.3f", timeHi, timeLo, dx, dy, rawX, rawY,
|
||||
wlWm.motion.x, wlWm.motion.y);
|
||||
|
||||
app_handleMouseRelative(
|
||||
dx,
|
||||
dy,
|
||||
wl_fixed_to_double(dxUnaccelW),
|
||||
wl_fixed_to_double(dyUnaccelW));
|
||||
wlInputRelativeMotion(&wlWm.input, active, dx, dy, rawX, rawY);
|
||||
}
|
||||
|
||||
static const struct zwp_relative_pointer_v1_listener relativePointerListener = {
|
||||
@@ -314,51 +337,46 @@ static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
|
||||
uint32_t serial, struct wl_surface * surface, struct wl_array * keys)
|
||||
{
|
||||
if (surface != wlWm.surface)
|
||||
{
|
||||
wlInputKeyboardEnter(&wlWm.input, false, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
wlWm.focusedOnSurface = true;
|
||||
app_handleFocusEvent(true);
|
||||
wlWm.keyboardEnterSerial = serial;
|
||||
|
||||
uint32_t * key;
|
||||
wl_array_for_each(key, keys)
|
||||
app_handleKeyPress(*key);
|
||||
wlInputKeyboardEnter(&wlWm.input, true, keys->data,
|
||||
keys->size / sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static void keyboardLeaveHandler(void * data, struct wl_keyboard * keyboard,
|
||||
uint32_t serial, struct wl_surface * surface)
|
||||
{
|
||||
if (surface != wlWm.surface)
|
||||
return;
|
||||
|
||||
wlWm.focusedOnSurface = false;
|
||||
waylandCBInvalidate();
|
||||
app_handleFocusEvent(false);
|
||||
wlInputKeyboardLeave(&wlWm.input, surface == wlWm.surface);
|
||||
}
|
||||
|
||||
static void keyboardKeyHandler(void * data, struct wl_keyboard * keyboard,
|
||||
uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
|
||||
{
|
||||
if (!wlWm.focusedOnSurface)
|
||||
if (!wlWm.input.focused)
|
||||
return;
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||
app_handleKeyPress(key);
|
||||
else
|
||||
app_handleKeyRelease(key);
|
||||
|
||||
if (!wlWm.xkbState || !app_isOverlayMode() || state != WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||
const bool pressed = state == WL_KEYBOARD_KEY_STATE_PRESSED;
|
||||
if (!wlWm.xkbState || !pressed)
|
||||
{
|
||||
wlInputKeyboardKey(&wlWm.input, key, pressed, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
key += 8; // xkb scancode is evdev scancode + 8
|
||||
int size = xkb_state_key_get_utf8(wlWm.xkbState, key, NULL, 0);
|
||||
|
||||
const xkb_keycode_t xkbKey = key + 8;
|
||||
const int size = xkb_state_key_get_utf8(wlWm.xkbState, xkbKey, NULL, 0);
|
||||
if (size <= 0)
|
||||
{
|
||||
wlInputKeyboardKey(&wlWm.input, key, true, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[size + 1];
|
||||
xkb_state_key_get_utf8(wlWm.xkbState, key, buffer, size + 1);
|
||||
app_handleKeyboardTyped(buffer);
|
||||
xkb_state_key_get_utf8(wlWm.xkbState, xkbKey, buffer, size + 1);
|
||||
wlInputKeyboardKey(&wlWm.input, key, true, buffer);
|
||||
}
|
||||
|
||||
static void keyboardModifiersHandler(void * data,
|
||||
@@ -369,18 +387,24 @@ static void keyboardModifiersHandler(void * data,
|
||||
return;
|
||||
|
||||
xkb_state_update_mask(wlWm.xkbState, modsDepressed, modsLatched, modsLocked, 0, 0, group);
|
||||
app_handleKeyboardModifiers(
|
||||
xkb_state_mod_name_is_active(wlWm.xkbState, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0,
|
||||
xkb_state_mod_name_is_active(wlWm.xkbState, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) > 0,
|
||||
xkb_state_mod_name_is_active(wlWm.xkbState, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) > 0,
|
||||
xkb_state_mod_name_is_active(wlWm.xkbState, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) > 0
|
||||
);
|
||||
const bool ctrl = xkb_state_mod_name_is_active(wlWm.xkbState,
|
||||
XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0;
|
||||
const bool shift = xkb_state_mod_name_is_active(wlWm.xkbState,
|
||||
XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) > 0;
|
||||
const bool alt = xkb_state_mod_name_is_active(wlWm.xkbState,
|
||||
XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) > 0;
|
||||
const bool super = xkb_state_mod_name_is_active(wlWm.xkbState,
|
||||
XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) > 0;
|
||||
|
||||
app_handleKeyboardLEDs(
|
||||
xkb_state_led_name_is_active(wlWm.xkbState, XKB_LED_NAME_NUM) > 0,
|
||||
xkb_state_led_name_is_active(wlWm.xkbState, XKB_LED_NAME_CAPS) > 0,
|
||||
xkb_state_led_name_is_active(wlWm.xkbState, XKB_LED_NAME_SCROLL) > 0
|
||||
);
|
||||
const bool numLock = xkb_state_led_name_is_active(wlWm.xkbState,
|
||||
XKB_LED_NAME_NUM) > 0;
|
||||
const bool capsLock = xkb_state_led_name_is_active(wlWm.xkbState,
|
||||
XKB_LED_NAME_CAPS) > 0;
|
||||
const bool scrollLock = xkb_state_led_name_is_active(wlWm.xkbState,
|
||||
XKB_LED_NAME_SCROLL) > 0;
|
||||
|
||||
wlInputKeyboardState(&wlWm.input, ctrl, shift, alt, super,
|
||||
numLock, capsLock, scrollLock);
|
||||
}
|
||||
|
||||
static const struct wl_keyboard_listener keyboardListener = {
|
||||
@@ -468,13 +492,10 @@ static void waylandCleanUpPointer(bool notify)
|
||||
|
||||
wl_pointer_destroy(wlWm.pointer);
|
||||
wlWm.pointer = NULL;
|
||||
wlWm.pointerInSurface = false;
|
||||
});
|
||||
|
||||
MLOG(lockSeq, "lock destroy id=%u why=pointer", lockId);
|
||||
|
||||
if (notify)
|
||||
app_handleEnterEvent(false);
|
||||
wlInputPointerDisconnect(&wlWm.input, notify);
|
||||
}
|
||||
|
||||
// Seat-handling listeners.
|
||||
@@ -531,6 +552,8 @@ static const struct wl_seat_listener seatListener = {
|
||||
|
||||
bool waylandInputInit(bool allowNoInput)
|
||||
{
|
||||
wlInputInit(&wlWm.input, &inputSink, NULL);
|
||||
|
||||
if (!wlWm.seat)
|
||||
{
|
||||
MTRACE("input seat=0 allowNone=%d", allowNoInput);
|
||||
@@ -672,7 +695,7 @@ void waylandWarpPointer(int x, int y, bool exiting)
|
||||
{
|
||||
const int reqX = x;
|
||||
const int reqY = y;
|
||||
if (!wlWm.pointerInSurface)
|
||||
if (!wlWm.input.pointerInSurface)
|
||||
{
|
||||
MTRACE("warp drop=surface target=%d,%d exit=%d", x, y, exiting);
|
||||
return;
|
||||
@@ -737,8 +760,7 @@ void waylandWarpPointer(int x, int y, bool exiting)
|
||||
|
||||
void waylandRealignPointer(void)
|
||||
{
|
||||
if (wlWm.pointerInSurface)
|
||||
app_updateCursorPos(wlWm.motion.x, wlWm.motion.y);
|
||||
wlInputRealignPointer(&wlWm.input, wlWm.motion.x, wlWm.motion.y);
|
||||
}
|
||||
|
||||
void waylandGuestPointerUpdated(double x, double y, double localX,
|
||||
|
||||
179
client/displayservers/Wayland/input_event.c
Normal file
179
client/displayservers/Wayland/input_event.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "input_event.h"
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <string.h>
|
||||
|
||||
static const double WL_SCROLL_STEP = 15.0;
|
||||
static const double WL_HALF_SCROLL_STEP = 7.5;
|
||||
|
||||
void wlInputInit(WaylandInput * input, const LG_DSInputSink * sink,
|
||||
void * opaque)
|
||||
{
|
||||
memset(input, 0, sizeof(*input));
|
||||
input->sink = sink;
|
||||
input->opaque = opaque;
|
||||
}
|
||||
|
||||
bool wlInputPointerEnter(WaylandInput * input, bool mainSurface)
|
||||
{
|
||||
if (!mainSurface)
|
||||
return false;
|
||||
|
||||
input->pointerInSurface = true;
|
||||
input->sink->enter(input->opaque, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wlInputPointerLeave(WaylandInput * input, bool mainSurface)
|
||||
{
|
||||
if (!mainSurface)
|
||||
return false;
|
||||
|
||||
input->pointerInSurface = false;
|
||||
input->sink->enter(input->opaque, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void wlInputPointerDisconnect(WaylandInput * input, bool notify)
|
||||
{
|
||||
input->pointerInSurface = false;
|
||||
if (notify)
|
||||
input->sink->enter(input->opaque, false);
|
||||
}
|
||||
|
||||
void wlInputPointerMotion(WaylandInput * input, double x, double y)
|
||||
{
|
||||
input->sink->position(input->opaque, x, y);
|
||||
}
|
||||
|
||||
static unsigned int mapButton(uint32_t button)
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case BTN_LEFT:
|
||||
return 1;
|
||||
case BTN_MIDDLE:
|
||||
return 2;
|
||||
case BTN_RIGHT:
|
||||
return 3;
|
||||
case BTN_SIDE:
|
||||
return 6;
|
||||
case BTN_EXTRA:
|
||||
return 7;
|
||||
case BTN_FORWARD:
|
||||
return 8;
|
||||
case BTN_BACK:
|
||||
return 9;
|
||||
case BTN_TASK:
|
||||
return 10;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wlInputPointerButton(WaylandInput * input, uint32_t button,
|
||||
bool pressed)
|
||||
{
|
||||
const unsigned int mapped = mapButton(button);
|
||||
if (mapped)
|
||||
input->sink->button(input->opaque, mapped, pressed);
|
||||
}
|
||||
|
||||
void wlInputPointerAxis(WaylandInput * input, bool vertical, double delta)
|
||||
{
|
||||
if (!vertical)
|
||||
return;
|
||||
|
||||
input->scrollState += delta;
|
||||
|
||||
while (input->scrollState > WL_HALF_SCROLL_STEP)
|
||||
{
|
||||
input->sink->button(input->opaque, 5, true);
|
||||
input->sink->button(input->opaque, 5, false);
|
||||
input->scrollState -= WL_SCROLL_STEP;
|
||||
}
|
||||
|
||||
while (input->scrollState < -WL_HALF_SCROLL_STEP)
|
||||
{
|
||||
input->sink->button(input->opaque, 4, true);
|
||||
input->sink->button(input->opaque, 4, false);
|
||||
input->scrollState += WL_SCROLL_STEP;
|
||||
}
|
||||
|
||||
input->sink->wheel(input->opaque, delta / WL_SCROLL_STEP);
|
||||
}
|
||||
|
||||
void wlInputRelativeMotion(WaylandInput * input, bool active,
|
||||
double x, double y, double rawX, double rawY)
|
||||
{
|
||||
if (active)
|
||||
input->sink->relative(input->opaque, x, y, rawX, rawY);
|
||||
}
|
||||
|
||||
void wlInputRealignPointer(WaylandInput * input, double x, double y)
|
||||
{
|
||||
if (input->pointerInSurface)
|
||||
input->sink->position(input->opaque, x, y);
|
||||
}
|
||||
|
||||
bool wlInputKeyboardEnter(WaylandInput * input, bool mainSurface,
|
||||
const uint32_t * keys, size_t count)
|
||||
{
|
||||
if (!mainSurface)
|
||||
return false;
|
||||
|
||||
input->focused = true;
|
||||
input->sink->focus(input->opaque, true);
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
input->sink->key(input->opaque, keys[i], true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wlInputKeyboardLeave(WaylandInput * input, bool mainSurface)
|
||||
{
|
||||
if (!mainSurface)
|
||||
return false;
|
||||
|
||||
input->focused = false;
|
||||
input->sink->focus(input->opaque, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void wlInputKeyboardKey(WaylandInput * input, uint32_t key, bool pressed,
|
||||
const char * text)
|
||||
{
|
||||
if (!input->focused)
|
||||
return;
|
||||
|
||||
input->sink->key(input->opaque, key, pressed);
|
||||
if (pressed && text && *text)
|
||||
input->sink->text(input->opaque, text);
|
||||
}
|
||||
|
||||
void wlInputKeyboardState(WaylandInput * input,
|
||||
bool ctrl, bool shift, bool alt, bool super,
|
||||
bool numLock, bool capsLock, bool scrollLock)
|
||||
{
|
||||
input->sink->modifiers(input->opaque, ctrl, shift, alt, super);
|
||||
input->sink->leds(input->opaque, numLock, capsLock, scrollLock);
|
||||
}
|
||||
63
client/displayservers/Wayland/input_event.h
Normal file
63
client/displayservers/Wayland/input_event.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_LG_WAYLAND_INPUT_EVENT_
|
||||
#define _H_LG_WAYLAND_INPUT_EVENT_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../input.h"
|
||||
|
||||
typedef struct WaylandInput
|
||||
{
|
||||
const LG_DSInputSink * sink;
|
||||
void * opaque;
|
||||
double scrollState;
|
||||
bool pointerInSurface;
|
||||
bool focused;
|
||||
}
|
||||
WaylandInput;
|
||||
|
||||
void wlInputInit(WaylandInput * input, const LG_DSInputSink * sink,
|
||||
void * opaque);
|
||||
|
||||
bool wlInputPointerEnter(WaylandInput * input, bool mainSurface);
|
||||
bool wlInputPointerLeave(WaylandInput * input, bool mainSurface);
|
||||
void wlInputPointerDisconnect(WaylandInput * input, bool notify);
|
||||
void wlInputPointerMotion(WaylandInput * input, double x, double y);
|
||||
void wlInputPointerButton(WaylandInput * input, uint32_t button,
|
||||
bool pressed);
|
||||
void wlInputPointerAxis(WaylandInput * input, bool vertical, double delta);
|
||||
void wlInputRelativeMotion(WaylandInput * input, bool active,
|
||||
double x, double y, double rawX, double rawY);
|
||||
void wlInputRealignPointer(WaylandInput * input, double x, double y);
|
||||
|
||||
bool wlInputKeyboardEnter(WaylandInput * input, bool mainSurface,
|
||||
const uint32_t * keys, size_t count);
|
||||
bool wlInputKeyboardLeave(WaylandInput * input, bool mainSurface);
|
||||
void wlInputKeyboardKey(WaylandInput * input, uint32_t key, bool pressed,
|
||||
const char * text);
|
||||
void wlInputKeyboardState(WaylandInput * input,
|
||||
bool ctrl, bool shift, bool alt, bool super,
|
||||
bool numLock, bool capsLock, bool scrollLock);
|
||||
|
||||
#endif
|
||||
@@ -57,6 +57,7 @@
|
||||
|
||||
#include "scale.h"
|
||||
#include "motion.h"
|
||||
#include "input_event.h"
|
||||
|
||||
typedef void (*WaylandPollCallback)(uint32_t events, void * opaque);
|
||||
typedef void (*WaylandPollCleanup)(void * opaque);
|
||||
@@ -128,8 +129,7 @@ struct WaylandDSState
|
||||
{
|
||||
_Atomic(bool) lockActive;
|
||||
bool keyboardGrabbed;
|
||||
bool pointerInSurface;
|
||||
bool focusedOnSurface;
|
||||
WaylandInput input;
|
||||
|
||||
WL_DesktopOps * desktop;
|
||||
|
||||
@@ -147,7 +147,6 @@ struct WaylandDSState
|
||||
bool configured;
|
||||
bool warpSupport;
|
||||
struct WlMotion motion;
|
||||
double scrollState;
|
||||
|
||||
#if defined(ENABLE_EGL) || defined(ENABLE_OPENGL)
|
||||
struct wl_egl_window * eglWindow;
|
||||
|
||||
@@ -16,6 +16,7 @@ pkg_check_modules(DISPLAYSERVER_X11 REQUIRED IMPORTED_TARGET
|
||||
|
||||
add_library(displayserver_X11 STATIC
|
||||
x11.c
|
||||
input_event.c
|
||||
atoms.c
|
||||
clipboard.c
|
||||
cursor.c
|
||||
|
||||
183
client/displayservers/X11/input_event.c
Normal file
183
client/displayservers/X11/input_event.c
Normal file
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "input_event.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static unsigned int mapButton(unsigned int button)
|
||||
{
|
||||
if (button >= 1 && button <= 5)
|
||||
return button;
|
||||
|
||||
/* X11 reserves buttons 6 and 7 for horizontal scrolling. */
|
||||
if (button >= 8 && button < 34)
|
||||
return button - 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void x11InputInit(X11Input * input, const LG_DSInputSink * sink,
|
||||
void * opaque)
|
||||
{
|
||||
memset(input, 0, sizeof(*input));
|
||||
input->sink = sink;
|
||||
input->opaque = opaque;
|
||||
}
|
||||
|
||||
bool x11InputFocus(X11Input * input, bool focused, double x, double y,
|
||||
const uint32_t * keys, size_t count)
|
||||
{
|
||||
(void)keys;
|
||||
(void)count;
|
||||
|
||||
if (input->focused == focused)
|
||||
return false;
|
||||
|
||||
input->focused = focused;
|
||||
input->sink->position(input->opaque, x, y);
|
||||
input->sink->focus(input->opaque, focused);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool x11InputPointerEnter(X11Input * input, bool mainWindow,
|
||||
bool normal, double x, double y)
|
||||
{
|
||||
if (input->entered || !mainWindow || !normal)
|
||||
return false;
|
||||
|
||||
input->sink->position(input->opaque, x, y);
|
||||
input->sink->enter(input->opaque, true);
|
||||
input->entered = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool x11InputPointerLeave(X11Input * input, bool mainWindow,
|
||||
bool normal, bool captureMode, double x, double y)
|
||||
{
|
||||
if (!input->entered || !mainWindow || input->buttons || captureMode ||
|
||||
!normal)
|
||||
return false;
|
||||
|
||||
input->sink->position(input->opaque, x, y);
|
||||
input->sink->enter(input->opaque, false);
|
||||
input->entered = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void x11InputPointerMotion(X11Input * input, double x, double y,
|
||||
bool pointerGrabbed)
|
||||
{
|
||||
input->sink->position(input->opaque, x, y);
|
||||
|
||||
if (!pointerGrabbed)
|
||||
input->sink->relative(input->opaque, 0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
void x11InputPointerButton(X11Input * input, unsigned int detail,
|
||||
bool pressed, bool raw, uint32_t time, bool inputActive)
|
||||
{
|
||||
if (raw)
|
||||
{
|
||||
if (!inputActive)
|
||||
return;
|
||||
}
|
||||
else if (!input->focused || !input->entered)
|
||||
return;
|
||||
|
||||
const unsigned int button = mapButton(detail);
|
||||
if (!button)
|
||||
return;
|
||||
|
||||
if (!raw)
|
||||
{
|
||||
if (pressed && button == 4)
|
||||
input->sink->wheel(input->opaque, -0.5);
|
||||
else if (pressed && button == 5)
|
||||
input->sink->wheel(input->opaque, 0.5);
|
||||
else if (button != 4 && button != 5)
|
||||
input->sink->button(input->opaque, button, pressed);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t * previousTime = pressed ?
|
||||
&input->previousPressTime : &input->previousReleaseTime;
|
||||
uint32_t * previousButton = pressed ?
|
||||
&input->previousPressButton : &input->previousReleaseButton;
|
||||
|
||||
if (time == *previousTime && detail == *previousButton)
|
||||
return;
|
||||
|
||||
*previousTime = time;
|
||||
*previousButton = detail;
|
||||
|
||||
const uint32_t mask = UINT32_C(1) << button;
|
||||
if (pressed)
|
||||
input->buttons |= mask;
|
||||
else
|
||||
input->buttons &= ~mask;
|
||||
|
||||
input->sink->button(input->opaque, button, pressed);
|
||||
}
|
||||
|
||||
void x11InputRelativeMotion(X11Input * input, uint32_t time,
|
||||
double x, double y, double rawX, double rawY, bool inputActive)
|
||||
{
|
||||
if (!inputActive)
|
||||
return;
|
||||
|
||||
if (time == input->previousMotionTime &&
|
||||
x == input->previousMotionX && y == input->previousMotionY)
|
||||
return;
|
||||
|
||||
input->previousMotionTime = time;
|
||||
input->previousMotionX = x;
|
||||
input->previousMotionY = y;
|
||||
|
||||
input->sink->relative(input->opaque, x, y, rawX, rawY);
|
||||
}
|
||||
|
||||
void x11InputKeyboardKey(X11Input * input, unsigned int keycode,
|
||||
int minKeycode, bool pressed, bool raw, bool inputActive,
|
||||
const char * text)
|
||||
{
|
||||
if (raw)
|
||||
{
|
||||
if (!inputActive)
|
||||
return;
|
||||
}
|
||||
else if (!input->focused)
|
||||
return;
|
||||
|
||||
input->sink->key(input->opaque, (int)keycode - minKeycode, pressed);
|
||||
|
||||
if (!raw && pressed && text)
|
||||
input->sink->text(input->opaque, text);
|
||||
}
|
||||
|
||||
void x11InputKeyboardState(X11Input * input,
|
||||
bool ctrl, bool shift, bool alt, bool super,
|
||||
bool numLock, bool capsLock, bool scrollLock)
|
||||
{
|
||||
(void)numLock;
|
||||
(void)capsLock;
|
||||
(void)scrollLock;
|
||||
input->sink->modifiers(input->opaque, ctrl, shift, alt, super);
|
||||
}
|
||||
69
client/displayservers/X11/input_event.h
Normal file
69
client/displayservers/X11/input_event.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_LG_X11_INPUT_EVENT_
|
||||
#define _H_LG_X11_INPUT_EVENT_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../input.h"
|
||||
|
||||
typedef struct X11Input
|
||||
{
|
||||
const LG_DSInputSink * sink;
|
||||
void * opaque;
|
||||
uint32_t buttons;
|
||||
uint32_t previousPressTime;
|
||||
uint32_t previousReleaseTime;
|
||||
uint32_t previousPressButton;
|
||||
uint32_t previousReleaseButton;
|
||||
uint32_t previousMotionTime;
|
||||
double previousMotionX;
|
||||
double previousMotionY;
|
||||
bool entered;
|
||||
bool focused;
|
||||
}
|
||||
X11Input;
|
||||
|
||||
void x11InputInit(X11Input * input, const LG_DSInputSink * sink,
|
||||
void * opaque);
|
||||
|
||||
bool x11InputFocus(X11Input * input, bool focused, double x, double y,
|
||||
const uint32_t * keys, size_t count);
|
||||
bool x11InputPointerEnter(X11Input * input, bool mainWindow,
|
||||
bool normal, double x, double y);
|
||||
bool x11InputPointerLeave(X11Input * input, bool mainWindow,
|
||||
bool normal, bool captureMode, double x, double y);
|
||||
void x11InputPointerMotion(X11Input * input, double x, double y,
|
||||
bool pointerGrabbed);
|
||||
void x11InputPointerButton(X11Input * input, unsigned int button,
|
||||
bool pressed, bool raw, uint32_t time, bool inputActive);
|
||||
void x11InputRelativeMotion(X11Input * input, uint32_t time,
|
||||
double x, double y, double rawX, double rawY, bool inputActive);
|
||||
void x11InputKeyboardKey(X11Input * input, unsigned int keycode,
|
||||
int minKeycode, bool pressed, bool raw, bool inputActive,
|
||||
const char * text);
|
||||
void x11InputKeyboardState(X11Input * input,
|
||||
bool ctrl, bool shift, bool alt, bool super,
|
||||
bool numLock, bool capsLock, bool scrollLock);
|
||||
|
||||
#endif
|
||||
@@ -95,6 +95,88 @@ static void x11XPresentEvent(XGenericEventCookie *cookie);
|
||||
static void x11UpdateKeyboardGroup(void);
|
||||
static void x11GrabPointer(void);
|
||||
|
||||
static void inputPosition(void * opaque, double x, double y)
|
||||
{
|
||||
(void)opaque;
|
||||
app_updateCursorPos(x, y);
|
||||
}
|
||||
|
||||
static void inputRelative(void * opaque, double x, double y,
|
||||
double rawX, double rawY)
|
||||
{
|
||||
(void)opaque;
|
||||
app_handleMouseRelative(x, y, rawX, rawY);
|
||||
}
|
||||
|
||||
static void inputButton(void * opaque, unsigned int button, bool pressed)
|
||||
{
|
||||
(void)opaque;
|
||||
if (pressed)
|
||||
app_handleButtonPress(button);
|
||||
else
|
||||
app_handleButtonRelease(button);
|
||||
}
|
||||
|
||||
static void inputWheel(void * opaque, double motion)
|
||||
{
|
||||
(void)opaque;
|
||||
app_handleWheelMotion(motion);
|
||||
}
|
||||
|
||||
static void inputKey(void * opaque, int scancode, bool pressed)
|
||||
{
|
||||
(void)opaque;
|
||||
if (pressed)
|
||||
app_handleKeyPress(scancode);
|
||||
else
|
||||
app_handleKeyRelease(scancode);
|
||||
}
|
||||
|
||||
static void inputText(void * opaque, const char * text)
|
||||
{
|
||||
(void)opaque;
|
||||
app_handleKeyboardTyped(text);
|
||||
}
|
||||
|
||||
static void inputModifiers(void * opaque, bool ctrl, bool shift,
|
||||
bool alt, bool super)
|
||||
{
|
||||
(void)opaque;
|
||||
app_handleKeyboardModifiers(ctrl, shift, alt, super);
|
||||
}
|
||||
|
||||
static void inputLEDs(void * opaque, bool numLock, bool capsLock,
|
||||
bool scrollLock)
|
||||
{
|
||||
(void)opaque;
|
||||
app_handleKeyboardLEDs(numLock, capsLock, scrollLock);
|
||||
}
|
||||
|
||||
static void inputEnter(void * opaque, bool entered)
|
||||
{
|
||||
(void)opaque;
|
||||
app_handleEnterEvent(entered);
|
||||
}
|
||||
|
||||
static void inputFocus(void * opaque, bool focused)
|
||||
{
|
||||
(void)opaque;
|
||||
app_handleFocusEvent(focused);
|
||||
}
|
||||
|
||||
static const LG_DSInputSink inputSink = {
|
||||
.position = inputPosition,
|
||||
.relative = inputRelative,
|
||||
.button = inputButton,
|
||||
.wheel = inputWheel,
|
||||
.key = inputKey,
|
||||
.text = inputText,
|
||||
.modifiers = inputModifiers,
|
||||
.leds = inputLEDs,
|
||||
.enter = inputEnter,
|
||||
.focus = inputFocus,
|
||||
};
|
||||
|
||||
static void x11SignalFrame(LG_DSWaitFrameResult result)
|
||||
{
|
||||
atomic_fetch_or_explicit(
|
||||
@@ -354,6 +436,7 @@ static bool x11Init(const LG_DSInitParams params)
|
||||
XSetIOErrorHandler(x11IOErrorHandler);
|
||||
|
||||
memset(&x11, 0, sizeof(x11));
|
||||
x11InputInit(&x11.input, &inputSink, NULL);
|
||||
LG_LOCK_INIT(x11.pointerLock);
|
||||
atomic_init(&x11.captureActive, false);
|
||||
atomic_init(&x11.pointerGrabbed, false);
|
||||
@@ -1117,10 +1200,10 @@ static int x11EventThread(void * unused)
|
||||
focused = true;
|
||||
}
|
||||
|
||||
if (x11.ewmhHasFocusEvent && x11.focused != focused)
|
||||
if (x11.ewmhHasFocusEvent && x11.input.focused != focused)
|
||||
{
|
||||
x11.focused = focused;
|
||||
app_handleFocusEvent(focused);
|
||||
x11.input.focused = focused;
|
||||
inputFocus(x11.input.opaque, focused);
|
||||
}
|
||||
|
||||
x11.fullscreen = fullscreen;
|
||||
@@ -1181,12 +1264,12 @@ static enum Modifiers keySymToModifier(KeySym sym)
|
||||
|
||||
static void updateModifiers(void)
|
||||
{
|
||||
app_handleKeyboardModifiers(
|
||||
x11InputKeyboardState(&x11.input,
|
||||
x11.modifiers[MOD_CTRL_LEFT] || x11.modifiers[MOD_CTRL_RIGHT],
|
||||
x11.modifiers[MOD_SHIFT_LEFT] || x11.modifiers[MOD_SHIFT_RIGHT],
|
||||
x11.modifiers[MOD_ALT_LEFT] || x11.modifiers[MOD_ALT_RIGHT],
|
||||
x11.modifiers[MOD_SUPER_LEFT] || x11.modifiers[MOD_SUPER_RIGHT]
|
||||
);
|
||||
x11.modifiers[MOD_SUPER_LEFT] || x11.modifiers[MOD_SUPER_RIGHT],
|
||||
false, false, false);
|
||||
}
|
||||
|
||||
static void x11UpdateKeyboardGroup(void)
|
||||
@@ -1196,29 +1279,15 @@ static void x11UpdateKeyboardGroup(void)
|
||||
atomic_store(&x11.keyboardGroup, state.group);
|
||||
}
|
||||
|
||||
static int x11MapButton(unsigned int button)
|
||||
{
|
||||
if (button >= 1 && button <= 5)
|
||||
return button;
|
||||
|
||||
/* X11 reserves buttons 6 and 7 for horizontal scrolling. */
|
||||
if (button >= 8 && button < 34)
|
||||
return button - 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void setFocus(bool focused, double x, double y)
|
||||
{
|
||||
if (x11.focused == focused)
|
||||
if (x11.input.focused == focused)
|
||||
return;
|
||||
|
||||
if (focused)
|
||||
x11UpdateKeyboardGroup();
|
||||
|
||||
x11.focused = focused;
|
||||
app_updateCursorPos(x, y);
|
||||
app_handleFocusEvent(focused);
|
||||
x11InputFocus(&x11.input, focused, x, y, NULL, 0);
|
||||
}
|
||||
|
||||
static bool x11GetKeyLabel(int sc, char * label, size_t size)
|
||||
@@ -1238,8 +1307,6 @@ static bool x11GetKeyLabel(int sc, char * label, size_t size)
|
||||
|
||||
static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
{
|
||||
static uint32_t button_state = 0;
|
||||
|
||||
switch(cookie->evtype)
|
||||
{
|
||||
case XI_FocusIn:
|
||||
@@ -1254,7 +1321,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
}
|
||||
|
||||
atomic_store(&x11.lastWMEvent, microtime());
|
||||
if (x11.focused)
|
||||
if (x11.input.focused)
|
||||
return;
|
||||
|
||||
if (xie->mode != XINotifyNormal &&
|
||||
@@ -1279,7 +1346,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
}
|
||||
|
||||
atomic_store(&x11.lastWMEvent, microtime());
|
||||
if (!x11.focused)
|
||||
if (!x11.input.focused)
|
||||
return;
|
||||
|
||||
if (xie->mode != XINotifyNormal &&
|
||||
@@ -1295,13 +1362,8 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
{
|
||||
atomic_store(&x11.lastWMEvent, microtime());
|
||||
XIEnterEvent *xie = cookie->data;
|
||||
if (x11.entered || xie->event != x11.window ||
|
||||
xie->mode != XINotifyNormal)
|
||||
return;
|
||||
|
||||
app_updateCursorPos(xie->event_x, xie->event_y);
|
||||
app_handleEnterEvent(true);
|
||||
x11.entered = true;
|
||||
x11InputPointerEnter(&x11.input, xie->event == x11.window,
|
||||
xie->mode == XINotifyNormal, xie->event_x, xie->event_y);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1310,15 +1372,11 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
atomic_store(&x11.lastWMEvent, microtime());
|
||||
XILeaveEvent *xie = cookie->data;
|
||||
|
||||
if (!x11.entered || xie->event != x11.window ||
|
||||
button_state != 0 || app_isCaptureMode() ||
|
||||
xie->mode == NotifyGrab)
|
||||
if (!x11InputPointerLeave(&x11.input, xie->event == x11.window,
|
||||
xie->mode != NotifyGrab, app_isCaptureMode(),
|
||||
xie->event_x, xie->event_y))
|
||||
return;
|
||||
|
||||
app_updateCursorPos(xie->event_x, xie->event_y);
|
||||
app_handleEnterEvent(false);
|
||||
x11.entered = false;
|
||||
|
||||
/**
|
||||
* Because there is a race with the pointer ungrab the enter event for the
|
||||
* next window is sometimes sent with the mode NotifyUngrab, unfortunatly
|
||||
@@ -1363,12 +1421,13 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
|
||||
case XI_KeyPress:
|
||||
{
|
||||
if (!x11.focused || x11.keyboardGrabbed)
|
||||
if (!x11.input.focused || x11.keyboardGrabbed)
|
||||
return;
|
||||
|
||||
XIDeviceEvent *device = cookie->data;
|
||||
atomic_store(&x11.keyboardGroup, device->group.effective);
|
||||
app_handleKeyPress(device->detail - x11.minKeycode);
|
||||
x11InputKeyboardKey(&x11.input, device->detail, x11.minKeycode,
|
||||
true, false, true, NULL);
|
||||
|
||||
if (!x11.xic || !app_isOverlayMode())
|
||||
return;
|
||||
@@ -1397,7 +1456,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
if (status == XLookupChars || status == XLookupBoth)
|
||||
{
|
||||
buffer[count] = '\0';
|
||||
app_handleKeyboardTyped(buffer);
|
||||
inputText(x11.input.opaque, buffer);
|
||||
}
|
||||
|
||||
if (status == XLookupKeySym || status == XLookupBoth)
|
||||
@@ -1414,11 +1473,12 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
|
||||
case XI_KeyRelease:
|
||||
{
|
||||
if (!x11.focused || x11.keyboardGrabbed)
|
||||
if (!x11.input.focused || x11.keyboardGrabbed)
|
||||
return;
|
||||
|
||||
XIDeviceEvent *device = cookie->data;
|
||||
app_handleKeyRelease(device->detail - x11.minKeycode);
|
||||
x11InputKeyboardKey(&x11.input, device->detail, x11.minKeycode,
|
||||
false, false, true, NULL);
|
||||
|
||||
if (!x11.xic || !app_isOverlayMode())
|
||||
return;
|
||||
@@ -1443,116 +1503,63 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
|
||||
case XI_RawKeyPress:
|
||||
{
|
||||
if (!x11.focused)
|
||||
return;
|
||||
|
||||
XIRawEvent *raw = cookie->data;
|
||||
app_handleKeyPress(raw->detail - x11.minKeycode);
|
||||
x11InputKeyboardKey(&x11.input, raw->detail, x11.minKeycode,
|
||||
true, true, x11.input.focused, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
case XI_RawKeyRelease:
|
||||
{
|
||||
if (!x11.focused)
|
||||
return;
|
||||
|
||||
XIRawEvent *raw = cookie->data;
|
||||
app_handleKeyRelease(raw->detail - x11.minKeycode);
|
||||
x11InputKeyboardKey(&x11.input, raw->detail, x11.minKeycode,
|
||||
false, true, x11.input.focused, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
case XI_ButtonPress:
|
||||
{
|
||||
if (!x11.focused || !x11.entered)
|
||||
return;
|
||||
|
||||
XIDeviceEvent *device = cookie->data;
|
||||
const int button = x11MapButton(device->detail);
|
||||
if (button == 4)
|
||||
app_handleWheelMotion(-0.5);
|
||||
else if (button == 5)
|
||||
app_handleWheelMotion(0.5);
|
||||
else if (button)
|
||||
app_handleButtonPress(button);
|
||||
|
||||
x11InputPointerButton(&x11.input, device->detail, true, false,
|
||||
device->time, true);
|
||||
return;
|
||||
}
|
||||
|
||||
case XI_ButtonRelease:
|
||||
{
|
||||
if (!x11.focused || !x11.entered)
|
||||
return;
|
||||
|
||||
XIDeviceEvent *device = cookie->data;
|
||||
const int button = x11MapButton(device->detail);
|
||||
if (button && button != 4 && button != 5)
|
||||
app_handleButtonRelease(button);
|
||||
x11InputPointerButton(&x11.input, device->detail, false, false,
|
||||
device->time, true);
|
||||
return;
|
||||
}
|
||||
|
||||
case XI_RawButtonPress:
|
||||
{
|
||||
if (!x11.focused || !x11.entered)
|
||||
return;
|
||||
|
||||
XIRawEvent *raw = cookie->data;
|
||||
const int button = x11MapButton(raw->detail);
|
||||
if (!button)
|
||||
return;
|
||||
|
||||
/* filter out duplicate events */
|
||||
static Time prev_time = 0;
|
||||
static unsigned int prev_detail = 0;
|
||||
if (raw->time == prev_time && raw->detail == prev_detail)
|
||||
return;
|
||||
|
||||
prev_time = raw->time;
|
||||
prev_detail = raw->detail;
|
||||
button_state |= UINT32_C(1) << button;
|
||||
|
||||
app_handleButtonPress(button);
|
||||
x11InputPointerButton(&x11.input, raw->detail, true, true,
|
||||
raw->time, x11.input.focused && x11.input.entered);
|
||||
return;
|
||||
}
|
||||
|
||||
case XI_RawButtonRelease:
|
||||
{
|
||||
if (!x11.focused || !x11.entered)
|
||||
return;
|
||||
|
||||
XIRawEvent *raw = cookie->data;
|
||||
const int button = x11MapButton(raw->detail);
|
||||
if (!button)
|
||||
return;
|
||||
|
||||
/* filter out duplicate events */
|
||||
static Time prev_time = 0;
|
||||
static unsigned int prev_detail = 0;
|
||||
if (raw->time == prev_time && raw->detail == prev_detail)
|
||||
return;
|
||||
|
||||
prev_time = raw->time;
|
||||
prev_detail = raw->detail;
|
||||
button_state &= ~(UINT32_C(1) << button);
|
||||
|
||||
app_handleButtonRelease(button);
|
||||
x11InputPointerButton(&x11.input, raw->detail, false, true,
|
||||
raw->time, x11.input.focused && x11.input.entered);
|
||||
return;
|
||||
}
|
||||
|
||||
case XI_Motion:
|
||||
{
|
||||
XIDeviceEvent *device = cookie->data;
|
||||
app_updateCursorPos(device->event_x, device->event_y);
|
||||
|
||||
if (!atomic_load_explicit(&x11.pointerGrabbed, memory_order_acquire))
|
||||
app_handleMouseRelative(0.0, 0.0, 0.0, 0.0);
|
||||
x11InputPointerMotion(&x11.input, device->event_x, device->event_y,
|
||||
atomic_load_explicit(&x11.pointerGrabbed,
|
||||
memory_order_acquire));
|
||||
return;
|
||||
}
|
||||
|
||||
case XI_RawMotion:
|
||||
{
|
||||
if (!x11.focused || !x11.entered)
|
||||
return;
|
||||
|
||||
XIRawEvent *raw = cookie->data;
|
||||
double raw_axis[2] = { 0 };
|
||||
double axis[2] = { 0 };
|
||||
@@ -1587,19 +1594,9 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
if (!has_axes)
|
||||
return;
|
||||
|
||||
/* filter out duplicate events */
|
||||
static Time prev_time = 0;
|
||||
static double prev_axis[2] = {0};
|
||||
if (raw->time == prev_time &&
|
||||
axis[0] == prev_axis[0] &&
|
||||
axis[1] == prev_axis[1])
|
||||
return;
|
||||
|
||||
prev_time = raw->time;
|
||||
prev_axis[0] = axis[0];
|
||||
prev_axis[1] = axis[1];
|
||||
|
||||
app_handleMouseRelative(axis[0], axis[1], raw_axis[0], raw_axis[1]);
|
||||
x11InputRelativeMotion(&x11.input, raw->time, axis[0], axis[1],
|
||||
raw_axis[0], raw_axis[1],
|
||||
x11.input.focused && x11.input.entered);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1903,7 +1900,7 @@ static void x11StopWaitFrame(void)
|
||||
|
||||
static void x11GuestPointerUpdated(double x, double y, double localX, double localY)
|
||||
{
|
||||
if (app_isCaptureMode() || !x11.entered)
|
||||
if (app_isCaptureMode() || !x11.input.entered)
|
||||
return;
|
||||
|
||||
// avoid running too often
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "common/locking.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/types.h"
|
||||
#include "input_event.h"
|
||||
#include "wm.h"
|
||||
|
||||
enum Modifiers
|
||||
@@ -96,12 +97,11 @@ struct X11DSState
|
||||
int xValuator;
|
||||
int yValuator;
|
||||
|
||||
X11Input input;
|
||||
LG_Lock pointerLock;
|
||||
_Atomic(bool) captureActive;
|
||||
_Atomic(bool) pointerGrabbed;
|
||||
bool keyboardGrabbed;
|
||||
bool entered;
|
||||
bool focused;
|
||||
bool fullscreen;
|
||||
|
||||
struct Rect rect;
|
||||
|
||||
44
client/displayservers/input.h
Normal file
44
client/displayservers/input.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_LG_DS_INPUT_
|
||||
#define _H_LG_DS_INPUT_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct LG_DSInputSink
|
||||
{
|
||||
void (*position)(void * opaque, double x, double y);
|
||||
void (*relative)(void * opaque, double x, double y,
|
||||
double rawX, double rawY);
|
||||
void (*button)(void * opaque, unsigned int button, bool pressed);
|
||||
void (*wheel)(void * opaque, double motion);
|
||||
void (*key)(void * opaque, int scancode, bool pressed);
|
||||
void (*text)(void * opaque, const char * text);
|
||||
void (*modifiers)(void * opaque, bool ctrl, bool shift, bool alt,
|
||||
bool super);
|
||||
void (*leds)(void * opaque, bool numLock, bool capsLock,
|
||||
bool scrollLock);
|
||||
void (*enter)(void * opaque, bool entered);
|
||||
void (*focus)(void * opaque, bool focused);
|
||||
}
|
||||
LG_DSInputSink;
|
||||
|
||||
#endif
|
||||
@@ -47,6 +47,46 @@ set_tests_properties(wayland-motion-tests PROPERTIES
|
||||
TIMEOUT 10
|
||||
)
|
||||
|
||||
add_executable(displayserver-input-tests
|
||||
displayserver_input_test.c
|
||||
../displayservers/Wayland/input_event.c
|
||||
../displayservers/X11/input_event.c
|
||||
)
|
||||
target_include_directories(displayserver-input-tests PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../displayservers"
|
||||
)
|
||||
|
||||
set(DISPLAYSERVER_INPUT_CASES
|
||||
pointer-events
|
||||
pointer-buttons
|
||||
pointer-release
|
||||
wheel-steps
|
||||
relative-motion
|
||||
keyboard-focus
|
||||
keyboard-keys
|
||||
keyboard-state
|
||||
)
|
||||
|
||||
foreach(backend IN ITEMS wayland x11)
|
||||
foreach(name IN LISTS DISPLAYSERVER_INPUT_CASES)
|
||||
add_test(NAME display-input-${backend}-${name}
|
||||
COMMAND displayserver-input-tests ${backend} ${name}
|
||||
)
|
||||
set_tests_properties(display-input-${backend}-${name} PROPERTIES
|
||||
TIMEOUT 10
|
||||
)
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
foreach(name IN ITEMS wheel-residual wheel-multiple)
|
||||
add_test(NAME display-input-wayland-${name}
|
||||
COMMAND displayserver-input-tests wayland ${name}
|
||||
)
|
||||
set_tests_properties(display-input-wayland-${name} PROPERTIES
|
||||
TIMEOUT 10
|
||||
)
|
||||
endforeach()
|
||||
|
||||
add_executable(mouse-tests
|
||||
mouse_test.c
|
||||
../src/core.c
|
||||
|
||||
877
client/tests/displayserver_input_test.c
Normal file
877
client/tests/displayserver_input_test.c
Normal file
@@ -0,0 +1,877 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "Wayland/input_event.h"
|
||||
#include "X11/input_event.h"
|
||||
#include "test.h"
|
||||
|
||||
#include <linux/input-event-codes.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
enum Backend
|
||||
{
|
||||
BACKEND_WAYLAND,
|
||||
BACKEND_X11,
|
||||
};
|
||||
|
||||
enum TraceType
|
||||
{
|
||||
TRACE_POSITION,
|
||||
TRACE_RELATIVE,
|
||||
TRACE_BUTTON,
|
||||
TRACE_WHEEL,
|
||||
TRACE_KEY,
|
||||
TRACE_TEXT,
|
||||
TRACE_MODIFIERS,
|
||||
TRACE_LEDS,
|
||||
TRACE_ENTER,
|
||||
TRACE_FOCUS,
|
||||
};
|
||||
|
||||
struct Trace
|
||||
{
|
||||
enum TraceType type;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
double x;
|
||||
double y;
|
||||
double rawX;
|
||||
double rawY;
|
||||
}
|
||||
motion;
|
||||
|
||||
struct
|
||||
{
|
||||
unsigned int value;
|
||||
bool pressed;
|
||||
}
|
||||
input;
|
||||
|
||||
struct
|
||||
{
|
||||
bool ctrl;
|
||||
bool shift;
|
||||
bool alt;
|
||||
bool super;
|
||||
}
|
||||
modifiers;
|
||||
|
||||
struct
|
||||
{
|
||||
bool num;
|
||||
bool caps;
|
||||
bool scroll;
|
||||
}
|
||||
leds;
|
||||
|
||||
double wheel;
|
||||
bool state;
|
||||
char text[32];
|
||||
} data;
|
||||
};
|
||||
|
||||
#define POSITION(_x, _y) \
|
||||
{ \
|
||||
.type = TRACE_POSITION, \
|
||||
.data.motion = { .x = (_x), .y = (_y) }, \
|
||||
}
|
||||
|
||||
#define RELATIVE(_x, _y, _rawX, _rawY) \
|
||||
{ \
|
||||
.type = TRACE_RELATIVE, \
|
||||
.data.motion = \
|
||||
{ .x = (_x), .y = (_y), .rawX = (_rawX), .rawY = (_rawY) }, \
|
||||
}
|
||||
|
||||
#define BUTTON(_button, _pressed) \
|
||||
{ \
|
||||
.type = TRACE_BUTTON, \
|
||||
.data.input = { .value = (_button), .pressed = (_pressed) }, \
|
||||
}
|
||||
|
||||
#define WHEEL(_motion) \
|
||||
{ \
|
||||
.type = TRACE_WHEEL, \
|
||||
.data.wheel = (_motion), \
|
||||
}
|
||||
|
||||
#define KEY(_key, _pressed) \
|
||||
{ \
|
||||
.type = TRACE_KEY, \
|
||||
.data.input = { .value = (_key), .pressed = (_pressed) }, \
|
||||
}
|
||||
|
||||
#define TEXT(_text) \
|
||||
{ \
|
||||
.type = TRACE_TEXT, \
|
||||
.data.text = _text, \
|
||||
}
|
||||
|
||||
#define MODIFIERS(_ctrl, _shift, _alt, _super) \
|
||||
{ \
|
||||
.type = TRACE_MODIFIERS, \
|
||||
.data.modifiers = \
|
||||
{ .ctrl = (_ctrl), .shift = (_shift), .alt = (_alt), \
|
||||
.super = (_super) }, \
|
||||
}
|
||||
|
||||
#define LEDS(_num, _caps, _scroll) \
|
||||
{ \
|
||||
.type = TRACE_LEDS, \
|
||||
.data.leds = { .num = (_num), .caps = (_caps), .scroll = (_scroll) }, \
|
||||
}
|
||||
|
||||
#define ENTER(_entered) \
|
||||
{ \
|
||||
.type = TRACE_ENTER, \
|
||||
.data.state = (_entered), \
|
||||
}
|
||||
|
||||
#define FOCUS(_focused) \
|
||||
{ \
|
||||
.type = TRACE_FOCUS, \
|
||||
.data.state = (_focused), \
|
||||
}
|
||||
|
||||
#define ARRAY_LENGTH(_array) (sizeof(_array) / sizeof((_array)[0]))
|
||||
#define MAX_TRACE 128
|
||||
|
||||
struct TraceLog
|
||||
{
|
||||
struct Trace values[MAX_TRACE];
|
||||
size_t count;
|
||||
};
|
||||
|
||||
struct Fixture
|
||||
{
|
||||
enum Backend backend;
|
||||
struct TraceLog trace;
|
||||
uint32_t time;
|
||||
|
||||
union
|
||||
{
|
||||
WaylandInput wayland;
|
||||
X11Input x11;
|
||||
} input;
|
||||
};
|
||||
|
||||
static struct Trace * pushTrace(struct TraceLog * log, enum TraceType type)
|
||||
{
|
||||
CHECK(log->count < ARRAY_LENGTH(log->values));
|
||||
struct Trace * trace = &log->values[log->count++];
|
||||
memset(trace, 0, sizeof(*trace));
|
||||
trace->type = type;
|
||||
return trace;
|
||||
}
|
||||
|
||||
static void tracePosition(void * opaque, double x, double y)
|
||||
{
|
||||
struct Trace * trace = pushTrace(opaque, TRACE_POSITION);
|
||||
trace->data.motion.x = x;
|
||||
trace->data.motion.y = y;
|
||||
}
|
||||
|
||||
static void traceRelative(void * opaque, double x, double y,
|
||||
double rawX, double rawY)
|
||||
{
|
||||
struct Trace * trace = pushTrace(opaque, TRACE_RELATIVE);
|
||||
trace->data.motion.x = x;
|
||||
trace->data.motion.y = y;
|
||||
trace->data.motion.rawX = rawX;
|
||||
trace->data.motion.rawY = rawY;
|
||||
}
|
||||
|
||||
static void traceButton(void * opaque, unsigned int button, bool pressed)
|
||||
{
|
||||
struct Trace * trace = pushTrace(opaque, TRACE_BUTTON);
|
||||
trace->data.input.value = button;
|
||||
trace->data.input.pressed = pressed;
|
||||
}
|
||||
|
||||
static void traceWheel(void * opaque, double motion)
|
||||
{
|
||||
struct Trace * trace = pushTrace(opaque, TRACE_WHEEL);
|
||||
trace->data.wheel = motion;
|
||||
}
|
||||
|
||||
static void traceKey(void * opaque, int scancode, bool pressed)
|
||||
{
|
||||
struct Trace * trace = pushTrace(opaque, TRACE_KEY);
|
||||
trace->data.input.value = scancode;
|
||||
trace->data.input.pressed = pressed;
|
||||
}
|
||||
|
||||
static void traceText(void * opaque, const char * text)
|
||||
{
|
||||
struct Trace * trace = pushTrace(opaque, TRACE_TEXT);
|
||||
snprintf(trace->data.text, sizeof(trace->data.text), "%s", text);
|
||||
}
|
||||
|
||||
static void traceModifiers(void * opaque, bool ctrl, bool shift,
|
||||
bool alt, bool super)
|
||||
{
|
||||
struct Trace * trace = pushTrace(opaque, TRACE_MODIFIERS);
|
||||
trace->data.modifiers.ctrl = ctrl;
|
||||
trace->data.modifiers.shift = shift;
|
||||
trace->data.modifiers.alt = alt;
|
||||
trace->data.modifiers.super = super;
|
||||
}
|
||||
|
||||
static void traceLEDs(void * opaque, bool numLock, bool capsLock,
|
||||
bool scrollLock)
|
||||
{
|
||||
struct Trace * trace = pushTrace(opaque, TRACE_LEDS);
|
||||
trace->data.leds.num = numLock;
|
||||
trace->data.leds.caps = capsLock;
|
||||
trace->data.leds.scroll = scrollLock;
|
||||
}
|
||||
|
||||
static void traceEnter(void * opaque, bool entered)
|
||||
{
|
||||
struct Trace * trace = pushTrace(opaque, TRACE_ENTER);
|
||||
trace->data.state = entered;
|
||||
}
|
||||
|
||||
static void traceFocus(void * opaque, bool focused)
|
||||
{
|
||||
struct Trace * trace = pushTrace(opaque, TRACE_FOCUS);
|
||||
trace->data.state = focused;
|
||||
}
|
||||
|
||||
static const LG_DSInputSink sink =
|
||||
{
|
||||
.position = tracePosition,
|
||||
.relative = traceRelative,
|
||||
.button = traceButton,
|
||||
.wheel = traceWheel,
|
||||
.key = traceKey,
|
||||
.text = traceText,
|
||||
.modifiers = traceModifiers,
|
||||
.leds = traceLEDs,
|
||||
.enter = traceEnter,
|
||||
.focus = traceFocus,
|
||||
};
|
||||
|
||||
static void initFixture(struct Fixture * fixture, enum Backend backend)
|
||||
{
|
||||
memset(fixture, 0, sizeof(*fixture));
|
||||
fixture->backend = backend;
|
||||
fixture->time = 1;
|
||||
|
||||
if (backend == BACKEND_WAYLAND)
|
||||
wlInputInit(&fixture->input.wayland, &sink, &fixture->trace);
|
||||
else
|
||||
x11InputInit(&fixture->input.x11, &sink, &fixture->trace);
|
||||
}
|
||||
|
||||
static void clearTrace(struct Fixture * fixture)
|
||||
{
|
||||
fixture->trace.count = 0;
|
||||
}
|
||||
|
||||
static bool sameDouble(double a, double b)
|
||||
{
|
||||
const double delta = a > b ? a - b : b - a;
|
||||
return delta < 0.000000001;
|
||||
}
|
||||
|
||||
static bool sameTrace(const struct Trace * actual,
|
||||
const struct Trace * expected)
|
||||
{
|
||||
if (actual->type != expected->type)
|
||||
return false;
|
||||
|
||||
switch (actual->type)
|
||||
{
|
||||
case TRACE_POSITION:
|
||||
return
|
||||
sameDouble(actual->data.motion.x, expected->data.motion.x) &&
|
||||
sameDouble(actual->data.motion.y, expected->data.motion.y);
|
||||
|
||||
case TRACE_RELATIVE:
|
||||
return
|
||||
sameDouble(actual->data.motion.x, expected->data.motion.x) &&
|
||||
sameDouble(actual->data.motion.y, expected->data.motion.y) &&
|
||||
sameDouble(actual->data.motion.rawX, expected->data.motion.rawX) &&
|
||||
sameDouble(actual->data.motion.rawY, expected->data.motion.rawY);
|
||||
|
||||
case TRACE_BUTTON:
|
||||
case TRACE_KEY:
|
||||
return
|
||||
actual->data.input.value == expected->data.input.value &&
|
||||
actual->data.input.pressed == expected->data.input.pressed;
|
||||
|
||||
case TRACE_WHEEL:
|
||||
return sameDouble(actual->data.wheel, expected->data.wheel);
|
||||
|
||||
case TRACE_TEXT:
|
||||
return strcmp(actual->data.text, expected->data.text) == 0;
|
||||
|
||||
case TRACE_MODIFIERS:
|
||||
return
|
||||
actual->data.modifiers.ctrl == expected->data.modifiers.ctrl &&
|
||||
actual->data.modifiers.shift == expected->data.modifiers.shift &&
|
||||
actual->data.modifiers.alt == expected->data.modifiers.alt &&
|
||||
actual->data.modifiers.super == expected->data.modifiers.super;
|
||||
|
||||
case TRACE_LEDS:
|
||||
return
|
||||
actual->data.leds.num == expected->data.leds.num &&
|
||||
actual->data.leds.caps == expected->data.leds.caps &&
|
||||
actual->data.leds.scroll == expected->data.leds.scroll;
|
||||
|
||||
case TRACE_ENTER:
|
||||
case TRACE_FOCUS:
|
||||
return actual->data.state == expected->data.state;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char * traceName(enum TraceType type)
|
||||
{
|
||||
static const char * names[] =
|
||||
{
|
||||
[TRACE_POSITION ] = "position",
|
||||
[TRACE_RELATIVE ] = "relative",
|
||||
[TRACE_BUTTON ] = "button",
|
||||
[TRACE_WHEEL ] = "wheel",
|
||||
[TRACE_KEY ] = "key",
|
||||
[TRACE_TEXT ] = "text",
|
||||
[TRACE_MODIFIERS] = "modifiers",
|
||||
[TRACE_LEDS ] = "leds",
|
||||
[TRACE_ENTER ] = "enter",
|
||||
[TRACE_FOCUS ] = "focus",
|
||||
};
|
||||
return names[type];
|
||||
}
|
||||
|
||||
static void printTrace(const struct Trace * trace)
|
||||
{
|
||||
fprintf(stderr, "%s", traceName(trace->type));
|
||||
switch (trace->type)
|
||||
{
|
||||
case TRACE_POSITION:
|
||||
fprintf(stderr, " %.6f %.6f", trace->data.motion.x,
|
||||
trace->data.motion.y);
|
||||
break;
|
||||
|
||||
case TRACE_RELATIVE:
|
||||
fprintf(stderr, " %.6f %.6f %.6f %.6f", trace->data.motion.x,
|
||||
trace->data.motion.y, trace->data.motion.rawX,
|
||||
trace->data.motion.rawY);
|
||||
break;
|
||||
|
||||
case TRACE_BUTTON:
|
||||
case TRACE_KEY:
|
||||
fprintf(stderr, " %u %s", trace->data.input.value,
|
||||
trace->data.input.pressed ? "down" : "up");
|
||||
break;
|
||||
|
||||
case TRACE_WHEEL:
|
||||
fprintf(stderr, " %.6f", trace->data.wheel);
|
||||
break;
|
||||
|
||||
case TRACE_TEXT:
|
||||
fprintf(stderr, " %s", trace->data.text);
|
||||
break;
|
||||
|
||||
case TRACE_MODIFIERS:
|
||||
fprintf(stderr, " %d %d %d %d", trace->data.modifiers.ctrl,
|
||||
trace->data.modifiers.shift, trace->data.modifiers.alt,
|
||||
trace->data.modifiers.super);
|
||||
break;
|
||||
|
||||
case TRACE_LEDS:
|
||||
fprintf(stderr, " %d %d %d", trace->data.leds.num,
|
||||
trace->data.leds.caps, trace->data.leds.scroll);
|
||||
break;
|
||||
|
||||
case TRACE_ENTER:
|
||||
case TRACE_FOCUS:
|
||||
fprintf(stderr, " %d", trace->data.state);
|
||||
break;
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
static void expectTrace(const struct Fixture * fixture,
|
||||
const struct Trace * expected, size_t expectedCount)
|
||||
{
|
||||
bool match = fixture->trace.count == expectedCount;
|
||||
if (match)
|
||||
for (size_t i = 0; i < expectedCount; ++i)
|
||||
if (!sameTrace(&fixture->trace.values[i], &expected[i]))
|
||||
{
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (match)
|
||||
return;
|
||||
|
||||
fprintf(stderr, "expected trace (%zu events):\n", expectedCount);
|
||||
for (size_t i = 0; i < expectedCount; ++i)
|
||||
printTrace(&expected[i]);
|
||||
|
||||
fprintf(stderr, "actual trace (%zu events):\n", fixture->trace.count);
|
||||
for (size_t i = 0; i < fixture->trace.count; ++i)
|
||||
printTrace(&fixture->trace.values[i]);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void pointerEnter(struct Fixture * fixture, bool mainSurface,
|
||||
double x, double y)
|
||||
{
|
||||
if (fixture->backend == BACKEND_WAYLAND)
|
||||
{
|
||||
if (wlInputPointerEnter(&fixture->input.wayland, mainSurface))
|
||||
wlInputPointerMotion(&fixture->input.wayland, x, y);
|
||||
}
|
||||
else
|
||||
x11InputPointerEnter(&fixture->input.x11, mainSurface, true, x, y);
|
||||
}
|
||||
|
||||
static void pointerLeave(struct Fixture * fixture, bool mainSurface,
|
||||
double x, double y)
|
||||
{
|
||||
if (fixture->backend == BACKEND_WAYLAND)
|
||||
wlInputPointerLeave(&fixture->input.wayland, mainSurface);
|
||||
else
|
||||
x11InputPointerLeave(&fixture->input.x11, mainSurface, true, false,
|
||||
x, y);
|
||||
}
|
||||
|
||||
static void pointerMotion(struct Fixture * fixture, double x, double y)
|
||||
{
|
||||
if (fixture->backend == BACKEND_WAYLAND)
|
||||
wlInputPointerMotion(&fixture->input.wayland, x, y);
|
||||
else
|
||||
x11InputPointerMotion(&fixture->input.x11, x, y, false);
|
||||
}
|
||||
|
||||
static unsigned int nativeButton(enum Backend backend,
|
||||
unsigned int button)
|
||||
{
|
||||
if (backend == BACKEND_X11)
|
||||
return button > 5 ? button + 2 : button;
|
||||
|
||||
static const unsigned int wayland[] =
|
||||
{
|
||||
[1 ] = BTN_LEFT,
|
||||
[2 ] = BTN_MIDDLE,
|
||||
[3 ] = BTN_RIGHT,
|
||||
[6 ] = BTN_SIDE,
|
||||
[7 ] = BTN_EXTRA,
|
||||
[8 ] = BTN_FORWARD,
|
||||
[9 ] = BTN_BACK,
|
||||
[10] = BTN_TASK,
|
||||
};
|
||||
CHECK(button < ARRAY_LENGTH(wayland));
|
||||
return wayland[button];
|
||||
}
|
||||
|
||||
static void pointerButton(struct Fixture * fixture, unsigned int button,
|
||||
bool pressed)
|
||||
{
|
||||
const unsigned int native = nativeButton(fixture->backend, button);
|
||||
if (fixture->backend == BACKEND_WAYLAND)
|
||||
wlInputPointerButton(&fixture->input.wayland, native, pressed);
|
||||
else
|
||||
x11InputPointerButton(&fixture->input.x11, native, pressed, false,
|
||||
fixture->time++, true);
|
||||
}
|
||||
|
||||
static void unmappedButton(struct Fixture * fixture)
|
||||
{
|
||||
if (fixture->backend == BACKEND_WAYLAND)
|
||||
wlInputPointerButton(&fixture->input.wayland, BTN_STYLUS, true);
|
||||
else
|
||||
x11InputPointerButton(&fixture->input.x11, 6, true, false,
|
||||
fixture->time++, true);
|
||||
}
|
||||
|
||||
static void wheelStep(struct Fixture * fixture, bool down)
|
||||
{
|
||||
if (fixture->backend == BACKEND_WAYLAND)
|
||||
wlInputPointerAxis(&fixture->input.wayland, true, down ? 15.0 : -15.0);
|
||||
else
|
||||
x11InputPointerButton(&fixture->input.x11, down ? 5 : 4, true,
|
||||
false, fixture->time++, true);
|
||||
}
|
||||
|
||||
static void relativeMotion(struct Fixture * fixture, bool active,
|
||||
uint32_t time, double x, double y, double rawX, double rawY)
|
||||
{
|
||||
if (fixture->backend == BACKEND_WAYLAND)
|
||||
wlInputRelativeMotion(&fixture->input.wayland, active,
|
||||
x, y, rawX, rawY);
|
||||
else
|
||||
x11InputRelativeMotion(&fixture->input.x11, time,
|
||||
x, y, rawX, rawY, active);
|
||||
}
|
||||
|
||||
static void keyboardEnter(struct Fixture * fixture, bool mainSurface,
|
||||
const uint32_t * keys, size_t count)
|
||||
{
|
||||
if (fixture->backend == BACKEND_WAYLAND)
|
||||
wlInputKeyboardEnter(&fixture->input.wayland, mainSurface, keys, count);
|
||||
else if (mainSurface)
|
||||
x11InputFocus(&fixture->input.x11, true, 40.0, 30.0, keys, count);
|
||||
}
|
||||
|
||||
static void keyboardLeave(struct Fixture * fixture, bool mainSurface)
|
||||
{
|
||||
if (fixture->backend == BACKEND_WAYLAND)
|
||||
wlInputKeyboardLeave(&fixture->input.wayland, mainSurface);
|
||||
else if (mainSurface)
|
||||
x11InputFocus(&fixture->input.x11, false, 40.0, 30.0, NULL, 0);
|
||||
}
|
||||
|
||||
static void keyboardKey(struct Fixture * fixture, unsigned int key,
|
||||
bool pressed, const char * text, bool inputActive)
|
||||
{
|
||||
if (fixture->backend == BACKEND_WAYLAND)
|
||||
wlInputKeyboardKey(&fixture->input.wayland, key, pressed, text);
|
||||
else
|
||||
x11InputKeyboardKey(&fixture->input.x11, key + 8, 8, pressed,
|
||||
false, inputActive, text);
|
||||
}
|
||||
|
||||
static void keyboardState(struct Fixture * fixture,
|
||||
bool ctrl, bool shift, bool alt, bool super,
|
||||
bool numLock, bool capsLock, bool scrollLock)
|
||||
{
|
||||
if (fixture->backend == BACKEND_WAYLAND)
|
||||
wlInputKeyboardState(&fixture->input.wayland,
|
||||
ctrl, shift, alt, super, numLock, capsLock, scrollLock);
|
||||
else
|
||||
x11InputKeyboardState(&fixture->input.x11,
|
||||
ctrl, shift, alt, super, numLock, capsLock, scrollLock);
|
||||
}
|
||||
|
||||
static void activateInput(struct Fixture * fixture)
|
||||
{
|
||||
pointerEnter(fixture, true, 40.0, 30.0);
|
||||
keyboardEnter(fixture, true, NULL, 0);
|
||||
clearTrace(fixture);
|
||||
}
|
||||
|
||||
static void activatePointer(struct Fixture * fixture)
|
||||
{
|
||||
pointerEnter(fixture, true, 40.0, 30.0);
|
||||
clearTrace(fixture);
|
||||
}
|
||||
|
||||
static void testPointerEvents(enum Backend backend)
|
||||
{
|
||||
struct Fixture fixture;
|
||||
initFixture(&fixture, backend);
|
||||
|
||||
pointerEnter(&fixture, false, 1.0, 2.0);
|
||||
pointerEnter(&fixture, true, 10.0, 20.0);
|
||||
pointerMotion(&fixture, 11.5, 21.25);
|
||||
pointerLeave(&fixture, false, 4.0, 5.0);
|
||||
pointerLeave(&fixture, true, 12.0, 22.0);
|
||||
|
||||
static const struct Trace expected[] =
|
||||
{
|
||||
ENTER(true),
|
||||
POSITION(10.0, 20.0),
|
||||
POSITION(11.5, 21.25),
|
||||
ENTER(false),
|
||||
};
|
||||
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
|
||||
}
|
||||
|
||||
static void testPointerButtons(enum Backend backend)
|
||||
{
|
||||
struct Fixture fixture;
|
||||
initFixture(&fixture, backend);
|
||||
activatePointer(&fixture);
|
||||
|
||||
static const unsigned int buttons[] = { 1, 2, 3, 6, 7, 8, 9, 10 };
|
||||
for (size_t i = 0; i < ARRAY_LENGTH(buttons); ++i)
|
||||
{
|
||||
pointerButton(&fixture, buttons[i], true);
|
||||
pointerButton(&fixture, buttons[i], false);
|
||||
}
|
||||
unmappedButton(&fixture);
|
||||
|
||||
static const struct Trace expected[] =
|
||||
{
|
||||
BUTTON(1 , true), BUTTON(1 , false),
|
||||
BUTTON(2 , true), BUTTON(2 , false),
|
||||
BUTTON(3 , true), BUTTON(3 , false),
|
||||
BUTTON(6 , true), BUTTON(6 , false),
|
||||
BUTTON(7 , true), BUTTON(7 , false),
|
||||
BUTTON(8 , true), BUTTON(8 , false),
|
||||
BUTTON(9 , true), BUTTON(9 , false),
|
||||
BUTTON(10, true), BUTTON(10, false),
|
||||
};
|
||||
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
|
||||
}
|
||||
|
||||
static void testPointerRelease(enum Backend backend)
|
||||
{
|
||||
struct Fixture fixture;
|
||||
initFixture(&fixture, backend);
|
||||
activateInput(&fixture);
|
||||
|
||||
pointerButton(&fixture, 1, true);
|
||||
|
||||
/* Wayland's implicit pointer grab suppresses this native leave. */
|
||||
if (backend == BACKEND_X11)
|
||||
pointerLeave(&fixture, true, 100.0, 100.0);
|
||||
|
||||
pointerButton(&fixture, 1, false);
|
||||
|
||||
static const struct Trace expected[] =
|
||||
{
|
||||
BUTTON(1, true),
|
||||
BUTTON(1, false),
|
||||
};
|
||||
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
|
||||
}
|
||||
|
||||
static void testWheelSteps(enum Backend backend)
|
||||
{
|
||||
struct Fixture fixture;
|
||||
initFixture(&fixture, backend);
|
||||
activateInput(&fixture);
|
||||
|
||||
wheelStep(&fixture, true);
|
||||
wheelStep(&fixture, false);
|
||||
|
||||
static const struct Trace expected[] =
|
||||
{
|
||||
BUTTON(5, true),
|
||||
BUTTON(5, false),
|
||||
WHEEL(1.0),
|
||||
BUTTON(4, true),
|
||||
BUTTON(4, false),
|
||||
WHEEL(-1.0),
|
||||
};
|
||||
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
|
||||
}
|
||||
|
||||
static void testWheelResidual(enum Backend backend)
|
||||
{
|
||||
CHECK(backend == BACKEND_WAYLAND);
|
||||
struct Fixture fixture;
|
||||
initFixture(&fixture, backend);
|
||||
|
||||
wlInputPointerAxis(&fixture.input.wayland, false, 100.0);
|
||||
wlInputPointerAxis(&fixture.input.wayland, true, 5.0);
|
||||
wlInputPointerAxis(&fixture.input.wayland, true, 5.0);
|
||||
wlInputPointerAxis(&fixture.input.wayland, true, -2.0);
|
||||
wlInputPointerAxis(&fixture.input.wayland, true, -1.0);
|
||||
|
||||
static const struct Trace expected[] =
|
||||
{
|
||||
WHEEL(1.0 / 3.0),
|
||||
BUTTON(5, true),
|
||||
BUTTON(5, false),
|
||||
WHEEL(1.0 / 3.0),
|
||||
WHEEL(-2.0 / 15.0),
|
||||
BUTTON(4, true),
|
||||
BUTTON(4, false),
|
||||
WHEEL(-1.0 / 15.0),
|
||||
};
|
||||
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
|
||||
}
|
||||
|
||||
static void testWheelMultiple(enum Backend backend)
|
||||
{
|
||||
CHECK(backend == BACKEND_WAYLAND);
|
||||
struct Fixture fixture;
|
||||
initFixture(&fixture, backend);
|
||||
|
||||
wlInputPointerAxis(&fixture.input.wayland, true, 45.0);
|
||||
|
||||
static const struct Trace expected[] =
|
||||
{
|
||||
BUTTON(5, true), BUTTON(5, false),
|
||||
BUTTON(5, true), BUTTON(5, false),
|
||||
BUTTON(5, true), BUTTON(5, false),
|
||||
WHEEL(3.0),
|
||||
};
|
||||
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
|
||||
}
|
||||
|
||||
static void testRelativeMotion(enum Backend backend)
|
||||
{
|
||||
struct Fixture fixture;
|
||||
initFixture(&fixture, backend);
|
||||
activateInput(&fixture);
|
||||
|
||||
relativeMotion(&fixture, false, 50, 1.0, 2.0, 3.0, 4.0);
|
||||
relativeMotion(&fixture, true, 51, 1.0, 2.0, 3.0, 4.0);
|
||||
relativeMotion(&fixture, true, 51, 1.0, 2.0, 3.0, 4.0);
|
||||
|
||||
static const struct Trace expected[] =
|
||||
{
|
||||
RELATIVE(1.0, 2.0, 3.0, 4.0),
|
||||
RELATIVE(1.0, 2.0, 3.0, 4.0),
|
||||
};
|
||||
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
|
||||
}
|
||||
|
||||
static void testKeyboardFocus(enum Backend backend)
|
||||
{
|
||||
struct Fixture fixture;
|
||||
initFixture(&fixture, backend);
|
||||
static const uint32_t held[] = { 30, 42 };
|
||||
|
||||
keyboardEnter(&fixture, false, held, ARRAY_LENGTH(held));
|
||||
keyboardEnter(&fixture, true, held, ARRAY_LENGTH(held));
|
||||
keyboardLeave(&fixture, false);
|
||||
keyboardLeave(&fixture, true);
|
||||
|
||||
static const struct Trace expected[] =
|
||||
{
|
||||
FOCUS(true),
|
||||
KEY(30, true),
|
||||
KEY(42, true),
|
||||
FOCUS(false),
|
||||
};
|
||||
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
|
||||
}
|
||||
|
||||
static void testKeyboardKeys(enum Backend backend)
|
||||
{
|
||||
struct Fixture fixture;
|
||||
initFixture(&fixture, backend);
|
||||
|
||||
keyboardKey(&fixture, 30, true, "ignored", true);
|
||||
keyboardKey(&fixture, 30, false, "ignored", true);
|
||||
CHECK(fixture.trace.count == 0);
|
||||
|
||||
keyboardEnter(&fixture, true, NULL, 0);
|
||||
clearTrace(&fixture);
|
||||
|
||||
keyboardKey(&fixture, 30, true, "a", true);
|
||||
keyboardKey(&fixture, 30, false, "ignored", true);
|
||||
|
||||
static const struct Trace expected[] =
|
||||
{
|
||||
KEY(30, true),
|
||||
TEXT("a"),
|
||||
KEY(30, false),
|
||||
};
|
||||
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
|
||||
|
||||
keyboardLeave(&fixture, true);
|
||||
clearTrace(&fixture);
|
||||
keyboardKey(&fixture, 31, true, "s", true);
|
||||
keyboardKey(&fixture, 31, false, NULL, true);
|
||||
CHECK(fixture.trace.count == 0);
|
||||
}
|
||||
|
||||
static void testKeyboardState(enum Backend backend)
|
||||
{
|
||||
struct Fixture fixture;
|
||||
initFixture(&fixture, backend);
|
||||
keyboardEnter(&fixture, true, NULL, 0);
|
||||
clearTrace(&fixture);
|
||||
|
||||
keyboardState(&fixture, true, false, true, false,
|
||||
true, false, true);
|
||||
keyboardState(&fixture, false, true, false, true,
|
||||
false, true, false);
|
||||
|
||||
static const struct Trace expected[] =
|
||||
{
|
||||
MODIFIERS(true, false, true, false),
|
||||
LEDS(true, false, true),
|
||||
MODIFIERS(false, true, false, true),
|
||||
LEDS(false, true, false),
|
||||
};
|
||||
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
|
||||
}
|
||||
|
||||
struct Test
|
||||
{
|
||||
const char * name;
|
||||
void (*run)(enum Backend backend);
|
||||
};
|
||||
|
||||
static const struct Test tests[] =
|
||||
{
|
||||
{ "pointer-events" , testPointerEvents },
|
||||
{ "pointer-buttons", testPointerButtons },
|
||||
{ "pointer-release", testPointerRelease },
|
||||
{ "wheel-steps" , testWheelSteps },
|
||||
{ "wheel-residual" , testWheelResidual },
|
||||
{ "wheel-multiple" , testWheelMultiple },
|
||||
{ "relative-motion", testRelativeMotion },
|
||||
{ "keyboard-focus" , testKeyboardFocus },
|
||||
{ "keyboard-keys" , testKeyboardKeys },
|
||||
{ "keyboard-state" , testKeyboardState },
|
||||
};
|
||||
|
||||
static bool parseBackend(const char * name, enum Backend * backend)
|
||||
{
|
||||
if (strcmp(name, "wayland") == 0)
|
||||
{
|
||||
*backend = BACKEND_WAYLAND;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcmp(name, "x11") == 0)
|
||||
{
|
||||
*backend = BACKEND_X11;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
fprintf(stderr, "usage: %s <wayland|x11> <case>\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
enum Backend backend;
|
||||
if (!parseBackend(argv[1], &backend))
|
||||
{
|
||||
fprintf(stderr, "unknown backend: %s\n", argv[1]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ARRAY_LENGTH(tests); ++i)
|
||||
if (strcmp(argv[2], tests[i].name) == 0)
|
||||
{
|
||||
tests[i].run(backend);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr, "unknown test: %s\n", argv[2]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
Reference in New Issue
Block a user