diff --git a/test_seat.c b/test_seat.c new file mode 100644 index 0000000..ebcf1e0 --- /dev/null +++ b/test_seat.c @@ -0,0 +1,790 @@ +/* Wayland compositor running on top of an X server. + +Copyright (C) 2022 to various contributors. + +This file is part of 12to11. + +12to11 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 3 of the License, or (at your +option) any later version. + +12to11 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 12to11. If not, see . */ + +#include "12to11-test.h" + +/* This file is included by seat.c at the very bottom, so it does not + have to include anything itself! */ + +typedef struct _TestSeatController TestSeatController; +typedef struct _TestXIModifierState TestXIModifierState; +typedef struct _TestXIValuatorState TestXIValuatorState; +typedef struct _TestXIButtonState TestXIButtonState; + +/* The current test seat counter. */ +static unsigned int test_seat_counter; + +/* The test serial counter. */ +static unsigned long request_serial_counter; + +struct _TestSeatController +{ + /* The associated seat. */ + Seat *seat; + + /* The associated controller resource. */ + struct wl_resource *resource; +}; + +struct _TestXIModifierState +{ + /* Modifier state. These fields mean the same as they do in + XIModifierState. */ + int base; + int latched; + int locked; + int effective; +}; + +struct _TestXIValuatorState +{ + /* The mask of set valuators. */ + unsigned char *mask; + + /* Sparse array of valuators. */ + double *values; + + /* The length of the mask. */ + size_t mask_len; + + /* The number of valuators set. */ + int num_valuators; +}; + +struct _TestXIButtonState +{ + /* Mask of set buttons. Always between 0 and 8. */ + unsigned char mask[XIMaskLen (8)]; +}; + + + +static void +DestroyXIModifierState (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +SetValues (struct wl_client *client, struct wl_resource *resource, + int32_t base, int32_t latched, int32_t locked, int32_t effective) +{ + TestXIModifierState *state; + + state = wl_resource_get_user_data (resource); + state->base = base; + state->latched = latched; + state->locked = locked; + state->effective = effective; +} + +static const struct test_XIModifierState_interface XIModifierState_impl = + { + .destroy = DestroyXIModifierState, + .set_values = SetValues, + }; + +static void +HandleXIModifierStateDestroy (struct wl_resource *resource) +{ + TestXIModifierState *state; + + state = wl_resource_get_user_data (resource); + XLFree (state); +} + + + +static void +DestroyXIButtonState (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +AddButton (struct wl_client *client, struct wl_resource *resource, + uint32_t button) +{ + TestXIButtonState *state; + + state = wl_resource_get_user_data (resource); + + if (button < 1 || button > 8) + /* The button is invalid. */ + wl_resource_post_error (resource, TEST_MANAGER_ERROR_INVALID_BUTTON, + "invalid button specified"); + else + SetMask (state->mask, button); +} + +static void +RemoveButton (struct wl_client *client, struct wl_resource *resource, + uint32_t button) +{ + TestXIButtonState *state; + + state = wl_resource_get_user_data (resource); + + if (button < 1 || button > 8) + wl_resource_post_error (resource, TEST_MANAGER_ERROR_INVALID_BUTTON, + "invalid button specified"); + else + ClearMask (state->mask, button); +} + +static const struct test_XIButtonState_interface XIButtonState_impl = + { + .destroy = DestroyXIButtonState, + .add_button = AddButton, + .remove_button = RemoveButton, + }; + +static void +HandleXIButtonStateDestroy (struct wl_resource *resource) +{ + TestXIButtonState *state; + + state = wl_resource_get_user_data (resource); + XLFree (state); +} + + + +static void +AddValuatorToTestXIValuatorState (struct wl_client *client, + struct wl_resource *resource, + uint32_t valuator, + wl_fixed_t value) +{ + TestXIValuatorState *state; + double *old_values, *new_values; + size_t i, j; + + if (valuator < 1 || valuator > 65535) + { + /* The valuator cannot be represented. */ + wl_resource_post_error (resource, TEST_MANAGER_ERROR_INVALID_VALUATOR, + "the specified valuator cannot be represented"); + return; + } + + state = wl_resource_get_user_data (resource); + + /* Check if the valuator is already present and post a + value_exists error if so. */ + if (XIMaskLen (valuator) <= state->mask_len + && MaskIsSet (state->mask, valuator)) + wl_resource_post_error (resource, TEST_MANAGER_ERROR_VALUE_EXISTS, + "the specified valuator is already set"); + else + { + /* If the mask needs to be expanded, do it now. */ + if (state->mask_len < XIMaskLen (valuator)) + { + state->mask = XLRealloc (state->mask, + state->mask_len); + + /* Clear the newly allocated part of the mask. */ + memset (state->mask + state->mask_len, + 0, XIMaskLen (valuator) - state->mask_len); + } + + SetMask (state->mask, valuator); + state->num_valuators++; + + /* Now, rewrite the sparse array of values. */ + old_values = state->values; + new_values = XLCalloc (state->num_valuators, + sizeof *state->values); + + for (i = 0, j = 0; i < MAX (state->mask_len, + XIMaskLen (valuator)) * 8; ++i) + { + if (i == valuator) + /* Insert the new value. */ + new_values[j++] = wl_fixed_to_double (value); + else if (XIMaskIsSet (state->mask, valuator)) + /* Use the old value. */ + new_values[j++] = *old_values++; + } + + /* Free the old values. */ + XLFree (state->values); + + /* Assign the new values and mask length to the state. */ + state->values = new_values; + state->mask_len = MAX (state->mask_len, + XIMaskLen (valuator)); + } +} + +static const struct test_XIValuatorState_interface XIValuatorState_impl = + { + .add_valuator = AddValuatorToTestXIValuatorState, + }; + +static void +HandleXIValuatorStateDestroy (struct wl_resource *resource) +{ + TestXIValuatorState *state; + + state = wl_resource_get_user_data (resource); + + /* Free the mask. */ + XLFree (state->mask); + + /* Free the values. */ + XLFree (state->values); + + /* Free the state itself. */ + XLFree (state); +} + + + +static void +DestroyTestSeatController (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +BindSeat (struct wl_client *client, struct wl_resource *resource, + uint32_t version, uint32_t id) +{ + TestSeatController *controller; + + controller = wl_resource_get_user_data (resource); + + if (!version || version > 8) + wl_resource_post_error (resource, TEST_MANAGER_ERROR_BAD_SEAT_VERSION, + "the specified version of the wl_seat interface" + " is not supported"); + else + /* Bind the resource to the seat. */ + HandleBind1 (client, controller->seat, version, id); +} + +static void +GetXIModifierState (struct wl_client *client, struct wl_resource *resource, + uint32_t id) +{ + TestXIModifierState *state; + struct wl_resource *state_resource; + + state = XLSafeMalloc (sizeof *state); + + if (!state) + { + /* Allocating the state structure failed. */ + wl_resource_post_no_memory (resource); + return; + } + + /* Now, try to create the modifier state resource. */ + state_resource + = wl_resource_create (client, &test_XIModifierState_interface, + wl_resource_get_version (resource), id); + + if (!state_resource) + { + /* Allocating the resource failed. */ + XLFree (state); + wl_resource_post_no_memory (resource); + + return; + } + + /* Clear the state. */ + memset (state, 0, sizeof *state); + + /* Set the resource data. */ + wl_resource_set_implementation (state_resource, &XIModifierState_impl, + state, HandleXIModifierStateDestroy); +} + +static void +GetXIButtonState (struct wl_client *client, struct wl_resource *resource, + uint32_t id) +{ + TestXIButtonState *state; + struct wl_resource *state_resource; + + state = XLSafeMalloc (sizeof *state); + + if (!state) + { + /* Allocating the state structure failed. */ + wl_resource_post_no_memory (resource); + return; + } + + /* Now, try to create the button state resource. */ + state_resource + = wl_resource_create (client, &test_XIButtonState_interface, + wl_resource_get_version (resource), id); + + if (!state_resource) + { + /* Allocating the resource failed. */ + XLFree (state); + wl_resource_post_no_memory (resource); + + return; + } + + /* Clear the state. */ + memset (state, 0, sizeof *state); + + /* Set the resource data. */ + wl_resource_set_implementation (state_resource, &XIButtonState_impl, + state, HandleXIButtonStateDestroy); +} + +static void +GetXIValuatorState (struct wl_client *client, struct wl_resource *resource, + uint32_t id) +{ + TestXIValuatorState *state; + struct wl_resource *state_resource; + + state = XLSafeMalloc (sizeof *state); + + if (!state) + { + /* Allocating the state structure failed. */ + wl_resource_post_no_memory (resource); + return; + } + + /* Now, try to create the button state resource. */ + state_resource + = wl_resource_create (client, &test_XIValuatorState_interface, + wl_resource_get_version (resource), id); + + if (!state_resource) + { + /* Allocating the resource failed. */ + XLFree (state); + wl_resource_post_no_memory (resource); + + return; + } + + /* Clear the state. */ + memset (state, 0, sizeof *state); + + /* Set the resource data. */ + wl_resource_set_implementation (state_resource, &XIValuatorState_impl, + state, HandleXIValuatorStateDestroy); +} + + + +static void +TranslateTestButtons (struct wl_resource *resource, XIButtonState *buttons) +{ + TestXIButtonState *state; + + if (!resource) + { + /* Use default values if nothing was specified. */ + + buttons->mask_len = 0; + buttons->mask = NULL; + + return; + } + + /* The mask in buttons will be destroyed along with the resource! */ + state = wl_resource_get_user_data (resource); + buttons->mask_len = sizeof state->mask; + buttons->mask = state->mask; +} + +static void +TranslateTestValuators (struct wl_resource *resource, + XIValuatorState *valuators) +{ + TestXIValuatorState *state; + + if (!resource) + { + /* Use default values if nothing was specified. */ + valuators->mask_len = 0; + valuators->values = NULL; + valuators->mask = NULL; + + return; + } + + state = wl_resource_get_user_data (resource); + valuators->mask_len = state->mask_len; + valuators->mask = state->mask; + valuators->values = state->values; +} + +static void +TranslateTestModifiers (struct wl_resource *resource, + XIModifierState *modifiers) +{ + TestXIModifierState *state; + + if (!resource) + { + /* Use default values if nothing was specified. */ + modifiers->base = 0; + modifiers->latched = 0; + modifiers->locked = 0; + modifiers->effective = 0; + + return; + } + + state = wl_resource_get_user_data (resource); + modifiers->base = state->base; + modifiers->latched = state->latched; + modifiers->locked = state->locked; + modifiers->effective = state->effective; +} + +static void +DispatchTestEvent (TestSeatController *controller, Window window, + XIEvent *event) +{ + Surface *surface; + Subcompositor *subcompositor; + + /* Look up a test surface with the given window and dispatch the + event to it. */ + + surface = XLLookUpTestSurface (window, &subcompositor); + + if (!surface) + /* The client submitted an invalid event window! */ + return; + + if (event->evtype == XI_FocusIn) + DispatchFocusIn (surface, (XIFocusInEvent *) event); + else if (event->evtype == XI_FocusOut) + DispatchFocusOut (surface, (XIFocusOutEvent *) event); + else if (event->evtype == XI_Enter + || event->evtype == XI_Leave) + DispatchEntryExit (subcompositor, (XIEnterEvent *) event); + else if (event->evtype == XI_Motion) + DispatchMotion (subcompositor, (XIDeviceEvent *) event); + else if (event->evtype == XI_ButtonPress + || event->evtype == XI_ButtonRelease) + DispatchButton (subcompositor, (XIDeviceEvent *) event); + else if (event->evtype == XI_KeyPress + || event->evtype == XI_KeyRelease) + DispatchKey ((XIDeviceEvent *) event); + else if (event->evtype == XI_BarrierHit) + DispatchBarrierHit ((XIBarrierEvent *) event); + else if (event->evtype == XI_GesturePinchBegin + || event->evtype == XI_GesturePinchUpdate + || event->evtype == XI_GesturePinchEnd) + DispatchGesturePinch (subcompositor, (XIGesturePinchEvent *) event); + else if (event->evtype == XI_GestureSwipeBegin + || event->evtype == XI_GestureSwipeUpdate + || event->evtype == XI_GestureSwipeEnd) + DispatchGestureSwipe (subcompositor, (XIGestureSwipeEvent *) event); +} + +#define GenerateCrossingEvent(event_type, controller, test_event) \ + test_event.type = GenericEvent; \ + test_event.serial = request_serial_counter++; \ + test_event.send_event = True; \ + test_event.display = compositor.display; \ + test_event.extension = xi2_opcode; \ + test_event.evtype = event_type; \ + test_event.time = time; \ + test_event.deviceid = controller->seat->master_pointer; \ + test_event.sourceid = sourceid; \ + test_event.detail = detail; \ + test_event.root = root; \ + test_event.event = event; \ + test_event.child = child; \ + test_event.root_x = wl_fixed_to_double (root_x); \ + test_event.root_y = wl_fixed_to_double (root_y); \ + test_event.event_x = wl_fixed_to_double (event_x); \ + test_event.event_y = wl_fixed_to_double (event_y); \ + test_event.mode = mode; \ + test_event.focus = focus; \ + test_event.same_screen = same_screen; \ + TranslateTestButtons (buttons_resource, &test_event.buttons); \ + TranslateTestModifiers (mods_resource, &test_event.mods); \ + TranslateTestModifiers (group_resource, &test_event.group) + +static void +DispatchXIEnter (struct wl_client *client, struct wl_resource *resource, + uint32_t time, int32_t sourceid, int32_t detail, + uint32_t root, uint32_t event, uint32_t child, + wl_fixed_t root_x, wl_fixed_t root_y, + wl_fixed_t event_x, wl_fixed_t event_y, int32_t mode, + int32_t focus, int32_t same_screen, + struct wl_resource *buttons_resource, + struct wl_resource *mods_resource, + struct wl_resource *group_resource) +{ + TestSeatController *controller; + XIEnterEvent test_event; + + controller = wl_resource_get_user_data (resource); + GenerateCrossingEvent (XI_Enter, controller, test_event); + + /* Now dispatch the event. */ + DispatchTestEvent (controller, event, (XIEvent *) &test_event); +} + +static void +DispatchXILeave (struct wl_client *client, struct wl_resource *resource, + uint32_t time, int32_t sourceid, int32_t detail, + uint32_t root, uint32_t event, uint32_t child, + wl_fixed_t root_x, wl_fixed_t root_y, + wl_fixed_t event_x, wl_fixed_t event_y, int32_t mode, + int32_t focus, int32_t same_screen, + struct wl_resource *buttons_resource, + struct wl_resource *mods_resource, + struct wl_resource *group_resource) +{ + TestSeatController *controller; + XILeaveEvent test_event; + + controller = wl_resource_get_user_data (resource); + GenerateCrossingEvent (XI_Leave, controller, test_event); + + /* Now dispatch the event. */ + DispatchTestEvent (controller, event, (XIEvent *) &test_event); +} + +#define GenerateDeviceEvent(event_type, controller, test_event) \ + test_event.type = GenericEvent; \ + test_event.serial = request_serial_counter++; \ + test_event.send_event = True; \ + test_event.display = compositor.display; \ + test_event.extension = xi2_opcode; \ + test_event.evtype = event_type; \ + test_event.time = time; \ + test_event.deviceid = controller->seat->master_pointer; \ + test_event.sourceid = sourceid; \ + test_event.detail = detail; \ + test_event.root = root; \ + test_event.child = child; \ + test_event.event = event; \ + test_event.root_x = wl_fixed_to_double (root_x); \ + test_event.root_y = wl_fixed_to_double (root_y); \ + test_event.event_x = wl_fixed_to_double (event_x); \ + test_event.event_y = wl_fixed_to_double (event_y); \ + test_event.flags = flags; \ + TranslateTestButtons (buttons_resource, &test_event.buttons); \ + TranslateTestValuators (valuators_resource, &test_event.valuators); \ + TranslateTestModifiers (mods_resource, &test_event.mods); \ + TranslateTestModifiers (group_resource, &test_event.group) + +static void +DispatchXIMotion (struct wl_client *client, struct wl_resource *resource, + uint32_t time, int32_t sourceid, int32_t detail, + uint32_t root, uint32_t event, uint32_t child, + wl_fixed_t root_x, wl_fixed_t root_y, wl_fixed_t event_x, + wl_fixed_t event_y, int32_t flags, + struct wl_resource *buttons_resource, + struct wl_resource *valuators_resource, + struct wl_resource *mods_resource, + struct wl_resource *group_resource) +{ + TestSeatController *controller; + XIDeviceEvent test_event; + + controller = wl_resource_get_user_data (resource); + GenerateDeviceEvent (XI_Motion, controller, test_event); + + /* Now dispatch the event. */ + DispatchTestEvent (controller, event, (XIEvent *) &test_event); +} + +static void +DispatchXIButtonPress (struct wl_client *client, struct wl_resource *resource, + uint32_t time, int32_t sourceid, int32_t detail, + uint32_t root, uint32_t event, uint32_t child, + wl_fixed_t root_x, wl_fixed_t root_y, + wl_fixed_t event_x, wl_fixed_t event_y, int32_t flags, + struct wl_resource *buttons_resource, + struct wl_resource *valuators_resource, + struct wl_resource *mods_resource, + struct wl_resource *group_resource) +{ + TestSeatController *controller; + XIDeviceEvent test_event; + + controller = wl_resource_get_user_data (resource); + GenerateDeviceEvent (XI_ButtonPress, controller, test_event); + + /* Now dispatch the event. */ + DispatchTestEvent (controller, event, (XIEvent *) &test_event); +} + +static void +DispatchXIButtonRelease (struct wl_client *client, struct wl_resource *resource, + uint32_t time, int32_t sourceid, int32_t detail, + uint32_t root, uint32_t event, uint32_t child, + wl_fixed_t root_x, wl_fixed_t root_y, + wl_fixed_t event_x, wl_fixed_t event_y, int32_t flags, + struct wl_resource *buttons_resource, + struct wl_resource *valuators_resource, + struct wl_resource *mods_resource, + struct wl_resource *group_resource) +{ + TestSeatController *controller; + XIDeviceEvent test_event; + + controller = wl_resource_get_user_data (resource); + GenerateDeviceEvent (XI_ButtonRelease, controller, test_event); + + /* Now dispatch the event. */ + DispatchTestEvent (controller, event, (XIEvent *) &test_event); +} + +static const struct test_seat_controller_interface seat_controller_impl = + { + .destroy = DestroyTestSeatController, + .bind_seat = BindSeat, + .get_XIModifierState = GetXIModifierState, + .get_XIButtonState = GetXIButtonState, + .get_XIValuatorState = GetXIValuatorState, + .dispatch_XI_Enter = DispatchXIEnter, + .dispatch_XI_Leave = DispatchXILeave, + .dispatch_XI_Motion = DispatchXIMotion, + .dispatch_XI_ButtonPress = DispatchXIButtonPress, + .dispatch_XI_ButtonRelease = DispatchXIButtonRelease, + }; + +static void +HandleControllerResourceDestroy (struct wl_resource *resource) +{ + TestSeatController *controller; + Seat *seat; + + controller = wl_resource_get_user_data (resource); + seat = controller->seat; + + /* Make the seat inert and remove it from live_seats. */ + seat->flags |= IsInert; + + /* Set the focus surface to NULL, so surfaces don't mistakenly + treat themselves as still focused. */ + + SetFocusSurface (seat, NULL); + + /* Run destroy handlers. */ + + RunDestroyListeners (seat); + + /* Since the seat is now inert, remove it from the assoc + table and destroy the global. */ + + XLDeleteAssoc (seats, seat->master_keyboard); + XLDeleteAssoc (seats, seat->master_pointer); + + /* Also remove it from the list of live seats. */ + + live_seats = XLListRemove (live_seats, seat); + + /* Run and remove all resize completion callbacks. */ + + RunResizeDoneCallbacks (seat); + + /* And release the seat. */ + + ReleaseSeat (seat); + + /* Free the controller resource. */ + XLFree (controller); +} + + + +void +XLGetTestSeat (struct wl_client *client, struct wl_resource *resource, + uint32_t id) +{ + TestSeatController *controller; + Seat *seat; + char name_format[sizeof "test seat: " + 40]; + + controller = XLSafeMalloc (sizeof *controller); + + if (!controller) + { + wl_resource_post_no_memory (resource); + return; + } + + memset (controller, 0, sizeof *controller); + controller->resource + = wl_resource_create (client, &test_seat_controller_interface, + wl_resource_get_version (resource), id); + + if (!controller->resource) + { + wl_resource_post_no_memory (resource); + XLFree (controller); + return; + } + + seat = XLCalloc (1, sizeof *seat); + + /* Allocate a "device ID" for the seat. Device IDs are unsigned 16 + bit values, so any larger value is guaranteed to be okay for our + own use. */ + + if (!test_seat_counter) + test_seat_counter = 65555; + test_seat_counter++; + + /* Initialize some random bogus values. */ + seat->master_pointer = test_seat_counter; + seat->master_keyboard = test_seat_counter; + + /* Add a unique seat name. */ + sprintf (name_format, "test seat %u", test_seat_counter); + seat->name = XLStrdup (name_format); + + /* Refrain from creating a global for this seat. */ + seat->global = NULL; + + InitSeatCommon (seat); + + /* Associate the dummy device with the seat. */ + XLMakeAssoc (seats, test_seat_counter, seat); + seat->flags |= IsTestSeat; + + /* Add the seat to the live seat list. */ + live_seats = XLListPrepend (live_seats, seat); + + /* Retain the seat. */ + RetainSeat (seat); + controller->seat = seat; + + wl_resource_set_implementation (controller->resource, &seat_controller_impl, + controller, HandleControllerResourceDestroy); +} diff --git a/tests/seat_test.c b/tests/seat_test.c new file mode 100644 index 0000000..5a74081 --- /dev/null +++ b/tests/seat_test.c @@ -0,0 +1,480 @@ +/* Tests for the Wayland compositor running on the X server. + +Copyright (C) 2022 to various contributors. + +This file is part of 12to11. + +12to11 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 3 of the License, or (at your +option) any later version. + +12to11 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 12to11. If not, see . */ + +#include "test_harness.h" + +#include + +enum test_expect_event_kind + { + POINTER_ENTER_EVENT, + POINTER_FRAME_EVENT, + POINTER_MOTION_EVENT, + }; + +struct test_expect_data +{ + /* The coordinates of the event. */ + double x, y; + + /* What kind of event is being waited for. */ + enum test_expect_event_kind kind; + + /* Whether or not the expected event arrived. */ + bool arrived; +}; + +enum test_kind + { + MAP_WINDOW_KIND, + TEST_ENTRY_KIND, + }; + +static const char *test_names[] = + { + "map_window", + "test_entry", + }; + +#define LAST_TEST TEST_ENTRY_KIND +#define TEST_SOURCE_DEVICE 4500000 + +/* The display. */ +static struct test_display *display; + +/* Test interfaces. */ +static struct test_interface test_interfaces[] = + { + /* No interfaces yet. */ + }; + +/* The test surface window. */ +static Window test_surface_window; + +/* The test surface and Wayland surface. */ +static struct test_surface *test_surface; +static struct wl_surface *wayland_surface; + +/* How many elements are in the current listener data. */ +static int num_listener_data; + +/* The current listener data. */ +struct test_expect_data *current_listener_data; + + + +/* Forward declarations. */ +static void submit_surface_damage (struct wl_surface *, int, int, int, int); +static void expect_surface_enter (double, double); +static void expect_surface_motion (double, double); + + + +/* Get a timestamp suitable for use in events dispatched to the test + seat. */ + +static uint32_t +test_get_time (void) +{ + struct timespec timespec; + + clock_gettime (CLOCK_MONOTONIC, ×pec); + + return (timespec.tv_sec * 1000 + + timespec.tv_nsec / 1000000); +} + +/* Get the root window. */ + +static Window +test_get_root (void) +{ + return DefaultRootWindow (display->x_display); +} + + + +static void +test_single_step (enum test_kind kind) +{ + struct wl_buffer *buffer; + + test_log ("running test step: %s", test_names[kind]); + + switch (kind) + { + case MAP_WINDOW_KIND: + buffer = load_png_image (display, "seat_test.png"); + + if (!buffer) + report_test_failure ("failed to load seat_test.png"); + + wl_surface_attach (wayland_surface, buffer, 0, 0); + submit_surface_damage (wayland_surface, 0, 0, 500, 500); + wl_surface_commit (wayland_surface); + wl_buffer_destroy (buffer); + break; + + case TEST_ENTRY_KIND: + /* Enter the 500x500 window. The window is at 0, 0 relative to + the root window. */ + test_seat_controller_dispatch_XI_Enter (display->seat->controller, + test_get_time (), + TEST_SOURCE_DEVICE, + XINotifyAncestor, + test_get_root (), + test_surface_window, + None, + wl_fixed_from_double (0.0), + wl_fixed_from_double (0.0), + wl_fixed_from_double (0.0), + wl_fixed_from_double (0.0), + XINotifyNormal, + False, True, NULL, NULL, + NULL); + + /* Expect an enter at 0.0 by 0.0. */ + expect_surface_enter (0.0, 0.0); + + /* Now move the mouse a little. */ + test_seat_controller_dispatch_XI_Motion (display->seat->controller, + test_get_time (), + TEST_SOURCE_DEVICE, + 0, + test_get_root (), + test_surface_window, + None, + wl_fixed_from_double (1.0), + wl_fixed_from_double (2.0), + wl_fixed_from_double (1.0), + wl_fixed_from_double (2.0), + 0, + NULL, NULL, NULL, NULL); + + /* Expect mouse motion at the specified coordinates. */ + expect_surface_motion (1.0, 2.0); + break; + } + + if (kind == LAST_TEST) + test_complete (); +} + + + +static void +expect_surface_enter (double x, double y) +{ + struct test_expect_data data[2]; + + test_log ("waiting for enter at %g, %g", x, y); + + memset (data, 0, sizeof data); + + data[0].x = x; + data[0].y = y; + data[0].kind = POINTER_ENTER_EVENT; + data[1].kind = POINTER_FRAME_EVENT; + + /* Set the current listener data and do a roundtrip. */ + current_listener_data = data; + num_listener_data = 2; + + wl_display_roundtrip (display->display); + current_listener_data = NULL; + num_listener_data = 0; + + /* See whether or not the event arrived. */ + if (!data[0].arrived || !data[1].arrived) + report_test_failure ("expected events did not arrive"); + else + test_log ("received enter followed by frame"); +} + +static void +expect_surface_motion (double x, double y) +{ + struct test_expect_data data[2]; + + test_log ("waiting for motion at %g, %g", x, y); + + memset (data, 0, sizeof data); + + data[0].x = x; + data[0].y = y; + data[0].kind = POINTER_MOTION_EVENT; + data[1].kind = POINTER_FRAME_EVENT; + + /* Set the current listener data and do a roundtrip. */ + current_listener_data = data; + num_listener_data = 2; + + wl_display_roundtrip (display->display); + current_listener_data = NULL; + num_listener_data = 0; + + /* See whether or not the event arrived. */ + if (!data[0].arrived || !data[1].arrived) + report_test_failure ("expected events did not arrive"); + else + test_log ("received motion followed by frame"); +} + + + +static void +handle_test_surface_mapped (void *data, struct test_surface *surface, + uint32_t xid, const char *display_string) +{ + /* Sleep for 1 second to ensure that the window is exposed and + redirected. */ + sleep (1); + + /* Start the test. */ + test_surface_window = xid; + + /* Run the test again. */ + test_single_step (TEST_ENTRY_KIND); +} + +static const struct test_surface_listener test_surface_listener = + { + handle_test_surface_mapped, + }; + + + +/* Obtain the next test data record. The events arriving are checked + to be in the order in which they arrive in + current_listener_data. */ + +static struct test_expect_data * +get_next_expect_data (void) +{ + struct test_expect_data *data; + int i; + + data = current_listener_data; + + for (i = 0; i < num_listener_data; ++i) + { + if (current_listener_data[i].arrived) + continue; + + return ¤t_listener_data[i]; + } + + return NULL; +} + +static void +handle_pointer_enter (void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t surface_x, wl_fixed_t surface_y) +{ + struct test_expect_data *test_data; + + test_data = get_next_expect_data (); + + if (!test_data) + { + test_log ("ignored enter event at %g %g", + wl_fixed_to_double (surface_x), + wl_fixed_to_double (surface_y)); + return; + } + + if (test_data->kind != POINTER_ENTER_EVENT) + return; + + test_log ("got enter event at %g, %g", + wl_fixed_to_double (surface_x), + wl_fixed_to_double (surface_y)); + + if (test_data->x == wl_fixed_to_double (surface_x) + && test_data->y == wl_fixed_to_double (surface_y)) + test_data->arrived = true; + else + report_test_failure ("missed enter event at %g %g", + test_data->x, test_data->y); +} + +static void +handle_pointer_leave (void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface) +{ + /* ... */ +} + +static void +handle_pointer_motion (void *data, struct wl_pointer *wl_pointer, + uint32_t time, wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + struct test_expect_data *test_data; + + test_data = get_next_expect_data (); + + if (!test_data) + { + test_log ("ignored motion event at %g %g", + wl_fixed_to_double (surface_x), + wl_fixed_to_double (surface_y)); + return; + } + + if (test_data->kind != POINTER_MOTION_EVENT) + return; + + test_log ("got motion event at %g, %g", + wl_fixed_to_double (surface_x), + wl_fixed_to_double (surface_y)); + + if (test_data->x == wl_fixed_to_double (surface_x) + && test_data->y == wl_fixed_to_double (surface_y)) + test_data->arrived = true; + else + report_test_failure ("missed motion event at %g %g", + test_data->x, test_data->y); +} + +static void +handle_pointer_button (void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, + uint32_t state) +{ + /* TODO... */ +} + +static void +handle_pointer_axis (void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) +{ + /* TODO... */ +} + +static void +handle_pointer_frame (void *data, struct wl_pointer *wl_pointer) +{ + struct test_expect_data *test_data; + + test_data = get_next_expect_data (); + + if (!test_data) + { + test_log ("ignored frame event"); + return; + } + + if (test_data->kind != POINTER_FRAME_EVENT) + return; + + test_log ("got frame event"); + test_data->arrived = true; +} + +static void +handle_pointer_axis_source (void *data, struct wl_pointer *wl_pointer, + uint32_t axis_source) +{ + /* TODO... */ +} + +static void +handle_pointer_axis_stop (void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis) +{ + /* TODO... */ +} + +static void +handle_pointer_axis_discrete (void *data, struct wl_pointer *wl_pointer, + uint32_t axis, int32_t discrete) +{ + /* TODO... */ +} + +static void +handle_pointer_axis_value120 (void *data, struct wl_pointer *wl_pointer, + uint32_t axis, int32_t value120) +{ + /* TODO... */ +} + +static const struct wl_pointer_listener pointer_listener = + { + handle_pointer_enter, + handle_pointer_leave, + handle_pointer_motion, + handle_pointer_button, + handle_pointer_axis, + handle_pointer_frame, + handle_pointer_axis_source, + handle_pointer_axis_stop, + handle_pointer_axis_discrete, + handle_pointer_axis_value120, + }; + + + +static void +submit_surface_damage (struct wl_surface *surface, int x, int y, int width, + int height) +{ + test_log ("damaging surface by %d, %d, %d, %d", x, y, width, + height); + + wl_surface_damage (surface, x, y, width, height); +} + +static void +run_test (void) +{ + if (!make_test_surface (display, &wayland_surface, + &test_surface)) + report_test_failure ("failed to create test surface"); + + test_surface_add_listener (test_surface, &test_surface_listener, + NULL); + test_single_step (MAP_WINDOW_KIND); + + /* Initialize the pointer listener. */ + wl_pointer_add_listener (display->seat->pointer, &pointer_listener, + NULL); + + while (true) + { + if (wl_display_dispatch (display->display) == -1) + die ("wl_display_dispatch"); + } +} + +int +main (void) +{ + test_init (); + display = open_test_display (test_interfaces, + ARRAYELTS (test_interfaces)); + + if (!display) + report_test_failure ("failed to open display"); + + test_init_seat (display); + run_test (); +} diff --git a/tests/seat_test.png b/tests/seat_test.png new file mode 100644 index 0000000..ce46f94 Binary files /dev/null and b/tests/seat_test.png differ