/* 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 . */
#include
#include
#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 serial associated with this activation token. */
uint32_t serial;
};
/* The xdg_activation_v1 global. */
static struct wl_global *xdg_activation_global;
static void
HandleSeatDestroyed (void *data)
{
XdgActivationToken *token;
token = data;
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)
{
/* This information is not useful. */
}
static void
Commit (struct wl_client *client, struct wl_resource *resource)
{
XdgActivationToken *token;
Timestamp last_user_time;
char buffer[80];
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. */
last_user_time = XLSeatGetLastUserTime (token->seat);
sprintf (buffer, "%"PRIu32".%"PRIu32".%d",
last_user_time.months,
last_user_time.milliseconds,
XLSeatGetPointerDevice (token->seat));
xdg_activation_token_v1_send_done (token->resource, buffer);
/* Free the token. */
finish:
if (token->seat_destroy_callback)
XLSeatCancelDestroyListener (token->seat_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);
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 void
Activate (struct wl_client *client, struct wl_resource *resource,
const char *token, struct wl_resource *surface_resource)
{
Timestamp timestamp;
Surface *surface;
int deviceid;
if (sscanf (token, "%"SCNu32".%"SCNu32".%d", ×tamp.months,
×tamp.milliseconds, &deviceid) != 3)
/* The activation token is invalid. */
return;
/* Activate the surface with the given token. */
surface = wl_resource_get_user_data (surface_resource);
if (surface->role->funcs.activate)
surface->role->funcs.activate (surface, surface->role,
deviceid, timestamp);
}
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);
}