From 5206933399775e5ff46baefbbb3a776c2a9bb84c Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Mon, 12 Oct 2026 10:53:22 +0300 Subject: feat: add type-safe native platform headers and portable drag support - Add include/yasl/native/{wayland,cocoa,x11}.h with type-safe handle accessors. - Unify drag-and-drop and window carry support across Wayland, X11, and Cocoa. - Preserve full ABI compatibility for capability flags. - Install native headers conditionally based on enabled backend. - Include missing tablet example and backend test suites. --- tests/x11/test_tablet.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 tests/x11/test_tablet.c (limited to 'tests/x11/test_tablet.c') diff --git a/tests/x11/test_tablet.c b/tests/x11/test_tablet.c new file mode 100644 index 0000000..39717df --- /dev/null +++ b/tests/x11/test_tablet.c @@ -0,0 +1,103 @@ +/* Exercise native XI2 samples through the backend and public input queue. */ +#include "../../src/x11/window.c" + +static struct yasl_mouse_event next(struct x11_surface *surface) +{ + struct yasl_input_event event; + assert(yasl_surface_input_poll(&surface->base, &event)); + assert(event.surface == &surface->base && event.kind == YASL_INPUT_MOUSE); + return event.mouse; +} + +int main(void) +{ + struct x11_display display = { + .base.ops = &yasl_x11_backend, .abs_pressure = 10, .abs_tilt_x = 11, .abs_tilt_y = 12, + }; + struct x11_surface surface = {.base.display = &display.base, .window = 42}; + display.surfaces = &surface; + XIValuatorClassInfo pressure = {.type = XIValuatorClass, .number = 2, + .label = 10, .min = 100, .max = 1100, .mode = XIModeAbsolute}; + XIValuatorClassInfo tilt_x = {.type = XIValuatorClass, .number = 4, + .label = 11, .min = -64, .max = 63, .mode = XIModeAbsolute}; + XIValuatorClassInfo tilt_y = {.type = XIValuatorClass, .number = 7, + .label = 12, .min = -64, .max = 63, .mode = XIModeAbsolute}; + XIAnyClassInfo *classes[] = {(void *)&pressure, (void *)&tilt_x, (void *)&tilt_y}; + XIDeviceInfo info = {.deviceid = 8, .name = "Tablet pen", .num_classes = 3, .classes = classes}; + struct x11_pointer pointer = {0}; + pointer_info(&display, &pointer, &info); + assert(pointer.device == YASL_PTR_DEV_PEN); + display.pointers = &pointer; /* Cache avoids requiring a real X server. */ + + unsigned char mask[1] = {0}; + XISetMask(mask, 2); + XISetMask(mask, 4); + XISetMask(mask, 7); + double values[] = {600, -35, 42}; + XIDeviceEvent sample = {.evtype = XI_ButtonPress, .sourceid = 8, .event = 42, + .detail = 1, .event_x = 200.5, .event_y = 175.25, + .mods.effective = ShiftMask, .valuators = {sizeof(mask), mask, values}}; + XGenericEventCookie cookie = {.evtype = XI_ButtonPress, .data = &sample}; + xi_event(&display, &cookie); + struct yasl_mouse_event ev = next(&surface); + assert(ev.kind == YASL_PTR_PRESS && ev.button == 0 && ev.device == YASL_PTR_DEV_PEN); + assert(ev.pos.x == 201 && ev.pos.y == 175 && ev.mods == YASL_MOD_SHIFT); + assert(ev.pen.pressure == 32768 && ev.pen.tilt_x == -35 && ev.pen.tilt_y == 42); + + /* XI2 sends only changed axes, with values packed in mask order. */ + mask[0] = 0; + XISetMask(mask, 2); + values[0] = 1100; + sample.evtype = cookie.evtype = XI_Motion; + xi_event(&display, &cookie); + ev = next(&surface); + assert(ev.kind == YASL_PTR_MOTION && ev.pen.pressure == 65535); + assert(ev.pen.tilt_x == -35 && ev.pen.tilt_y == 42); + values[0] = 5000; + xi_event(&display, &cookie); + assert(next(&surface).pen.pressure == 65535); + values[0] = -50; + xi_event(&display, &cookie); + assert(next(&surface).pen.pressure == 0); + + info.name = "Tablet ERASER"; + pointer_info(&display, &pointer, &info); + assert(pointer.device == YASL_PTR_DEV_ERASER); + values[0] = 900; + sample.evtype = cookie.evtype = XI_ButtonPress; + xi_event(&display, &cookie); + ev = next(&surface); + assert(ev.device == YASL_PTR_DEV_ERASER && ev.pen.pressure > 0); + /* Release must clear pressure even when the driver omits that valuator. */ + sample.valuators.mask_len = 0; + sample.evtype = cookie.evtype = XI_ButtonRelease; + xi_event(&display, &cookie); + ev = next(&surface); + assert(ev.kind == YASL_PTR_RELEASE && ev.button == 0 && ev.pen.pressure == 0); + assert(ev.device == YASL_PTR_DEV_ERASER); + + XIEnterEvent crossing = {.sourceid = 8, .event = 42, .event_x = 200, .event_y = 175}; + cookie = (XGenericEventCookie){.evtype = XI_Leave, .data = &crossing}; + xi_event(&display, &cookie); + ev = next(&surface); + assert(ev.kind == YASL_PTR_LEAVE && ev.device == YASL_PTR_DEV_ERASER && !ev.pen.pressure); + + /* No tablet axes must produce an ordinary, pressure-free mouse. */ + info.num_classes = 0; + pointer_info(&display, &pointer, &info); + sample.evtype = XI_ButtonPress; + sample.detail = 3; + xi_pointer_event(&surface, &sample); + ev = next(&surface); + assert(ev.device == YASL_PTR_DEV_MOUSE && ev.button == 2 && ev.pen.pressure == 0); + assert(pointer_button(2) == 1 && pointer_button(8) == 3); + pointer.pressure = (struct tablet_axis){2, 1, 1, 50}; + assert(pressure_value(&pointer.pressure) == 0); + pointer.tilt_x = (struct tablet_axis){4, -90, 90, -200}; + assert(tilt_value(&pointer.tilt_x) == -90); + pointer.tilt_x.value = 200; + assert(tilt_value(&pointer.tilt_x) == 90); + display.pointers = NULL; + input_queue_clear(&surface.input); + return 0; +} -- cgit v1.2.3