From 163dca96bd7b2d5891f1208cdf02b51ce9eea327 Mon Sep 17 00:00:00 2001 From: hujianwei Date: Thu, 10 Nov 2022 06:14:51 +0000 Subject: [PATCH] Add scroll valuator tests * 12to11-test.xml (test_manager): : New errors. (test_XIDeviceInfo, test_device_controller): New interfaces. (test_seat_controller): : New request. : New event. * seat.c (NoticeDeviceDisabled): Ignore test seats. (XLSeatDispatchCoreKeyEvent): Expand commentary. * test_seat.c (struct _TestDeviceController) (struct _TestXIDeviceInfo): New structs. (DestroyXIValuatorState): New function. (AddValuatorToTestXIValuatorState): Fix crashes. (XIValuatorState_impl): Add DestroyXIValuatorState. (DestroyDeviceInfo, SetDeviceId, SetName, SetUse, SetAttachment) (SetEnabled, AddXIScrollClassInfo, AddXIValuatorClassInfo): New functions. (XIDeviceInfo_impl): New interface implementation. (HandleXIDeviceInfoDestroy): New function. (DestroyDeviceController, AddDeviceInfo, GetDeviceInfo): New functions. (device_controller_impl): New interface. (HandleTestDeviceControllerDestroy): New function. (GetDeviceController): New function. (seat_controller_impl): Add it. (XLGetTestSeat): Send new `device_id' event. * tests/seat_test.c (enum test_expect_event_kind): Add POINTER_AXIS_VALUE120_EVENT. (struct test_recorded_axis_value120_event): New event kind. (enum test_kind): Add TEST_VALUATOR_KIND. (test_names): Add test_valuator. (LAST_TEST): Set to TEST_VALUATOR_KIND. (run_valuator_test): New function. (test_single_step): Implement accordingly. (expect_axis_value120_event, handle_pointer_axis_value120): New functions. * tests/test_harness.c (handle_seat_controller_device_id): New function. (seat_controller_listener): New listener. (test_init_seat): Wait for device ID and init device controller. * tests/test_harness.h (struct test_seat): Add device controller and device ID fields. --- 12to11-test.xml | 168 ++++++++++++++++ seat.c | 16 +- test_seat.c | 453 ++++++++++++++++++++++++++++++++++++++++++- tests/seat_test.c | 325 ++++++++++++++++++++++++++++++- tests/test_harness.c | 36 ++++ tests/test_harness.h | 6 + 6 files changed, 992 insertions(+), 12 deletions(-) diff --git a/12to11-test.xml b/12to11-test.xml index 30e7bec..fab139b 100644 --- a/12to11-test.xml +++ b/12to11-test.xml @@ -45,6 +45,14 @@ summary="an invalid version was specified"/> + + + + @@ -239,12 +247,156 @@ + + + Simulated XInput 2 device information for a device. + + + + + Destroy the test device info resource. + + + + + + Set the device ID of this device info structure to the + specified ID. If the ID is less than 65536, post an + invalid_device_id error. + + + + + + + + Set the name of this device to the specified name. + + + + + + + + Set the use of this device to the specified value. + + + + + + + + Set the device attachment to the master pointer and keyboard + of the specified seat controller. + + + + + + + + Set whether or not the device is enabled. + + + + + + + + Add a scroll class with the specified type, source ID, number, + scroll type, increment and flags to the device info. + + If the specified source device ID is less than 65536, post an + invalid_device_id error. + + + + + + + + + + + + Add a valuator class with the specified type, source ID, + number, label, min, max, value, resolution and mode to the + device info. + + If the specified source device ID is less than 65536, post an + invalid_device_id error. + + Intern the label into an atom. If the string is empty, raise + an invalid_label error. + + + + + + + + + + + + + + + + This object allows registering a list of artificial input + devices to use during tests. These devices must have a device + ID larger than 65535, and are removed upon destruction of the + test controller resource. + + + + + Destroy the test device controller resource. + + + + + + Register a device with the specified device information. If + the device info is incomplete, meaning that one of + set_device_id, set_name, set_use, set_attachment, or + set_enabled was not called after it was created, post an + incomplete_device_info error. If the device ID already + exists, post a device_exists error if the device was not + created by this device controller, or update the device with + the new information if it was. + + If a test seat is created with the device ID in the specified + device info after add_device_info is called, then the seat + will not possess this device info. + + The device will be removed upon destruction of the + test_device_controller resource. + + + + + + + + Create a device information object. The device information + object is not associated with the test controller in any way. + + + + + + This object extends a wl_seat created with the test_manager.get_test_seat request with several requests to dispatch generated events. + Immediately after being created, a device_id event is sent with + the device ID of this seat. + If the seat associated with the test controller is destroyed by the time a request is made with the test controller, the latter request is simply ignored. @@ -420,5 +572,21 @@ + + + + Obtain a new device controller. + + + + + + + + This event is sent immediately after the test_seat_controller + is created, with the device ID of the test seat. + + + diff --git a/seat.c b/seat.c index f664111..2fecdd3 100644 --- a/seat.c +++ b/seat.c @@ -95,6 +95,7 @@ enum IsInPinchGesture = (1 << 9), IsInSwipeGesture = (1 << 10), IsTestSeat = (1 << 11), + IsTestDeviceSpecified = (1 << 12), }; enum @@ -2189,7 +2190,9 @@ NoticeDeviceDisabled (int deviceid) seat = XLLookUpAssoc (seats, deviceid); - if (seat) + /* Test seats should not be destroyed here. */ + + if (seat && !(seat->flags & IsTestSeat)) { /* The device has been disabled, mark the seat inert and dereference it. The seat is still referred to by the @@ -6181,13 +6184,16 @@ XLSeatDispatchCoreKeyEvent (Seat *seat, Surface *surface, XEvent *event, if (keysym) { /* If looking up the event keycode also results in the keysym, - then just use the keycode specified in the event. */ + then just use the keycode specified in the event. This is + because French keyboard layouts have multiple keycodes that + decode to the same keysym, which causes problems later on + when Wayland clients keep repeating the "a" key, as a keysym + was looked up for the key press but not for the corresponding + key release. */ if (XkbLookupKeySym (compositor.display, event->xkey.keycode, event->xkey.state, &mods_return, &sym_return) && keysym == sym_return) - { - keycode = event->xkey.keycode; - } + keycode = event->xkey.keycode; else keycode = XKeysymToKeycode (compositor.display, keysym); diff --git a/test_seat.c b/test_seat.c index ebcf1e0..6d2f99a 100644 --- a/test_seat.c +++ b/test_seat.c @@ -23,9 +23,11 @@ along with 12to11. If not, see . */ have to include anything itself! */ typedef struct _TestSeatController TestSeatController; +typedef struct _TestDeviceController TestDeviceController; typedef struct _TestXIModifierState TestXIModifierState; typedef struct _TestXIValuatorState TestXIValuatorState; typedef struct _TestXIButtonState TestXIButtonState; +typedef struct _TestXIDeviceInfo TestXIDeviceInfo; /* The current test seat counter. */ static unsigned int test_seat_counter; @@ -42,6 +44,18 @@ struct _TestSeatController struct wl_resource *resource; }; +struct _TestDeviceController +{ + /* The associated struct wl_resource. */ + struct wl_resource *resource; + + /* Array of device IDs used by this test device controller. */ + int *device_ids; + + /* Number of device IDs associated with this controller. */ + int num_ids; +}; + struct _TestXIModifierState { /* Modifier state. These fields mean the same as they do in @@ -73,6 +87,43 @@ struct _TestXIButtonState unsigned char mask[XIMaskLen (8)]; }; +enum + { + StateDeviceIdSet = 1, + StateNameSet = 1 << 1, + StateUseSet = 1 << 2, + StateAttachmentSet = 1 << 3, + StateEnabledSet = 1 << 4, + StateComplete = 0x1f, + }; + +struct _TestXIDeviceInfo +{ + /* The associated resource. */ + struct wl_resource *resource; + + /* The device name. */ + char *name; + + /* Array of classes. */ + XIAnyClassInfo **classes; + + /* The device ID. */ + int device_id; + + /* The use, attachment. */ + int use, attachment; + + /* Whether or not the device is enabled. */ + Bool enabled; + + /* The number of classes there are. */ + int num_classes; + + /* How many fields are set. */ + int state; +}; + static void @@ -168,6 +219,13 @@ HandleXIButtonStateDestroy (struct wl_resource *resource) +static void +DestroyXIValuatorState (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + static void AddValuatorToTestXIValuatorState (struct wl_client *client, struct wl_resource *resource, @@ -200,7 +258,7 @@ AddValuatorToTestXIValuatorState (struct wl_client *client, if (state->mask_len < XIMaskLen (valuator)) { state->mask = XLRealloc (state->mask, - state->mask_len); + XIMaskLen (valuator)); /* Clear the newly allocated part of the mask. */ memset (state->mask + state->mask_len, @@ -221,7 +279,7 @@ AddValuatorToTestXIValuatorState (struct wl_client *client, if (i == valuator) /* Insert the new value. */ new_values[j++] = wl_fixed_to_double (value); - else if (XIMaskIsSet (state->mask, valuator)) + else if (XIMaskIsSet (state->mask, i)) /* Use the old value. */ new_values[j++] = *old_values++; } @@ -238,6 +296,7 @@ AddValuatorToTestXIValuatorState (struct wl_client *client, static const struct test_XIValuatorState_interface XIValuatorState_impl = { + .destroy = DestroyXIValuatorState, .add_valuator = AddValuatorToTestXIValuatorState, }; @@ -261,8 +320,355 @@ HandleXIValuatorStateDestroy (struct wl_resource *resource) static void -DestroyTestSeatController (struct wl_client *client, - struct wl_resource *resource) +DestroyDeviceInfo (struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +SetDeviceId (struct wl_client *client, struct wl_resource *resource, + uint32_t device_id) +{ + TestXIDeviceInfo *info; + + info = wl_resource_get_user_data (resource); + + if (device_id < 65536) + { + wl_resource_post_error (resource, TEST_MANAGER_ERROR_INVALID_DEVICE_ID, + "invalid device id specified"); + return; + } + + info->device_id = device_id; + info->state |= StateDeviceIdSet; +} + +static void +SetName (struct wl_client *client, struct wl_resource *resource, + const char *name) +{ + TestXIDeviceInfo *info; + + info = wl_resource_get_user_data (resource); + + if (info->name) + XLFree (info->name); + info->name = XLStrdup (name); + info->state |= StateNameSet; +} + +static void +SetUse (struct wl_client *client, struct wl_resource *resource, + int32_t use) +{ + TestXIDeviceInfo *info; + + info = wl_resource_get_user_data (resource); + info->use = use; + info->state |= StateUseSet; +} + +static void +SetAttachment (struct wl_client *client, struct wl_resource *resource, + struct wl_resource *attachment_resource) +{ + TestSeatController *controller; + TestXIDeviceInfo *info; + + controller = wl_resource_get_user_data (attachment_resource); + info = wl_resource_get_user_data (resource); + + info->attachment = controller->seat->master_pointer; + info->state |= StateAttachmentSet; +} + +static void +SetEnabled (struct wl_client *client, struct wl_resource *resource, + uint32_t enabled) +{ + TestXIDeviceInfo *info; + + info = wl_resource_get_user_data (resource); + + if (enabled) + info->enabled = True; + else + info->enabled = False; + info->state |= StateEnabledSet; +} + +static void +AddXIScrollClassInfo (struct wl_client *client, struct wl_resource *resource, + int32_t sourceid, int32_t number, int32_t scroll_type, + wl_fixed_t increment, int32_t flags) +{ + TestXIDeviceInfo *info; + XIScrollClassInfo *class; + + if (sourceid < 65536) + { + wl_resource_post_error (resource, TEST_MANAGER_ERROR_INVALID_DEVICE_ID, + "invalid device ID specified"); + return; + } + + info = wl_resource_get_user_data (resource); + + class = XLMalloc (sizeof *class); + class->type = XIScrollClass; + class->sourceid = sourceid; + class->number = number; + class->scroll_type = scroll_type; + class->increment = wl_fixed_to_double (increment); + class->flags = flags; + + /* Extend info->classes to hold more classes. */ + info->num_classes++; + info->classes = XLRealloc (info->classes, + sizeof *info->classes * info->num_classes); + + /* Attach the class. */ + info->classes[info->num_classes - 1] = (XIAnyClassInfo *) class; +} + +static void +AddXIValuatorClassInfo (struct wl_client *client, struct wl_resource *resource, + int32_t sourceid, int32_t number, const char *label, + wl_fixed_t min, wl_fixed_t max, wl_fixed_t value, + int32_t resolution, int32_t mode) +{ + TestXIDeviceInfo *info; + XIValuatorClassInfo *class; + + if (sourceid < 65536) + { + wl_resource_post_error (resource, TEST_MANAGER_ERROR_INVALID_DEVICE_ID, + "invalid device ID specified"); + return; + } + + /* Avoid interning empty strings. */ + + if (!strlen (label)) + { + wl_resource_post_error (resource, TEST_MANAGER_ERROR_INVALID_LABEL, + "the specified label is invalid"); + return; + } + + info = wl_resource_get_user_data (resource); + class = XLMalloc (sizeof *class); + class->type = XIValuatorClass; + class->sourceid = sourceid; + class->number = number; + class->label = InternAtom (label); + class->min = wl_fixed_to_double (min); + class->max = wl_fixed_to_double (max); + class->value = wl_fixed_to_double (value); + class->resolution = resolution; + class->mode = mode; + + /* Extend info->classes to hold more classes. */ + info->num_classes++; + info->classes = XLRealloc (info->classes, + sizeof *info->classes * info->num_classes); + + /* Attach the class. */ + info->classes[info->num_classes - 1] = (XIAnyClassInfo *) class; +} + +static const struct test_XIDeviceInfo_interface XIDeviceInfo_impl = + { + .destroy = DestroyDeviceInfo, + .set_device_id = SetDeviceId, + .set_name = SetName, + .set_use = SetUse, + .set_attachment = SetAttachment, + .set_enabled = SetEnabled, + .add_XIScrollClassInfo = AddXIScrollClassInfo, + .add_XIValuatorClassInfo = AddXIValuatorClassInfo, + }; + +static void +HandleXIDeviceInfoDestroy (struct wl_resource *resource) +{ + TestXIDeviceInfo *info; + int i; + + info = wl_resource_get_user_data (resource); + + /* Free the name. */ + XLFree (info->name); + + /* Free each of the classes. */ + for (i = 0; i < info->num_classes; ++i) + XLFree (info->classes[i]); + + /* Free the class array. */ + XLFree (info->classes); + + /* Free the info itself. */ + XLFree (info); +} + + + +static void +DestroyDeviceController (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +AddDeviceInfo (struct wl_client *client, struct wl_resource *resource, + struct wl_resource *device_info) +{ + TestDeviceController *controller; + TestXIDeviceInfo *info; + Seat *seat; + DeviceInfo *deviceinfo; + int i; + XIDeviceInfo test_info; + + /* Add a virtual device to the device controller. */ + controller = wl_resource_get_user_data (resource); + info = wl_resource_get_user_data (device_info); + + /* First, ensure that the device info is completely specified. */ + + if ((info->state & StateComplete) != StateComplete) + { + wl_resource_post_error (resource, + TEST_MANAGER_ERROR_INCOMPLETE_DEVICE_INFO, + "the specified device information was not" + " completely specified"); + return; + } + + /* Next, check whether or not a device already exists. */ + seat = XLLookUpAssoc (seats, info->device_id); + deviceinfo = XLLookUpAssoc (devices, info->device_id); + + if ((seat && seat->flags & IsTestDeviceSpecified) || deviceinfo) + { + /* If a device already exists, see whether or not it was created + by this test device controller. */ + + for (i = 0; i < controller->num_ids; ++i) + { + if (controller->device_ids[i] == info->device_id) + /* It was created by this controller. Simply update the + values. */ + goto continue_update; + } + + /* Otherwise, post an error. */ + wl_resource_post_error (resource, + TEST_MANAGER_ERROR_DEVICE_EXISTS, + "the device %d already exists, and was " + "not created by this controller", + info->device_id); + return; + } + + continue_update: + + /* Now, construct the XIDeviceInfo. */ + test_info.deviceid = info->device_id; + test_info.name = info->name; + test_info.use = info->use; + test_info.attachment = info->attachment; + test_info.enabled = info->enabled; + test_info.num_classes = info->num_classes; + test_info.classes = info->classes; + + /* If the seat exists, repopulate its valuators with that specified + in the device info. */ + + if (seat) + { + FreeValuators (seat); + UpdateValuators (seat, &test_info); + + /* Next, set a flag that means the seat has its information + provided by device info. */ + seat->flags |= IsTestDeviceSpecified; + } + + /* Now, record the device info. */ + RecordDeviceInformation (&test_info); +} + +static void +GetDeviceInfo (struct wl_client *client, struct wl_resource *resource, + uint32_t id) +{ + TestXIDeviceInfo *info; + + info = XLSafeMalloc (sizeof *info); + + if (!info) + { + wl_resource_post_no_memory (resource); + return; + } + + memset (info, 0, sizeof *info); + info->resource + = wl_resource_create (client, &test_XIDeviceInfo_interface, + wl_resource_get_version (resource), id); + + if (!info->resource) + { + XLFree (info); + return; + } + + wl_resource_set_implementation (info->resource, &XIDeviceInfo_impl, + info, HandleXIDeviceInfoDestroy); +} + +static const struct test_device_controller_interface device_controller_impl = + { + .destroy = DestroyDeviceController, + .add_device_info = AddDeviceInfo, + .get_device_info = GetDeviceInfo, + }; + +static void +HandleTestDeviceControllerDestroy (struct wl_resource *resource) +{ + TestDeviceController *controller; + int i; + Seat *seat; + + controller = wl_resource_get_user_data (resource); + + /* Remove each device associated with the device controller. */ + for (i = 0; i < controller->num_ids; ++i) + { + NoticeDeviceDisabled (controller->device_ids[i]); + + /* NoticeDeviceDisabled is special-cased to not free valuators + for test seats. If there is a seat associated with this + device ID, free the valuators on it as well. */ + seat = XLLookUpAssoc (seats, controller->device_ids[i]); + FreeValuators (seat); + + /* Clear the test device specified flag. */ + seat->flags &= ~IsTestDeviceSpecified; + } + + XLFree (controller); +} + + + +static void +DestroySeatController (struct wl_client *client, + struct wl_resource *resource) { wl_resource_destroy (resource); } @@ -664,9 +1070,41 @@ DispatchXIButtonRelease (struct wl_client *client, struct wl_resource *resource, DispatchTestEvent (controller, event, (XIEvent *) &test_event); } +static void +GetDeviceController (struct wl_client *client, struct wl_resource *resource, + uint32_t id) +{ + TestDeviceController *controller; + + 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_device_controller_interface, + wl_resource_get_version (resource), id); + + if (!controller->resource) + { + XLFree (controller); + wl_resource_post_no_memory (resource); + return; + } + + wl_resource_set_implementation (controller->resource, + &device_controller_impl, + controller, + HandleTestDeviceControllerDestroy); +} + static const struct test_seat_controller_interface seat_controller_impl = { - .destroy = DestroyTestSeatController, + .destroy = DestroySeatController, .bind_seat = BindSeat, .get_XIModifierState = GetXIModifierState, .get_XIButtonState = GetXIButtonState, @@ -676,6 +1114,7 @@ static const struct test_seat_controller_interface seat_controller_impl = .dispatch_XI_Motion = DispatchXIMotion, .dispatch_XI_ButtonPress = DispatchXIButtonPress, .dispatch_XI_ButtonRelease = DispatchXIButtonRelease, + .get_device_controller = GetDeviceController, }; static void @@ -787,4 +1226,8 @@ XLGetTestSeat (struct wl_client *client, struct wl_resource *resource, wl_resource_set_implementation (controller->resource, &seat_controller_impl, controller, HandleControllerResourceDestroy); + + /* Send the device ID to the client. */ + test_seat_controller_send_device_id (controller->resource, + seat->master_pointer); } diff --git a/tests/seat_test.c b/tests/seat_test.c index fe4c78d..6502866 100644 --- a/tests/seat_test.c +++ b/tests/seat_test.c @@ -29,6 +29,7 @@ enum test_expect_event_kind POINTER_MOTION_EVENT, POINTER_LEAVE_EVENT, POINTER_BUTTON_EVENT, + POINTER_AXIS_VALUE120_EVENT, }; struct test_recorded_event @@ -82,6 +83,18 @@ struct test_recorded_button_event uint32_t button, state; }; +struct test_recorded_axis_value120_event +{ + /* The event header. */ + struct test_recorded_event header; + + /* The axis. */ + uint32_t axis; + + /* The value120. */ + int32_t value120; +}; + struct test_subsurface { /* The subsurface itself. */ @@ -97,6 +110,7 @@ enum test_kind TEST_ENTRY_KIND, TEST_CLICK_KIND, TEST_GRAB_KIND, + TEST_VALUATOR_KIND, }; static const char *test_names[] = @@ -105,9 +119,10 @@ static const char *test_names[] = "test_entry", "test_click", "test_grab", + "test_valuator", }; -#define LAST_TEST TEST_GRAB_KIND +#define LAST_TEST TEST_VALUATOR_KIND #define TEST_SOURCE_DEVICE 4500000 /* The display. */ @@ -148,6 +163,7 @@ static void expect_enter_event (struct wl_surface *, double, double); static void expect_motion_event (double, double); static void expect_leave_event (void); static void expect_button_event (int, int); +static void expect_axis_value120_event (uint32_t, int32_t); static void expect_no_events (void); @@ -466,6 +482,159 @@ run_grab_test (struct test_XIButtonState *button_state, expect_enter_event (wayland_surface, 0.0, 0.0); } +static void +run_valuator_test (void) +{ + struct test_XIDeviceInfo *info; + struct test_XIValuatorState *valuator_state; + + /* First, create the device info. */ + info + = test_device_controller_get_device_info (display->seat->device_controller); + + /* Send the first leave event. */ + test_seat_controller_dispatch_XI_Leave (display->seat->controller, + test_get_time (), + TEST_SOURCE_DEVICE, + XINotifyAncestor, + test_get_root (), + test_surface_window, + None, + wl_fixed_from_double (-1.0), + wl_fixed_from_double (-1.0), + wl_fixed_from_double (-1.0), + wl_fixed_from_double (-1.0), + XINotifyNormal, + False, True, + NULL, NULL, NULL); + + /* Set the device ID and add the valuators. */ + test_XIDeviceInfo_set_device_id (info, display->seat->device_id); + test_XIDeviceInfo_set_use (info, XIMasterPointer); + test_XIDeviceInfo_set_attachment (info, display->seat->controller); + test_XIDeviceInfo_set_name (info, "Test virtual pointer"); + test_XIDeviceInfo_set_enabled (info, 1); + test_XIDeviceInfo_add_XIScrollClassInfo (info, + TEST_SOURCE_DEVICE, + 1, + XIScrollTypeVertical, + wl_fixed_from_double (1.0), + XIScrollFlagPreferred); + test_XIDeviceInfo_add_XIScrollClassInfo (info, + TEST_SOURCE_DEVICE, + 2, + XIScrollTypeHorizontal, + wl_fixed_from_double (2.0), + XIScrollFlagPreferred); + test_XIDeviceInfo_add_XIValuatorClassInfo (info, + TEST_SOURCE_DEVICE, + 1, + "Rel Scroll Vertical", + wl_fixed_from_double (0.0), + wl_fixed_from_double (0.0), + wl_fixed_from_double (0.0), + 1, + XIModeRelative); + test_XIDeviceInfo_add_XIValuatorClassInfo (info, + TEST_SOURCE_DEVICE, + 2, + "Rel Scroll Horizontal", + wl_fixed_from_double (0.0), + wl_fixed_from_double (0.0), + wl_fixed_from_double (0.0), + 1, + XIModeRelative); + test_device_controller_add_device_info (display->seat->device_controller, + info); + test_XIDeviceInfo_destroy (info); + + /* Dispatch the first entry event. */ + 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 (1.0), + wl_fixed_from_double (1.0), + wl_fixed_from_double (1.0), + wl_fixed_from_double (1.0), + XINotifyNormal, + False, True, NULL, NULL, + NULL); + + /* Create the valuator state. */ + valuator_state + = test_seat_controller_get_XIValuatorState (display->seat->controller); + + if (!valuator_state) + report_test_failure ("failed to create valuator state"); + + test_XIValuatorState_add_valuator (valuator_state, 1, + wl_fixed_from_double (1.0)); + test_XIValuatorState_add_valuator (valuator_state, 2, + wl_fixed_from_double (1.0)); + + /* Dispatch the first motion event. */ + 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 (2.0), + wl_fixed_from_double (2.0), + wl_fixed_from_double (2.0), + wl_fixed_from_double (2.0), + 0, + NULL, + valuator_state, + NULL, NULL); + test_XIValuatorState_destroy (valuator_state); + + /* Dispatch the second motion event. */ + valuator_state + = test_seat_controller_get_XIValuatorState (display->seat->controller); + + if (!valuator_state) + report_test_failure ("failed to create valuator state"); + + test_XIValuatorState_add_valuator (valuator_state, 1, + wl_fixed_from_double (1.1)); + test_XIValuatorState_add_valuator (valuator_state, 2, + wl_fixed_from_double (2.6)); + 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 (2.0), + wl_fixed_from_double (2.0), + wl_fixed_from_double (2.0), + wl_fixed_from_double (2.0), + 0, + NULL, + valuator_state, + NULL, NULL); + test_XIValuatorState_destroy (valuator_state); + + /* Now, verify the events that arrive. */ + record_events (); + expect_frame_event (); + expect_axis_value120_event (WL_POINTER_AXIS_VERTICAL_SCROLL, 12); + expect_axis_value120_event (WL_POINTER_AXIS_HORIZONTAL_SCROLL, 96); + expect_frame_event (); + expect_motion_event (2.0, 2.0); + expect_frame_event (); + expect_enter_event (wayland_surface, 1.0, 1.0); + expect_frame_event (); + expect_leave_event (); +} + static void test_single_step (enum test_kind kind) { @@ -473,6 +642,8 @@ test_single_step (enum test_kind kind) struct test_XIButtonState *button_state; struct test_subsurface *child; + button_state = NULL; + again: test_log ("running test step: %s", test_names[kind]); @@ -613,7 +784,99 @@ test_single_step (enum test_kind kind) /* Run the test. */ run_grab_test (button_state, child); + kind = TEST_VALUATOR_KIND; + goto again; + case TEST_VALUATOR_KIND: + /* Dispatch a leave event at -1, -1, and then attach the following + scroll valuator information to the seat: + + type + ScrollClass + number + 1 + scroll_type + Vertical + flags + Preferred + increment + 1.0 + + type + ScrollClass + number + 2 + scroll_type + Horizontal + flags + Preferred + increment + 2.0 + + type + ValuatorClass + sourceid + TEST_SOURCE_DEVICE + number + 1 + label + Rel Scroll Vertical + min + 0.0 + max + 0.0 + resolution + 1 + mode + Relative + value + 0.0 + + type + ValuatorClass + sourceid + TEST_SOURCE_DEVICE + number + 2 + label + Rel Scroll Horizontal + min + 0.0 + max + 0.0 + resolution + 1 + mode + Relative + value + 0.0 + + then, dispatch an entry event at 1, 1, followed by two motion + events at 2, 2 with the following valuators: + + 1, 1.0, 2, 1.0 + 1, 1.1, 2, 2.6 + + verify that the following events arrive in the specified order: + + leave () + frame () + enter (SERIAL, PARENT, 1.0, 1.0) + frame () + + motion (TIME, 2.0, 2.0) (this motion event should arrive because + the entry event happened after + the scroll valuator information was + recorded. The first motion event to + arrive after that should be used to obtain + the current value of the valuator, and + not for calculating scroll deltas.) + frame (); + axis_value120 (WL_POINTER_AXIS_HORIZONTAL_SCROLL, 96) + axis_value120 (WL_POINTER_AXIS_VERTICAL_SCROLL, 12) + frame (); */ + + run_valuator_test (); break; } @@ -752,6 +1015,37 @@ expect_button_event (int button, int state) report_test_failure ("expected button event, but it was not received"); } +static void +expect_axis_value120_event (uint32_t axis, int32_t value120) +{ + struct test_recorded_event *event; + struct test_recorded_axis_value120_event *axis_value120_event; + + event = record_tail; + + if (!event) + report_test_failure ("expected event not sent"); + + record_tail = event->last; + + if (event->kind == POINTER_AXIS_VALUE120_EVENT) + { + axis_value120_event + = (struct test_recorded_axis_value120_event *) event; + + if (axis_value120_event->axis == axis + && axis_value120_event->value120 == value120) + free (event); + else + report_test_failure ("expected axis_value120 event received " + "with incorrect parameters (axis: %"PRIu32"," + " value120: %"PRIi32")", axis, value120); + } + else + report_test_failure ("expected axis_value120 event, but it was not " + "received"); +} + static void expect_no_events (void) { @@ -1011,7 +1305,34 @@ static void handle_pointer_axis_value120 (void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t value120) { - /* TODO... */ + struct test_recorded_axis_value120_event *event; + + if (!recording_events) + { + test_log ("ignored button event"); + return; + } + + event = malloc (sizeof *event); + + if (!event) + report_test_failure ("failed to record event"); + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak" +#endif + + event->header.kind = POINTER_AXIS_VALUE120_EVENT; + event->header.last = record_tail; + record_tail = &event->header; + + event->axis = axis; + event->value120 = value120; + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif } static const struct wl_pointer_listener pointer_listener = diff --git a/tests/test_harness.c b/tests/test_harness.c index efb7744..6aaaec0 100644 --- a/tests/test_harness.c +++ b/tests/test_harness.c @@ -825,6 +825,26 @@ test_init (void) = getenv ("TEST_WRITE_REFERENCE") != NULL; } + + +static void +handle_seat_controller_device_id (void *data, + struct test_seat_controller *controller, + uint32_t device_id) +{ + struct test_display *display; + + display = data; + display->seat->device_id = device_id; +} + +static const struct test_seat_controller_listener seat_controller_listener = + { + handle_seat_controller_device_id, + }; + + + void test_init_seat (struct test_display *display) { @@ -842,6 +862,22 @@ test_init_seat (struct test_display *display) if (!display->seat->controller) report_test_failure ("failed to obtain seat controller"); + display->seat->device_controller + = test_seat_controller_get_device_controller (display->seat->controller); + + if (!display->seat->device_controller) + report_test_failure ("failed to obtain device controller"); + + /* Fetch the device ID of the seat. */ + display->seat->device_id = 0; + test_seat_controller_add_listener (display->seat->controller, + &seat_controller_listener, + display); + wl_display_roundtrip (display->display); + + if (!display->seat->device_id) + report_test_failure ("failed to obtain device ID"); + /* The protocol translator currently supports version 8 of wl_seat, so bind to that. */ diff --git a/tests/test_harness.h b/tests/test_harness.h index 2cc90b1..a17972d 100644 --- a/tests/test_harness.h +++ b/tests/test_harness.h @@ -40,12 +40,18 @@ struct test_seat /* The test seat, if any. */ struct test_seat_controller *controller; + /* The device manager, if any. */ + struct test_device_controller *device_controller; + /* The seat resource itself. */ struct wl_seat *seat; /* The wl_pointer resource. */ struct wl_pointer *pointer; + /* The device ID of the seat. */ + uint32_t device_id; + /* The buttons currently held down. */ unsigned char buttons; };