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"/>
<entry name="invalid_user_time" value="12"
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>
<request name="get_test_surface">
@ -155,12 +175,25 @@
</request>
<request name="set_always_garbage">
<description summary="set_always_garbage">
<description summary="set always garbage">
Force the subcompositor to be garbaged, and all contents
redrawn from scratch upon any damage.
</description>
</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">
<description summary="role initialized">
The map event is sent once the window is mapped and its
@ -208,6 +241,13 @@
</description>
<arg name="presentation_hint" type="uint"/>
</event>
<event name="resize_finished">
<description summary="resize finished">
The resize_finished event is sent immediately after a resize
completes.
</description>
</event>
</interface>
<interface name="test_scale_lock" version="1">

View file

@ -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 *),

31
seat.c
View file

@ -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 *

69
test.c
View file

@ -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,

View file

@ -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