12to11/xdg_activation.c
hujianwei 4772d8cede 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.
2022-11-16 07:11:03 +00:00

393 lines
9.4 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Wayland compositor running on top of an X server.
Copyright (C) 2022 to various contributors.
This file is part of 12to11.
12to11 is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
12to11 is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
#include <stdio.h>
#include <string.h>
#include "compositor.h"
#include "xdg-activation-v1.h"
typedef struct _XdgActivationToken XdgActivationToken;
struct _XdgActivationToken
{
/* The wl_resource associated with this activation token. */
struct wl_resource *resource;
/* The seat associated with this activation token. */
Seat *seat;
/* 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;
};
/* The xdg_activation_v1 global. */
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;
}
/* Compositor activation policy. The protocol translator,
notwithstanding the judgement of the window manager, allows any
client whose token was created with (IOW, had at the time of
Commit) at least the latest key or pointer serial on its seat to
activate its toplevels. The timestamp used to focus the toplevels
is the activation token. */
static void
SetSerial (struct wl_client *client, struct wl_resource *resource,
uint32_t serial, struct wl_resource *seat_resource)
{
XdgActivationToken *token;
Seat *seat;
token = wl_resource_get_user_data (resource);
/* If token is NULL, then the token has already been used. Silently
ignore this request. */
if (!token)
return;
/* First, clear the current seat. */
if (token->seat_destroy_callback)
XLSeatCancelDestroyListener (token->seat_destroy_callback);
token->seat = NULL;
token->seat_destroy_callback = NULL;
seat = wl_resource_get_user_data (seat_resource);
if (XLSeatIsInert (seat))
/* Just return if the seat is inert. */
return;
/* Otherwise, set the seat and serial. */
token->seat = seat;
token->serial = serial;
token->seat_destroy_callback
= XLSeatRunOnDestroy (seat, HandleSeatDestroyed, token);
}
static void
SetAppId (struct wl_client *client, struct wl_resource *resource,
const char *app_id)
{
/* This information is not useful. */
}
static void
SetSurface (struct wl_client *client, struct wl_resource *resource,
struct wl_resource *surface_resource)
{
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
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);
if (!token)
{
wl_resource_post_error (resource,
XDG_ACTIVATION_TOKEN_V1_ERROR_ALREADY_USED,
"the specified activation token has been passed"
" to a previous commit request and is no longer"
" valid");
return;
}
wl_resource_set_user_data (resource, NULL);
if (!token->seat || !XLSeatCheckActivationSerial (token->seat,
token->serial))
{
/* Send an invalid serial. */
xdg_activation_token_v1_send_done (token->resource,
"activation_rejected");
goto finish;
}
/* Send the last user time as the activation token, along with the
surface id (if set). */
last_user_time = XLSeatGetLastUserTime (token->seat);
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),
/* If id is 0, then a surface was not specified. */
id);
xdg_activation_token_v1_send_done (token->resource, buffer);
/* Free the token. */
finish:
if (token->seat_destroy_callback)
XLSeatCancelDestroyListener (token->seat_destroy_callback);
if (token->destroy_callback)
XLSurfaceCancelRunOnFree (token->destroy_callback);
XLFree (token);
}
static void
DestroyActivationToken (struct wl_client *client,
struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static struct xdg_activation_token_v1_interface xdg_activation_token_impl =
{
.set_serial = SetSerial,
.set_app_id = SetAppId,
.set_surface = SetSurface,
.commit = Commit,
.destroy = DestroyActivationToken,
};
static void
HandleResourceDestroy (struct wl_resource *resource)
{
XdgActivationToken *token;
token = wl_resource_get_user_data (resource);
if (!token)
return;
if (token->seat_destroy_callback)
XLSeatCancelDestroyListener (token->seat_destroy_callback);
if (token->destroy_callback)
XLSurfaceCancelRunOnFree (token->destroy_callback);
XLFree (token);
}
static void
Destroy (struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static void
GetActivationToken (struct wl_client *client, struct wl_resource *resource,
uint32_t id)
{
XdgActivationToken *token;
token = XLSafeMalloc (sizeof *token);
if (!token)
{
wl_resource_post_no_memory (resource);
return;
}
memset (token, 0, sizeof *token);
token->resource
= wl_resource_create (client, &xdg_activation_token_v1_interface,
wl_resource_get_version (resource), id);
if (!token->resource)
{
XLFree (token);
wl_resource_post_no_memory (resource);
return;
}
wl_resource_set_implementation (token->resource,
&xdg_activation_token_impl,
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, *activator_surface;
int deviceid;
unsigned int surface_id;
if (sscanf (token, "%"SCNu32".%"SCNu32".%d.%u", &timestamp.months,
&timestamp.milliseconds, &deviceid, &surface_id) != 4)
/* The activation token is invalid. */
return;
/* Activate the surface with the given token. */
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,
activator_surface);
}
static const struct xdg_activation_v1_interface xdg_activation_impl =
{
.destroy = Destroy,
.get_activation_token = GetActivationToken,
.activate = Activate,
};
static void
HandleBind (struct wl_client *client, void *data, uint32_t version,
uint32_t id)
{
struct wl_resource *resource;
/* Create an xdg_activation_v1 resource. This resource is then used
to create activation tokens and activate surfaces. */
resource = wl_resource_create (client, &xdg_activation_v1_interface,
version, id);
if (!resource)
{
wl_client_post_no_memory (client);
return;
}
wl_resource_set_implementation (resource, &xdg_activation_impl,
NULL, NULL);
}
void
XLInitXdgActivation (void)
{
xdg_activation_global
= wl_global_create (compositor.wl_display,
&xdg_activation_v1_interface,
1, NULL, HandleBind);
}