mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-10 17:21:32 +00:00
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / idd (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
Keep raw and cooked event selection inside the tested X11 policy. Exercise grab transitions and held-key normalization directly.
990 lines
25 KiB
C
990 lines
25 KiB
C
/**
|
|
* 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;
|
|
|
|
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;
|
|
|
|
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)
|
|
{
|
|
if (fixture->backend == BACKEND_WAYLAND)
|
|
wlInputPointerLeave(&fixture->input.wayland, mainSurface);
|
|
else
|
|
x11InputPointerLeave(&fixture->input.x11, mainSurface, true, false);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static void relativeMotion(struct Fixture * fixture, bool active,
|
|
double x, double y, double rawX, double rawY)
|
|
{
|
|
if (fixture->backend == BACKEND_WAYLAND)
|
|
wlInputRelativeMotion(&fixture->input.wayland, active,
|
|
x, y, rawX, rawY);
|
|
else
|
|
{
|
|
x11InputSetPointerGrabbed(&fixture->input.x11, active);
|
|
x11InputRelativeMotion(&fixture->input.x11, x, y, rawX, rawY);
|
|
}
|
|
}
|
|
|
|
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, 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, NULL, 0);
|
|
}
|
|
|
|
static void keyboardKey(struct Fixture * fixture, unsigned int key,
|
|
bool pressed, const char * text)
|
|
{
|
|
if (fixture->backend == BACKEND_WAYLAND)
|
|
wlInputKeyboardKey(&fixture->input.wayland, key, pressed, text);
|
|
else
|
|
{
|
|
const bool accepted = x11InputKeyboardKey(
|
|
&fixture->input.x11, key + 8, 8, pressed, false);
|
|
if (accepted && pressed)
|
|
x11InputKeyboardText(&fixture->input.x11, 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);
|
|
pointerLeave(&fixture, true);
|
|
|
|
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);
|
|
|
|
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, 1.0, 2.0, 3.0, 4.0);
|
|
relativeMotion(&fixture, true, 1.0, 2.0, 3.0, 4.0);
|
|
relativeMotion(&fixture, true, 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");
|
|
keyboardKey(&fixture, 30, false, "ignored");
|
|
CHECK(fixture.trace.count == 0);
|
|
|
|
keyboardEnter(&fixture, true, NULL, 0);
|
|
clearTrace(&fixture);
|
|
|
|
keyboardKey(&fixture, 30, true, "a");
|
|
keyboardKey(&fixture, 30, false, "ignored");
|
|
keyboardKey(&fixture, 31, true, "");
|
|
keyboardKey(&fixture, 31, false, NULL);
|
|
|
|
static const struct Trace expected[] =
|
|
{
|
|
KEY(30, true),
|
|
TEXT("a"),
|
|
KEY(30, false),
|
|
KEY(31, true),
|
|
KEY(31, false),
|
|
};
|
|
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
|
|
|
|
keyboardLeave(&fixture, true);
|
|
clearTrace(&fixture);
|
|
keyboardKey(&fixture, 31, true, "s");
|
|
keyboardKey(&fixture, 31, false, NULL);
|
|
CHECK(fixture.trace.count == 0);
|
|
}
|
|
|
|
static void testX11PointerRouting(enum Backend backend)
|
|
{
|
|
CHECK(backend == BACKEND_X11);
|
|
|
|
struct Fixture fixture;
|
|
initFixture(&fixture, backend);
|
|
pointerEnter(&fixture, true, 40.0, 30.0);
|
|
clearTrace(&fixture);
|
|
|
|
x11InputPointerButton(&fixture.input.x11, 1, true, true);
|
|
x11InputRelativeMotion(&fixture.input.x11, 1.0, 2.0, 3.0, 4.0);
|
|
|
|
x11InputSetPointerGrabbed(&fixture.input.x11, true);
|
|
x11InputPointerButton(&fixture.input.x11, 1, true, false);
|
|
x11InputPointerButton(&fixture.input.x11, 1, true, true);
|
|
x11InputRelativeMotion(&fixture.input.x11, 1.0, 2.0, 3.0, 4.0);
|
|
|
|
x11InputSetPointerGrabbed(&fixture.input.x11, false);
|
|
x11InputPointerButton(&fixture.input.x11, 1, false, true);
|
|
x11InputRelativeMotion(&fixture.input.x11, 1.0, 2.0, 3.0, 4.0);
|
|
x11InputPointerButton(&fixture.input.x11, 1, false, false);
|
|
|
|
static const struct Trace expected[] =
|
|
{
|
|
BUTTON(1, true),
|
|
RELATIVE(1.0, 2.0, 3.0, 4.0),
|
|
BUTTON(1, false),
|
|
};
|
|
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
|
|
CHECK(fixture.input.x11.buttons == 0);
|
|
}
|
|
|
|
static void testX11KeyboardRouting(enum Backend backend)
|
|
{
|
|
CHECK(backend == BACKEND_X11);
|
|
|
|
struct Fixture fixture;
|
|
initFixture(&fixture, backend);
|
|
keyboardEnter(&fixture, true, NULL, 0);
|
|
clearTrace(&fixture);
|
|
|
|
CHECK(!x11InputKeyboardKey(&fixture.input.x11,
|
|
30 + 8, 8, true, true));
|
|
CHECK(x11InputKeyboardKey(&fixture.input.x11,
|
|
30 + 8, 8, true, false));
|
|
x11InputKeyboardText(&fixture.input.x11, "a");
|
|
|
|
x11InputSetKeyboardGrabbed(&fixture.input.x11, true);
|
|
CHECK(!x11InputKeyboardKey(&fixture.input.x11,
|
|
31 + 8, 8, true, false));
|
|
CHECK(x11InputKeyboardKey(&fixture.input.x11,
|
|
30 + 8, 8, false, true));
|
|
CHECK(x11InputKeyboardKey(&fixture.input.x11,
|
|
31 + 8, 8, true, true));
|
|
|
|
x11InputSetKeyboardGrabbed(&fixture.input.x11, false);
|
|
CHECK(!x11InputKeyboardKey(&fixture.input.x11,
|
|
31 + 8, 8, false, true));
|
|
CHECK(x11InputKeyboardKey(&fixture.input.x11,
|
|
31 + 8, 8, false, false));
|
|
|
|
static const struct Trace expected[] =
|
|
{
|
|
KEY(30, true),
|
|
TEXT("a"),
|
|
KEY(30, false),
|
|
KEY(31, true),
|
|
KEY(31, false),
|
|
};
|
|
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
|
|
}
|
|
|
|
static void setKeymapKey(char keymap[X11_INPUT_KEYMAP_SIZE],
|
|
unsigned int keycode)
|
|
{
|
|
CHECK(keycode < X11_INPUT_KEYMAP_SIZE * 8);
|
|
keymap[keycode / 8] = (char)(
|
|
(unsigned char)keymap[keycode / 8] | (1U << (keycode % 8)));
|
|
}
|
|
|
|
static void testX11HeldKeys(enum Backend backend)
|
|
{
|
|
CHECK(backend == BACKEND_X11);
|
|
|
|
char keymap[X11_INPUT_KEYMAP_SIZE] = { 0 };
|
|
setKeymapKey(keymap, 7);
|
|
setKeymapKey(keymap, 8);
|
|
setKeymapKey(keymap, 30);
|
|
setKeymapKey(keymap, 42);
|
|
setKeymapKey(keymap, 43);
|
|
|
|
uint32_t keys[4];
|
|
size_t count = x11InputHeldKeys(keymap, 8, 42,
|
|
keys, ARRAY_LENGTH(keys));
|
|
CHECK(count == 3);
|
|
CHECK(keys[0] == 0);
|
|
CHECK(keys[1] == 22);
|
|
CHECK(keys[2] == 34);
|
|
|
|
count = x11InputHeldKeys(keymap, 8, 42, keys, 2);
|
|
CHECK(count == 2);
|
|
CHECK(keys[0] == 0);
|
|
CHECK(keys[1] == 22);
|
|
}
|
|
|
|
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 },
|
|
{ "pointer-routing" , testX11PointerRouting },
|
|
{ "keyboard-routing", testX11KeyboardRouting },
|
|
{ "held-keys" , testX11HeldKeys },
|
|
};
|
|
|
|
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;
|
|
}
|