Fix dispatch of leave events with built-in move

* 12to11-test.xml (test_manager) <error>: New error
`resize_rejected'.
<resize_edge>: New enum.
(test_surface) <move_resize, resize_finished>: New events and
requests.

* compositor.h: Update prototypes.

* seat.c (CancelResizeOperation): Fix dangling pointer.
(FakePointerEdge):
(HandlePointerEdge): Always use builtin resize on test seats.
(XLMoveToplevel): Return status code.

* test.c (struct _TestSurface): New field `resize_callbacks'.
(HandleResizeDone): New function.
(DestroyBacking): Free resize callbacks.
(GetResizeDimensions, MoveResize): New functions.
(test_surface_impl, GetTestSurface): Attach new role hooks etc.

* tests/seat_test.c (enum test_expect_event_kind): New event
kind.
(struct test_recorded_button_event): Add serial field.  Set it.
(struct test_recorded_resize_finished_event): New struct.
(enum test_kind): New test.
(test_names): Name that test.
(LAST_TEST): Set it to the new test.
(run_resize_test, test_single_step): Implement the new test.
(expect_button_event): Return the serial of the button event.
(expect_surface_enter, handle_test_surface_resize_finished)
(test_surface_listener, handle_pointer_button)
(handle_keyboard_enter): Adjust for changes to event handling.
This commit is contained in:
hujianwei 2022-11-24 02:23:11 +00:00
parent 3213ef6493
commit bd3d0a96e0
5 changed files with 281 additions and 16 deletions

View file

@ -55,6 +55,26 @@
summary="the specified label is invalid"/> summary="the specified label is invalid"/>
<entry name="invalid_user_time" value="12" <entry name="invalid_user_time" value="12"
summary="the specified user time lies in the past"/> summary="the specified user time lies in the past"/>
<entry name="resize_rejected" value="13"
summary="the resize was rejected"/>
</enum>
<enum name="resize_edge">
<description summary="edge values for resizing">
These values are used to indicate which edge of a surface
is being dragged in a resize or move operation.
</description>
<entry name="none" value="0"/>
<entry name="top" value="1"/>
<entry name="bottom" value="2"/>
<entry name="left" value="4"/>
<entry name="top_left" value="5"/>
<entry name="bottom_left" value="6"/>
<entry name="right" value="8"/>
<entry name="top_right" value="9"/>
<entry name="bottom_right" value="10"/>
<entry name="move" value="11"/>
</enum> </enum>
<request name="get_test_surface"> <request name="get_test_surface">
@ -155,12 +175,25 @@
</request> </request>
<request name="set_always_garbage"> <request name="set_always_garbage">
<description summary="set_always_garbage"> <description summary="set always garbage">
Force the subcompositor to be garbaged, and all contents Force the subcompositor to be garbaged, and all contents
redrawn from scratch upon any damage. redrawn from scratch upon any damage.
</description> </description>
</request> </request>
<request name="move_resize">
<description summary="begin move or resize">
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.
</description>
<arg name="edge" type="uint" summary="the resize edge"/>
<arg name="serial" type="uint" summary="the resize serial"/>
<arg name="seat" type="object" interface="wl_seat"
summary="the resize seat"/>
</request>
<event name="mapped"> <event name="mapped">
<description summary="role initialized"> <description summary="role initialized">
The map event is sent once the window is mapped and its The map event is sent once the window is mapped and its
@ -208,6 +241,13 @@
</description> </description>
<arg name="presentation_hint" type="uint"/> <arg name="presentation_hint" type="uint"/>
</event> </event>
<event name="resize_finished">
<description summary="resize finished">
The resize_finished event is sent immediately after a resize
completes.
</description>
</event>
</interface> </interface>
<interface name="test_scale_lock" version="1"> <interface name="test_scale_lock" version="1">

View file

@ -1632,7 +1632,7 @@ extern void XLDispatchGEForSeats (XEvent *, Surface *,
extern void XLSelectStandardEvents (Window); extern void XLSelectStandardEvents (Window);
extern void XLInitSeats (void); extern void XLInitSeats (void);
extern Bool XLResizeToplevel (Seat *, Surface *, uint32_t, uint32_t); 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 Bool XLSeatExplicitlyGrabSurface (Seat *, Surface *, uint32_t);
extern void *XLSeatRunAfterResize (Seat *, void (*) (void *, void *), extern void *XLSeatRunAfterResize (Seat *, void (*) (void *, void *),

15
seat.c
View file

@ -2051,6 +2051,7 @@ CancelResizeOperation (Seat *seat, Time time, Subcompositor *subcompositor,
/* Stop the resize operation. */ /* Stop the resize operation. */
XLSurfaceCancelUnmapCallback (seat->resize_surface_callback); XLSurfaceCancelUnmapCallback (seat->resize_surface_callback);
seat->resize_surface = NULL; seat->resize_surface = NULL;
seat->resize_surface_callback = NULL;
/* Run resize completion callbacks. */ /* Run resize completion callbacks. */
RunResizeDoneCallbacks (seat); RunResizeDoneCallbacks (seat);
@ -5098,6 +5099,8 @@ FakePointerEdge (Seat *seat, Surface *target, uint32_t serial,
XISetMask (mask.mask, XI_ButtonPress); XISetMask (mask.mask, XI_ButtonPress);
XISetMask (mask.mask, XI_ButtonRelease); XISetMask (mask.mask, XI_ButtonRelease);
if (!(seat->flags & IsTestSeat))
{
/* Grab the pointer, and don't let go until the button is /* Grab the pointer, and don't let go until the button is
released. */ released. */
state = XIGrabDevice (compositor.display, seat->master_pointer, state = XIGrabDevice (compositor.display, seat->master_pointer,
@ -5106,14 +5109,21 @@ FakePointerEdge (Seat *seat, Surface *target, uint32_t serial,
if (state != Success) if (state != Success)
return False; return False;
}
/* On the other hand, cancel focus locking and leave the surface, /* On the other hand, cancel focus locking and leave the surface,
since we will not be reporting motion events until the resize since we will not be reporting motion events until the resize
operation completes. */ operation completes. */
if (seat->grab_held) 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. */ /* Set the surface as the surface undergoing resize. */
seat->resize_surface = target; seat->resize_surface = target;
seat->resize_surface_callback seat->resize_surface_callback
@ -5139,6 +5149,7 @@ HandlePointerEdge (Seat *seat, Surface *target, uint32_t serial,
return False; return False;
if (!XLWmSupportsHint (_NET_WM_MOVERESIZE) if (!XLWmSupportsHint (_NET_WM_MOVERESIZE)
|| (seat->flags & IsTestSeat)
|| getenv ("USE_BUILTIN_RESIZE")) || getenv ("USE_BUILTIN_RESIZE"))
return FakePointerEdge (seat, target, serial, edge); return FakePointerEdge (seat, target, serial, edge);
@ -5417,10 +5428,10 @@ XLResizeToplevel (Seat *seat, Surface *surface, uint32_t serial,
return StartResizeTracking (seat, surface, serial, edge); return StartResizeTracking (seat, surface, serial, edge);
} }
void Bool
XLMoveToplevel (Seat *seat, Surface *surface, uint32_t serial) XLMoveToplevel (Seat *seat, Surface *surface, uint32_t serial)
{ {
StartResizeTracking (seat, surface, serial, MoveEdge); return StartResizeTracking (seat, surface, serial, MoveEdge);
} }
void * void *

69
test.c
View file

@ -47,6 +47,9 @@ struct _TestSurface
/* The associated buffer release helper. */ /* The associated buffer release helper. */
BufferReleaseHelper *release_helper; BufferReleaseHelper *release_helper;
/* List of live resize callbacks. */
XLList *resize_callbacks;
/* The associated window. */ /* The associated window. */
Window window; Window window;
@ -71,6 +74,19 @@ static struct wl_global *test_manager_global;
/* Hash table of all surfaces. */ /* Hash table of all surfaces. */
static XLAssocTable *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 static void
DestroyBacking (TestSurface *test) DestroyBacking (TestSurface *test)
{ {
@ -90,6 +106,10 @@ DestroyBacking (TestSurface *test)
/* Free the subcompositor. */ /* Free the subcompositor. */
SubcompositorFree (test->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 /* And since there are no C level references to the role anymore, it
can be freed. */ can be freed. */
XLFree (test); 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 static void
SetAlwaysGarbage (struct wl_client *client, struct wl_resource *resource) 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); 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 = static const struct test_surface_interface test_surface_impl =
{ {
.destroy = Destroy, .destroy = Destroy,
.set_always_garbage = SetAlwaysGarbage, .set_always_garbage = SetAlwaysGarbage,
.move_resize = MoveResize,
}; };
static void static void
@ -535,6 +603,7 @@ GetTestSurface (struct wl_client *client, struct wl_resource *resource,
test->role.funcs.subsurface_update = SubsurfaceUpdate; test->role.funcs.subsurface_update = SubsurfaceUpdate;
test->role.funcs.get_window = GetWindow; test->role.funcs.get_window = GetWindow;
test->role.funcs.activate = Activate; test->role.funcs.activate = Activate;
test->role.funcs.get_resize_dimensions = GetResizeDimensions;
/* Add the resource implementation. */ /* Add the resource implementation. */
wl_resource_set_implementation (test->role.resource, &test_surface_impl, wl_resource_set_implementation (test->role.resource, &test_surface_impl,

View file

@ -33,6 +33,7 @@ enum test_expect_event_kind
KEYBOARD_ENTER_EVENT, KEYBOARD_ENTER_EVENT,
KEYBOARD_KEY_EVENT, KEYBOARD_KEY_EVENT,
KEYBOARD_MODIFIERS_EVENT, KEYBOARD_MODIFIERS_EVENT,
SURFACE_RESIZE_FINISHED_EVENT,
}; };
struct test_recorded_event struct test_recorded_event
@ -84,6 +85,9 @@ struct test_recorded_button_event
/* The button and state. */ /* The button and state. */
uint32_t button, state; uint32_t button, state;
/* The serial. */
uint32_t serial;
}; };
struct test_recorded_axis_value120_event struct test_recorded_axis_value120_event
@ -137,6 +141,12 @@ struct test_recorded_keyboard_modifiers_event
uint32_t group; uint32_t group;
}; };
struct test_recorded_resize_finished_event
{
/* The event header. */
struct test_recorded_event header;
};
struct test_subsurface struct test_subsurface
{ {
/* The subsurface itself. */ /* The subsurface itself. */
@ -154,6 +164,7 @@ enum test_kind
TEST_GRAB_KIND, TEST_GRAB_KIND,
TEST_VALUATOR_KIND, TEST_VALUATOR_KIND,
TEST_KEY_KIND, TEST_KEY_KIND,
TEST_RESIZE_KIND,
}; };
static const char *test_names[] = static const char *test_names[] =
@ -164,9 +175,10 @@ static const char *test_names[] =
"test_grab", "test_grab",
"test_valuator", "test_valuator",
"test_key", "test_key",
"test_resize",
}; };
#define LAST_TEST TEST_KEY_KIND #define LAST_TEST TEST_RESIZE_KIND
#define TEST_SOURCE_DEVICE 4500000 #define TEST_SOURCE_DEVICE 4500000
/* The display. */ /* 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_enter_event (struct wl_surface *, double, double);
static void expect_motion_event (double, double); static void expect_motion_event (double, double);
static void expect_leave_event (void); 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_axis_value120_event (uint32_t, int32_t);
static void expect_keyboard_enter_event (struct wl_surface *, uint32_t *, static void expect_keyboard_enter_event (struct wl_surface *, uint32_t *,
size_t); size_t);
static void expect_keyboard_key_event (uint32_t, uint32_t); static void expect_keyboard_key_event (uint32_t, uint32_t);
static void expect_keyboard_modifiers_event (uint32_t, uint32_t, static void expect_keyboard_modifiers_event (uint32_t, uint32_t,
uint32_t, uint32_t); uint32_t, uint32_t);
static void expect_resize_finished_event (void);
static void expect_no_events (void); static void expect_no_events (void);
@ -749,6 +762,62 @@ run_key_test (void)
expect_keyboard_enter_event (wayland_surface, NULL, 0); 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 static void
test_single_step (enum test_kind kind) 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) */ key (SERIAL, TIME, 59, WL_KEYBOARD_KEY_STATE_RELEASED) */
run_key_test (); 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; break;
} }
@ -1118,11 +1205,12 @@ expect_leave_event (void)
report_test_failure ("a leave event was expected, but not received"); report_test_failure ("a leave event was expected, but not received");
} }
static void static uint32_t
expect_button_event (int button, int state) expect_button_event (int button, int state)
{ {
struct test_recorded_event *event; struct test_recorded_event *event;
struct test_recorded_button_event *button_event; struct test_recorded_button_event *button_event;
uint32_t serial;
event = record_tail; event = record_tail;
@ -1136,7 +1224,12 @@ expect_button_event (int button, int state)
button_event = (struct test_recorded_button_event *) event; button_event = (struct test_recorded_button_event *) event;
if (button_event->button == button && button_event->state == state) if (button_event->button == button && button_event->state == state)
{
serial = button_event->serial;
free (event); free (event);
return serial;
}
else else
report_test_failure ("expected button event received " report_test_failure ("expected button event received "
"with incorrect parameters"); "with incorrect parameters");
@ -1283,6 +1376,25 @@ expect_no_events (void)
"yet some arrived"); "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 static void
expect_surface_enter (double x, double y) 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 = static const struct test_surface_listener test_surface_listener =
{ {
handle_test_surface_mapped, handle_test_surface_mapped,
NULL, NULL,
handle_test_surface_committed, 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->button = button;
event->state = state; event->state = state;
event->serial = serial;
#ifdef __GNUC__ #ifdef __GNUC__
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
@ -1634,6 +1778,7 @@ handle_keyboard_enter (void *data, struct wl_keyboard *keyboard,
memcpy (event->keys, keys->data, keys->size); memcpy (event->keys, keys->data, keys->size);
event->num_keys = keys->size / sizeof (uint32_t); event->num_keys = keys->size / sizeof (uint32_t);
event->surface = surface;
#ifdef __GNUC__ #ifdef __GNUC__
#pragma GCC diagnostic pop #pragma GCC diagnostic pop