forked from 12to11/12to11
Fix XDG activation in some edge cases
* 12to11-test.xml (test_manager) <activated>: Add activator_surface parameter. * compositor.h (enum _ClientDataType): New XdgActivationData type. (struct _RoleFuncs, struct _XdgRoleImplementationFuncs): Pass activator surface in `activate'. * seat.c (struct _Seat): New field for the serial of the last entry event. (SendKeyboardEnter, XLSeatCheckActivationSerial): Add workarounds for Firefox. * surface.c (HandleSurfaceDestroy): Allow client data free_functions to be NULL. * test.c (Activate): Accept and send activator surface. * xdg_activation.c (struct _XdgActivationToken): New fields for the surface and destroy callback. (HandleSurfaceDestroyed): New function. (SetSurface): Really record the activator surface. (GetIdForSurface): New function. (Commit): Include activator surface ID. (HandleResourceDestroy): Destroy activator surface. (GetSurfaceForId): New function. (Activate): Pass activator surface whenever specified. * xdg_surface.c (Activate): * xdg_toplevel.c (Activate): Adjust accordingly. * tests/xdg_activation_test.c (check_activation_with_serial): Check activator surface as well. (test_single_step): Fix damage rect for dummy buffer. (handle_test_surface_activated): Record the activator surface.
This commit is contained in:
parent
13b3d3d04b
commit
4772d8cede
9 changed files with 179 additions and 24 deletions
|
@ -175,9 +175,17 @@
|
|||
the surface associated with the role to be activated. Its
|
||||
parameters constitute the timestamp at which the activation
|
||||
occurred.
|
||||
|
||||
If the surface that created the activation token used to
|
||||
activate this test surface belongs to the same client that
|
||||
created the test surface, then it will be sent as the
|
||||
activator_surface argument to this event. Otherwise, the
|
||||
argument is left unspecified.
|
||||
</description>
|
||||
<arg name="months" type="uint"/>
|
||||
<arg name="milliseconds" type="uint"/>
|
||||
<arg name="activator_surface" type="object" interface="wl_surface"
|
||||
allow-null="true"/>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
|
|
|
@ -985,6 +985,7 @@ enum _ClientDataType
|
|||
ShortcutInhibitData,
|
||||
IdleInhibitData,
|
||||
MaxClientData,
|
||||
XdgActivationData,
|
||||
};
|
||||
|
||||
struct _DestroyCallback
|
||||
|
@ -1146,7 +1147,8 @@ struct _RoleFuncs
|
|||
void (*select_extra_events) (Surface *, Role *, unsigned long);
|
||||
void (*note_focus) (Surface *, Role *, FocusMode);
|
||||
void (*outputs_changed) (Surface *, Role *);
|
||||
void (*activate) (Surface *, Role *, int, Timestamp);
|
||||
void (*activate) (Surface *, Role *, int, Timestamp,
|
||||
Surface *);
|
||||
};
|
||||
|
||||
struct _Role
|
||||
|
@ -1343,7 +1345,8 @@ struct _XdgRoleImplementationFuncs
|
|||
void (*note_focus) (Role *, XdgRoleImplementation *, FocusMode);
|
||||
void (*outputs_changed) (Role *, XdgRoleImplementation *);
|
||||
void (*after_commit) (Role *, Surface *, XdgRoleImplementation *);
|
||||
void (*activate) (Role *, XdgRoleImplementation *, int, Time);
|
||||
void (*activate) (Role *, XdgRoleImplementation *, int, Time,
|
||||
Surface *);
|
||||
void (*rescale) (Role *, XdgRoleImplementation *);
|
||||
};
|
||||
|
||||
|
|
16
seat.c
16
seat.c
|
@ -567,6 +567,9 @@ struct _Seat
|
|||
/* The serial of the last key event sent. */
|
||||
uint32_t last_keyboard_serial;
|
||||
|
||||
/* The serial of the last keyboard enter event sent. */
|
||||
uint32_t last_enter_serial;
|
||||
|
||||
/* Whether or not a resize is in progress. */
|
||||
Bool resize_in_progress;
|
||||
|
||||
|
@ -2837,6 +2840,11 @@ SendKeyboardEnter (Seat *seat, Surface *enter)
|
|||
if (!info)
|
||||
return;
|
||||
|
||||
/* For some reason Firefox doesn't use the serial of the user event
|
||||
(key or button press) that triggered the activation, but the
|
||||
serial of the last keyboard entry event on the seat. */
|
||||
seat->last_enter_serial = serial;
|
||||
|
||||
keyboard = info->keyboards.next;
|
||||
|
||||
for (; keyboard != &info->keyboards; keyboard = keyboard->next)
|
||||
|
@ -6403,9 +6411,13 @@ XLSeatCheckActivationSerial (Seat *seat, uint32_t serial)
|
|||
return ((seat->last_button_press_serial
|
||||
&& serial >= seat->last_button_press_serial)
|
||||
|| (seat->last_button_serial
|
||||
&& serial >= seat->last_button_press_serial)
|
||||
&& serial >= seat->last_button_serial)
|
||||
|| (seat->last_keyboard_serial
|
||||
&& serial >= seat->last_keyboard_serial));
|
||||
&& serial >= seat->last_keyboard_serial)
|
||||
/* Also allow using the serial at which the focus last
|
||||
changed, if there currently is a keyboard focus. */
|
||||
|| (serial == seat->last_enter_serial
|
||||
&& seat->focus_surface));
|
||||
}
|
||||
|
||||
/* This is a particularly ugly hack, but there is no other way to
|
||||
|
|
|
@ -1410,8 +1410,10 @@ HandleSurfaceDestroy (struct wl_resource *resource)
|
|||
|
||||
while (data)
|
||||
{
|
||||
/* Free the client data. */
|
||||
data->free_function (data->data);
|
||||
if (data->free_function)
|
||||
/* Free the client data. */
|
||||
data->free_function (data->data);
|
||||
|
||||
XLFree (data->data);
|
||||
|
||||
/* And its record. */
|
||||
|
|
21
test.c
21
test.c
|
@ -338,16 +338,29 @@ GetWindow (Surface *surface, Role *role)
|
|||
|
||||
static void
|
||||
Activate (Surface *surface, Role *role, int deviceid,
|
||||
Timestamp timestamp)
|
||||
Timestamp timestamp, Surface *activator_surface)
|
||||
{
|
||||
struct wl_resource *resource;
|
||||
TestSurface *test;
|
||||
|
||||
test = TestSurfaceFromRole (role);
|
||||
|
||||
if (test->role.resource)
|
||||
test_surface_send_activated (test->role.resource,
|
||||
timestamp.months,
|
||||
timestamp.milliseconds);
|
||||
{
|
||||
/* If the activator surface belongs to the same client as the
|
||||
client who created the test surface, set the resource to the
|
||||
activator surface. */
|
||||
if (wl_resource_get_client (activator_surface->resource)
|
||||
== wl_resource_get_client (test->role.resource))
|
||||
resource = activator_surface->resource;
|
||||
else
|
||||
resource = NULL;
|
||||
|
||||
test_surface_send_activated (test->role.resource,
|
||||
timestamp.months,
|
||||
timestamp.milliseconds,
|
||||
resource);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct test_surface_interface test_surface_impl =
|
||||
|
|
|
@ -58,6 +58,9 @@ static struct wl_surface *wayland_surface;
|
|||
static uint32_t last_activation_months;
|
||||
static uint32_t last_activation_milliseconds;
|
||||
|
||||
/* The last activation surface. */
|
||||
static struct wl_surface *last_activation_surface;
|
||||
|
||||
|
||||
|
||||
/* Forward declarations. */
|
||||
|
@ -149,6 +152,10 @@ check_activation_with_serial (uint32_t serial, bool expect_success)
|
|||
|| last_activation_milliseconds != 1001)
|
||||
report_test_failure ("activation failed, wrong time or event not"
|
||||
" received");
|
||||
|
||||
if (last_activation_surface != wayland_surface)
|
||||
report_test_failure ("activation succeeded, but the activator"
|
||||
" surface was wrong");
|
||||
}
|
||||
else if (last_activation_months || last_activation_milliseconds)
|
||||
report_test_failure ("activation succeeded unexpectedly");
|
||||
|
@ -172,7 +179,7 @@ test_single_step (enum test_kind kind)
|
|||
report_test_failure ("failed to load tiny.png");
|
||||
|
||||
wl_surface_attach (wayland_surface, buffer, 0, 0);
|
||||
submit_surface_damage (wayland_surface, 0, 0, 500, 500);
|
||||
submit_surface_damage (wayland_surface, 0, 0, 4, 4);
|
||||
wl_surface_commit (wayland_surface);
|
||||
wait_for_map ();
|
||||
|
||||
|
@ -290,10 +297,12 @@ handle_test_surface_mapped (void *data, struct test_surface *test_surface,
|
|||
|
||||
static void
|
||||
handle_test_surface_activated (void *data, struct test_surface *test_surface,
|
||||
uint32_t months, uint32_t milliseconds)
|
||||
uint32_t months, uint32_t milliseconds,
|
||||
struct wl_surface *activator_surface)
|
||||
{
|
||||
last_activation_months = months;
|
||||
last_activation_milliseconds = milliseconds;
|
||||
last_activation_surface = activator_surface;
|
||||
}
|
||||
|
||||
static const struct test_surface_listener test_surface_listener =
|
||||
|
|
121
xdg_activation.c
121
xdg_activation.c
|
@ -36,6 +36,12 @@ struct _XdgActivationToken
|
|||
/* The seat destroy callback. */
|
||||
void *seat_destroy_callback;
|
||||
|
||||
/* The surface associated with this activation token. */
|
||||
Surface *surface;
|
||||
|
||||
/* The destroy callback associated with the surface. */
|
||||
DestroyCallback *destroy_callback;
|
||||
|
||||
/* The serial associated with this activation token. */
|
||||
uint32_t serial;
|
||||
};
|
||||
|
@ -45,12 +51,23 @@ static struct wl_global *xdg_activation_global;
|
|||
|
||||
|
||||
|
||||
static void
|
||||
HandleSurfaceDestroyed (void *data)
|
||||
{
|
||||
XdgActivationToken *token;
|
||||
|
||||
token = data;
|
||||
token->destroy_callback = NULL;
|
||||
token->surface = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
HandleSeatDestroyed (void *data)
|
||||
{
|
||||
XdgActivationToken *token;
|
||||
|
||||
token = data;
|
||||
token->seat_destroy_callback = NULL;
|
||||
token->seat = NULL;
|
||||
token->serial = 0;
|
||||
}
|
||||
|
@ -106,7 +123,47 @@ static void
|
|||
SetSurface (struct wl_client *client, struct wl_resource *resource,
|
||||
struct wl_resource *surface_resource)
|
||||
{
|
||||
/* This information is not useful. */
|
||||
XdgActivationToken *token;
|
||||
Surface *surface;
|
||||
|
||||
token = wl_resource_get_user_data (resource);
|
||||
surface = wl_resource_get_user_data (surface_resource);
|
||||
|
||||
if (token->surface)
|
||||
XLSurfaceCancelRunOnFree (token->destroy_callback);
|
||||
|
||||
/* The surface specified here is used by window managers to decide
|
||||
whether or not to transfer focus. It should be the surface that
|
||||
the client thinks is currently focused. */
|
||||
|
||||
token->surface = surface;
|
||||
token->destroy_callback
|
||||
= XLSurfaceRunOnFree (surface, HandleSurfaceDestroyed,
|
||||
token);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
GetIdForSurface (Surface *surface)
|
||||
{
|
||||
unsigned int *data;
|
||||
static unsigned int id;
|
||||
|
||||
/* Given a surface, return a unique identifier for that surface. */
|
||||
data = XLSurfaceGetClientData (surface, XdgActivationData,
|
||||
sizeof *data, NULL);
|
||||
|
||||
/* If data is 0, then initialize it with a unique id. */
|
||||
|
||||
if (!*data)
|
||||
{
|
||||
id++;
|
||||
if (!id)
|
||||
id++;
|
||||
*data = id;
|
||||
}
|
||||
|
||||
/* Return the surface's id. */
|
||||
return *data;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -115,6 +172,7 @@ Commit (struct wl_client *client, struct wl_resource *resource)
|
|||
XdgActivationToken *token;
|
||||
Timestamp last_user_time;
|
||||
char buffer[80];
|
||||
unsigned int id;
|
||||
|
||||
token = wl_resource_get_user_data (resource);
|
||||
|
||||
|
@ -139,12 +197,22 @@ Commit (struct wl_client *client, struct wl_resource *resource)
|
|||
goto finish;
|
||||
}
|
||||
|
||||
/* Send the last user time as the activation token. */
|
||||
/* Send the last user time as the activation token, along with the
|
||||
surface id (if set). */
|
||||
|
||||
last_user_time = XLSeatGetLastUserTime (token->seat);
|
||||
sprintf (buffer, "%"PRIu32".%"PRIu32".%d",
|
||||
|
||||
if (token->surface)
|
||||
id = GetIdForSurface (token->surface);
|
||||
else
|
||||
id = 0;
|
||||
|
||||
sprintf (buffer, "%"PRIu32".%"PRIu32".%d.%u",
|
||||
last_user_time.months,
|
||||
last_user_time.milliseconds,
|
||||
XLSeatGetPointerDevice (token->seat));
|
||||
XLSeatGetPointerDevice (token->seat),
|
||||
/* If id is 0, then a surface was not specified. */
|
||||
id);
|
||||
xdg_activation_token_v1_send_done (token->resource, buffer);
|
||||
|
||||
/* Free the token. */
|
||||
|
@ -152,6 +220,9 @@ Commit (struct wl_client *client, struct wl_resource *resource)
|
|||
if (token->seat_destroy_callback)
|
||||
XLSeatCancelDestroyListener (token->seat_destroy_callback);
|
||||
|
||||
if (token->destroy_callback)
|
||||
XLSurfaceCancelRunOnFree (token->destroy_callback);
|
||||
|
||||
XLFree (token);
|
||||
}
|
||||
|
||||
|
@ -184,6 +255,9 @@ HandleResourceDestroy (struct wl_resource *resource)
|
|||
if (token->seat_destroy_callback)
|
||||
XLSeatCancelDestroyListener (token->seat_destroy_callback);
|
||||
|
||||
if (token->destroy_callback)
|
||||
XLSurfaceCancelRunOnFree (token->destroy_callback);
|
||||
|
||||
XLFree (token);
|
||||
}
|
||||
|
||||
|
@ -226,16 +300,37 @@ GetActivationToken (struct wl_client *client, struct wl_resource *resource,
|
|||
token, HandleResourceDestroy);
|
||||
}
|
||||
|
||||
static Surface *
|
||||
GetSurfaceForId (unsigned int id)
|
||||
{
|
||||
Surface *surface;
|
||||
unsigned int *data;
|
||||
|
||||
surface = all_surfaces.next;
|
||||
while (surface != &all_surfaces)
|
||||
{
|
||||
data = XLSurfaceFindClientData (surface, XdgActivationData);
|
||||
|
||||
if (data && *data == id)
|
||||
return surface;
|
||||
|
||||
surface = surface->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
Activate (struct wl_client *client, struct wl_resource *resource,
|
||||
const char *token, struct wl_resource *surface_resource)
|
||||
{
|
||||
Timestamp timestamp;
|
||||
Surface *surface;
|
||||
Surface *surface, *activator_surface;
|
||||
int deviceid;
|
||||
unsigned int surface_id;
|
||||
|
||||
if (sscanf (token, "%"SCNu32".%"SCNu32".%d", ×tamp.months,
|
||||
×tamp.milliseconds, &deviceid) != 3)
|
||||
if (sscanf (token, "%"SCNu32".%"SCNu32".%d.%u", ×tamp.months,
|
||||
×tamp.milliseconds, &deviceid, &surface_id) != 4)
|
||||
/* The activation token is invalid. */
|
||||
return;
|
||||
|
||||
|
@ -243,9 +338,19 @@ Activate (struct wl_client *client, struct wl_resource *resource,
|
|||
|
||||
surface = wl_resource_get_user_data (surface_resource);
|
||||
|
||||
/* If a surface ID was specified, try to find the surface
|
||||
inside. */
|
||||
|
||||
if (surface_id)
|
||||
/* Try to obtain the surface associated with this ID. */
|
||||
activator_surface = GetSurfaceForId (surface_id);
|
||||
else
|
||||
activator_surface = NULL;
|
||||
|
||||
if (surface->role->funcs.activate)
|
||||
surface->role->funcs.activate (surface, surface->role,
|
||||
deviceid, timestamp);
|
||||
deviceid, timestamp,
|
||||
activator_surface);
|
||||
}
|
||||
|
||||
static const struct xdg_activation_v1_interface xdg_activation_impl =
|
||||
|
|
|
@ -1399,7 +1399,7 @@ OutputsChanged (Surface *surface, Role *role)
|
|||
|
||||
static void
|
||||
Activate (Surface *surface, Role *role, int deviceid,
|
||||
Timestamp timestamp)
|
||||
Timestamp timestamp, Surface *activator_surface)
|
||||
{
|
||||
XdgRole *xdg_role;
|
||||
|
||||
|
@ -1408,7 +1408,8 @@ Activate (Surface *surface, Role *role, int deviceid,
|
|||
if (xdg_role->impl && xdg_role->impl->funcs.activate)
|
||||
xdg_role->impl->funcs.activate (role, xdg_role->impl,
|
||||
deviceid,
|
||||
timestamp.milliseconds);
|
||||
timestamp.milliseconds,
|
||||
activator_surface);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -2292,7 +2292,7 @@ OutputsChanged (Role *role, XdgRoleImplementation *impl)
|
|||
|
||||
static void
|
||||
Activate (Role *role, XdgRoleImplementation *impl, int deviceid,
|
||||
Time time)
|
||||
Time time, Surface *activator_surface)
|
||||
{
|
||||
XEvent message;
|
||||
XdgToplevel *toplevel;
|
||||
|
@ -2310,7 +2310,9 @@ Activate (Role *role, XdgRoleImplementation *impl, int deviceid,
|
|||
message.xclient.format = 32;
|
||||
message.xclient.data.l[0] = 1;
|
||||
message.xclient.data.l[1] = time;
|
||||
message.xclient.data.l[2] = None;
|
||||
message.xclient.data.l[2] = (activator_surface
|
||||
? XLWindowFromSurface (activator_surface)
|
||||
: None);
|
||||
message.xclient.data.l[3] = 0;
|
||||
message.xclient.data.l[4] = 0;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue