diff --git a/12to11-test.xml b/12to11-test.xml index eeac4b5..f20eb4c 100644 --- a/12to11-test.xml +++ b/12to11-test.xml @@ -55,6 +55,26 @@ summary="the specified label is invalid"/> + + + + + + + These values are used to indicate which edge of a surface + is being dragged in a resize or move operation. + + + + + + + + + + + @@ -155,12 +175,25 @@ - + Force the subcompositor to be garbaged, and all contents redrawn from scratch upon any damage. + + + Begin a move or resize event with the specified resize edge + and event serial on the given seat. Send a resize_rejected + event if the edge is invalid or the serial is out of date, or + the specified role no longer has a surface. + + + + + + The map event is sent once the window is mapped and its @@ -208,6 +241,13 @@ + + + + The resize_finished event is sent immediately after a resize + completes. + + diff --git a/compositor.h b/compositor.h index a3e5a93..d000c0f 100644 --- a/compositor.h +++ b/compositor.h @@ -1632,7 +1632,7 @@ extern void XLDispatchGEForSeats (XEvent *, Surface *, extern void XLSelectStandardEvents (Window); extern void XLInitSeats (void); extern Bool XLResizeToplevel (Seat *, Surface *, uint32_t, uint32_t); -extern void XLMoveToplevel (Seat *, Surface *, uint32_t); +extern Bool XLMoveToplevel (Seat *, Surface *, uint32_t); extern Bool XLSeatExplicitlyGrabSurface (Seat *, Surface *, uint32_t); extern void *XLSeatRunAfterResize (Seat *, void (*) (void *, void *), diff --git a/seat.c b/seat.c index 2891900..24fd775 100644 --- a/seat.c +++ b/seat.c @@ -2051,6 +2051,7 @@ CancelResizeOperation (Seat *seat, Time time, Subcompositor *subcompositor, /* Stop the resize operation. */ XLSurfaceCancelUnmapCallback (seat->resize_surface_callback); seat->resize_surface = NULL; + seat->resize_surface_callback = NULL; /* Run resize completion callbacks. */ RunResizeDoneCallbacks (seat); @@ -5098,21 +5099,30 @@ FakePointerEdge (Seat *seat, Surface *target, uint32_t serial, XISetMask (mask.mask, XI_ButtonPress); XISetMask (mask.mask, XI_ButtonRelease); - /* Grab the pointer, and don't let go until the button is - released. */ - state = XIGrabDevice (compositor.display, seat->master_pointer, - window, seat->its_press_time, cursor, - XIGrabModeAsync, XIGrabModeAsync, False, &mask); + if (!(seat->flags & IsTestSeat)) + { + /* Grab the pointer, and don't let go until the button is + released. */ + state = XIGrabDevice (compositor.display, seat->master_pointer, + window, seat->its_press_time, cursor, + XIGrabModeAsync, XIGrabModeAsync, False, &mask); - if (state != Success) - return False; + if (state != Success) + return False; + } /* On the other hand, cancel focus locking and leave the surface, since we will not be reporting motion events until the resize operation completes. */ if (seat->grab_held) - CancelGrabEarly (seat); + { + CancelGrabEarly (seat); + + /* CancelGrabEarly may not have left the focus surface if the + pointer remains on it. Force a leave event to be sent. */ + EnteredSurface (seat, NULL, seat->its_press_time, 0, 0, False); + } /* Set the surface as the surface undergoing resize. */ seat->resize_surface = target; @@ -5139,6 +5149,7 @@ HandlePointerEdge (Seat *seat, Surface *target, uint32_t serial, return False; if (!XLWmSupportsHint (_NET_WM_MOVERESIZE) + || (seat->flags & IsTestSeat) || getenv ("USE_BUILTIN_RESIZE")) return FakePointerEdge (seat, target, serial, edge); @@ -5417,10 +5428,10 @@ XLResizeToplevel (Seat *seat, Surface *surface, uint32_t serial, return StartResizeTracking (seat, surface, serial, edge); } -void +Bool XLMoveToplevel (Seat *seat, Surface *surface, uint32_t serial) { - StartResizeTracking (seat, surface, serial, MoveEdge); + return StartResizeTracking (seat, surface, serial, MoveEdge); } void * diff --git a/test.c b/test.c index 3ffa684..3ea23aa 100644 --- a/test.c +++ b/test.c @@ -47,6 +47,9 @@ struct _TestSurface /* The associated buffer release helper. */ BufferReleaseHelper *release_helper; + /* List of live resize callbacks. */ + XLList *resize_callbacks; + /* The associated window. */ Window window; @@ -71,6 +74,19 @@ static struct wl_global *test_manager_global; /* Hash table of all surfaces. */ static XLAssocTable *surfaces; +static void +HandleResizeDone (void *key, void *data) +{ + TestSurface *test; + + test = data; + test->resize_callbacks + = XLListRemove (test->resize_callbacks, key); + + if (test->role.resource) + test_surface_send_resize_finished (test->role.resource); +} + static void DestroyBacking (TestSurface *test) { @@ -90,6 +106,10 @@ DestroyBacking (TestSurface *test) /* Free the subcompositor. */ SubcompositorFree (test->subcompositor); + /* Free all resize callbacks. */ + XLListFree (test->resize_callbacks, + XLSeatCancelResizeCallback); + /* And since there are no C level references to the role anymore, it can be freed. */ XLFree (test); @@ -369,6 +389,18 @@ Activate (Surface *surface, Role *role, int deviceid, } } +static void +GetResizeDimensions (Surface *surface, Role *role, int *width, + int *height) +{ + TestSurface *test; + + test = TestSurfaceFromRole (role); + + *width = SubcompositorWidth (test->subcompositor); + *height = SubcompositorHeight (test->subcompositor); +} + static void SetAlwaysGarbage (struct wl_client *client, struct wl_resource *resource) { @@ -380,10 +412,46 @@ SetAlwaysGarbage (struct wl_client *client, struct wl_resource *resource) SubcompositorSetAlwaysGarbaged (test->subcompositor); } +static void +MoveResize (struct wl_client *client, struct wl_resource *resource, + uint32_t edge, uint32_t serial, + struct wl_resource *seat_resource) +{ + TestSurface *test; + Seat *seat; + void *key; + + test = wl_resource_get_user_data (resource); + seat = wl_resource_get_user_data (seat_resource); + + if (!test->role.surface) + { + wl_resource_post_error (resource, TEST_MANAGER_ERROR_RESIZE_REJECTED, + "trying to resize test surface without surface"); + return; + } + + if (edge == TEST_MANAGER_RESIZE_EDGE_MOVE) + { + if (!XLMoveToplevel (seat, test->role.surface, serial)) + wl_resource_post_error (resource, TEST_MANAGER_ERROR_RESIZE_REJECTED, + "move rejected for unspecified reason"); + } + else if (!XLResizeToplevel (seat, test->role.surface, serial, edge)) + wl_resource_post_error (resource, TEST_MANAGER_ERROR_RESIZE_REJECTED, + "resize rejected for unspecified reason"); + + /* Now attach a resize complete callback. */ + key = XLSeatRunAfterResize (seat, HandleResizeDone, test); + test->resize_callbacks = XLListPrepend (test->resize_callbacks, + key); +} + static const struct test_surface_interface test_surface_impl = { .destroy = Destroy, .set_always_garbage = SetAlwaysGarbage, + .move_resize = MoveResize, }; static void @@ -535,6 +603,7 @@ GetTestSurface (struct wl_client *client, struct wl_resource *resource, test->role.funcs.subsurface_update = SubsurfaceUpdate; test->role.funcs.get_window = GetWindow; test->role.funcs.activate = Activate; + test->role.funcs.get_resize_dimensions = GetResizeDimensions; /* Add the resource implementation. */ wl_resource_set_implementation (test->role.resource, &test_surface_impl, diff --git a/tests/seat_test.c b/tests/seat_test.c index 9fbc0b1..84e44cd 100644 --- a/tests/seat_test.c +++ b/tests/seat_test.c @@ -33,6 +33,7 @@ enum test_expect_event_kind KEYBOARD_ENTER_EVENT, KEYBOARD_KEY_EVENT, KEYBOARD_MODIFIERS_EVENT, + SURFACE_RESIZE_FINISHED_EVENT, }; struct test_recorded_event @@ -84,6 +85,9 @@ struct test_recorded_button_event /* The button and state. */ uint32_t button, state; + + /* The serial. */ + uint32_t serial; }; struct test_recorded_axis_value120_event @@ -137,6 +141,12 @@ struct test_recorded_keyboard_modifiers_event uint32_t group; }; +struct test_recorded_resize_finished_event +{ + /* The event header. */ + struct test_recorded_event header; +}; + struct test_subsurface { /* The subsurface itself. */ @@ -154,6 +164,7 @@ enum test_kind TEST_GRAB_KIND, TEST_VALUATOR_KIND, TEST_KEY_KIND, + TEST_RESIZE_KIND, }; static const char *test_names[] = @@ -164,9 +175,10 @@ static const char *test_names[] = "test_grab", "test_valuator", "test_key", + "test_resize", }; -#define LAST_TEST TEST_KEY_KIND +#define LAST_TEST TEST_RESIZE_KIND #define TEST_SOURCE_DEVICE 4500000 /* The display. */ @@ -206,13 +218,14 @@ static void expect_frame_event (void); 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 uint32_t expect_button_event (int, int); static void expect_axis_value120_event (uint32_t, int32_t); static void expect_keyboard_enter_event (struct wl_surface *, uint32_t *, size_t); static void expect_keyboard_key_event (uint32_t, uint32_t); static void expect_keyboard_modifiers_event (uint32_t, uint32_t, uint32_t, uint32_t); +static void expect_resize_finished_event (void); static void expect_no_events (void); @@ -749,6 +762,62 @@ run_key_test (void) expect_keyboard_enter_event (wayland_surface, NULL, 0); } +static void +run_resize_test (struct test_XIButtonState *button_state) +{ + uint32_t serial; + + test_seat_controller_dispatch_XI_ButtonPress (display->seat->controller, + test_get_time (), + TEST_SOURCE_DEVICE, + 1, + 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, + button_state, + NULL, NULL, NULL); + test_XIButtonState_add_button (button_state, 1); + record_events (); + + /* Obtain the serial. */ + expect_frame_event (); + serial = expect_button_event (BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); + expect_no_events (); + + /* Now, start resize. */ + test_surface_move_resize (test_surface, TEST_MANAGER_RESIZE_EDGE_MOVE, + serial, display->seat->seat); + + /* And dispatch the button release. */ + test_seat_controller_dispatch_XI_ButtonRelease (display->seat->controller, + test_get_time (), + TEST_SOURCE_DEVICE, + 1, + 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, + button_state, + NULL, NULL, NULL); + test_XIButtonState_remove_button (button_state, 1); + + record_events (); + expect_frame_event (); + expect_enter_event (wayland_surface, 2.0, 2.0); + expect_resize_finished_event (); + expect_frame_event (); + expect_leave_event (); +} + static void test_single_step (enum test_kind kind) { @@ -1007,6 +1076,24 @@ test_single_step (enum test_kind kind) key (SERIAL, TIME, 59, WL_KEYBOARD_KEY_STATE_RELEASED) */ run_key_test (); + kind = TEST_RESIZE_KIND; + goto again; + + case TEST_RESIZE_KIND: + /* Test simple resize (or rather movement). Dispatch a + ButtonPress event, obtain the serial of said event, start + resize, and dispatch a ButtonRelease event. Verify that the + following events are sent to the pointer and surface. + + button (SERIAL, TIME, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED) + frame () + leave () + resize_finished () + frame (); + enter (SERIAL, SURFACE, 2.0, 2.0) + frame (); */ + + run_resize_test (button_state); break; } @@ -1118,11 +1205,12 @@ expect_leave_event (void) report_test_failure ("a leave event was expected, but not received"); } -static void +static uint32_t expect_button_event (int button, int state) { struct test_recorded_event *event; struct test_recorded_button_event *button_event; + uint32_t serial; event = record_tail; @@ -1136,7 +1224,12 @@ expect_button_event (int button, int state) button_event = (struct test_recorded_button_event *) event; if (button_event->button == button && button_event->state == state) - free (event); + { + serial = button_event->serial; + free (event); + + return serial; + } else report_test_failure ("expected button event received " "with incorrect parameters"); @@ -1283,6 +1376,25 @@ expect_no_events (void) "yet some arrived"); } +static void +expect_resize_finished_event (void) +{ + struct test_recorded_event *event; + + event = record_tail; + + if (!event) + return; + + record_tail = record_tail->last; + + if (event->kind == SURFACE_RESIZE_FINISHED_EVENT) + free (event); + else + report_test_failure ("expected resize_finished_event, but it was" + " not received"); +} + static void expect_surface_enter (double x, double y) { @@ -1331,11 +1443,42 @@ handle_test_surface_committed (void *data, struct test_surface *surface, } +static void +handle_test_surface_resize_finished (void *data, struct test_surface *surface) +{ + struct test_recorded_resize_finished_event *event; + + if (!recording_events) + { + test_log ("ignored resize finish 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 = SURFACE_RESIZE_FINISHED_EVENT; + event->header.last = record_tail; + record_tail = &event->header; + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +} + static const struct test_surface_listener test_surface_listener = { handle_test_surface_mapped, NULL, handle_test_surface_committed, + handle_test_surface_resize_finished, }; @@ -1475,6 +1618,7 @@ handle_pointer_button (void *data, struct wl_pointer *wl_pointer, event->button = button; event->state = state; + event->serial = serial; #ifdef __GNUC__ #pragma GCC diagnostic pop @@ -1634,6 +1778,7 @@ handle_keyboard_enter (void *data, struct wl_keyboard *keyboard, memcpy (event->keys, keys->data, keys->size); event->num_keys = keys->size / sizeof (uint32_t); + event->surface = surface; #ifdef __GNUC__ #pragma GCC diagnostic pop