xdg-activation-v1: new protocol implementation
This implements the new xdg-activation-v1 protocol [1]. [1]: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/50
This commit is contained in:
parent
b86a0c8d8f
commit
8ff435831f
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* This an unstable interface of wlroots. No guarantees are made regarding the
|
||||||
|
* future consistency of this API.
|
||||||
|
*/
|
||||||
|
#ifndef WLR_USE_UNSTABLE
|
||||||
|
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WLR_TYPES_WLR_XDG_ACTIVATION_V1
|
||||||
|
#define WLR_TYPES_WLR_XDG_ACTIVATION_V1
|
||||||
|
|
||||||
|
#include <wayland-server-core.h>
|
||||||
|
|
||||||
|
struct wlr_xdg_activation_token_v1 {
|
||||||
|
struct wlr_xdg_activation_v1 *activation;
|
||||||
|
// The source surface that created the token.
|
||||||
|
struct wlr_surface *surface; // can be NULL
|
||||||
|
struct wlr_seat *seat; // can be NULL
|
||||||
|
// The serial for the input event that created the token.
|
||||||
|
uint32_t serial; // invalid if seat is NULL
|
||||||
|
// The application ID to be activated. This is just a hint.
|
||||||
|
char *app_id; // can be NULL
|
||||||
|
struct wl_list link; // wlr_xdg_activation_v1.tokens
|
||||||
|
|
||||||
|
// private state
|
||||||
|
|
||||||
|
char *token;
|
||||||
|
struct wl_resource *resource; // can be NULL
|
||||||
|
|
||||||
|
struct wl_listener seat_destroy;
|
||||||
|
struct wl_listener surface_destroy;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_xdg_activation_v1 {
|
||||||
|
|
||||||
|
struct wl_list tokens; // wlr_xdg_activation_token_v1.link
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct wl_signal destroy;
|
||||||
|
struct wl_signal request_activate; // wlr_xdg_activation_v1_request_activate_event
|
||||||
|
} events;
|
||||||
|
|
||||||
|
// private state
|
||||||
|
|
||||||
|
struct wl_global *global;
|
||||||
|
|
||||||
|
struct wl_listener display_destroy;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_xdg_activation_v1_request_activate_event {
|
||||||
|
struct wlr_xdg_activation_v1 *activation;
|
||||||
|
// The token used to request activation.
|
||||||
|
struct wlr_xdg_activation_token_v1 *token;
|
||||||
|
// The surface requesting for activation.
|
||||||
|
struct wlr_surface *surface;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_xdg_activation_v1 *wlr_xdg_activation_v1_create(
|
||||||
|
struct wl_display *display);
|
||||||
|
|
||||||
|
#endif
|
|
@ -105,7 +105,6 @@ pixman = dependency('pixman-1')
|
||||||
math = cc.find_library('m')
|
math = cc.find_library('m')
|
||||||
rt = cc.find_library('rt')
|
rt = cc.find_library('rt')
|
||||||
|
|
||||||
|
|
||||||
wlr_files = []
|
wlr_files = []
|
||||||
wlr_deps = [
|
wlr_deps = [
|
||||||
wayland_server,
|
wayland_server,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
wayland_protos = dependency('wayland-protocols', version: '>=1.17')
|
wayland_protos = dependency('wayland-protocols', version: '>=1.21')
|
||||||
wl_protocol_dir = wayland_protos.get_variable(pkgconfig: 'pkgdatadir')
|
wl_protocol_dir = wayland_protos.get_variable(pkgconfig: 'pkgdatadir')
|
||||||
|
|
||||||
wayland_scanner_dep = dependency('wayland-scanner', native: true)
|
wayland_scanner_dep = dependency('wayland-scanner', native: true)
|
||||||
|
@ -23,6 +23,7 @@ protocols = {
|
||||||
'relative-pointer-unstable-v1': wl_protocol_dir / 'unstable/relative-pointer/relative-pointer-unstable-v1.xml',
|
'relative-pointer-unstable-v1': wl_protocol_dir / 'unstable/relative-pointer/relative-pointer-unstable-v1.xml',
|
||||||
'tablet-unstable-v2': wl_protocol_dir / 'unstable/tablet/tablet-unstable-v2.xml',
|
'tablet-unstable-v2': wl_protocol_dir / 'unstable/tablet/tablet-unstable-v2.xml',
|
||||||
'text-input-unstable-v3': wl_protocol_dir / 'unstable/text-input/text-input-unstable-v3.xml',
|
'text-input-unstable-v3': wl_protocol_dir / 'unstable/text-input/text-input-unstable-v3.xml',
|
||||||
|
'xdg-activation-v1': wl_protocol_dir / 'staging/xdg-activation/xdg-activation-v1.xml',
|
||||||
'xdg-decoration-unstable-v1': wl_protocol_dir / 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml',
|
'xdg-decoration-unstable-v1': wl_protocol_dir / 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml',
|
||||||
'xdg-foreign-unstable-v1': wl_protocol_dir / 'unstable/xdg-foreign/xdg-foreign-unstable-v1.xml',
|
'xdg-foreign-unstable-v1': wl_protocol_dir / 'unstable/xdg-foreign/xdg-foreign-unstable-v1.xml',
|
||||||
'xdg-foreign-unstable-v2': wl_protocol_dir / 'unstable/xdg-foreign/xdg-foreign-unstable-v2.xml',
|
'xdg-foreign-unstable-v2': wl_protocol_dir / 'unstable/xdg-foreign/xdg-foreign-unstable-v2.xml',
|
||||||
|
|
|
@ -62,10 +62,10 @@ wlr_files += files(
|
||||||
'wlr_virtual_keyboard_v1.c',
|
'wlr_virtual_keyboard_v1.c',
|
||||||
'wlr_virtual_pointer_v1.c',
|
'wlr_virtual_pointer_v1.c',
|
||||||
'wlr_xcursor_manager.c',
|
'wlr_xcursor_manager.c',
|
||||||
|
'wlr_xdg_activation_v1.c',
|
||||||
'wlr_xdg_decoration_v1.c',
|
'wlr_xdg_decoration_v1.c',
|
||||||
'wlr_xdg_foreign_v1.c',
|
'wlr_xdg_foreign_v1.c',
|
||||||
'wlr_xdg_foreign_v2.c',
|
'wlr_xdg_foreign_v2.c',
|
||||||
'wlr_xdg_foreign_registry.c',
|
'wlr_xdg_foreign_registry.c',
|
||||||
'wlr_xdg_output_v1.c',
|
'wlr_xdg_output_v1.c',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,327 @@
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <wlr/types/wlr_seat.h>
|
||||||
|
#include <wlr/types/wlr_surface.h>
|
||||||
|
#include <wlr/types/wlr_xdg_activation_v1.h>
|
||||||
|
#include <wlr/util/log.h>
|
||||||
|
#include "util/signal.h"
|
||||||
|
#include "util/token.h"
|
||||||
|
#include "xdg-activation-v1-protocol.h"
|
||||||
|
|
||||||
|
#define XDG_ACTIVATION_V1_VERSION 1
|
||||||
|
|
||||||
|
static const struct xdg_activation_token_v1_interface token_impl;
|
||||||
|
|
||||||
|
static struct wlr_xdg_activation_token_v1 *token_from_resource(
|
||||||
|
struct wl_resource *resource) {
|
||||||
|
assert(wl_resource_instance_of(resource,
|
||||||
|
&xdg_activation_token_v1_interface, &token_impl));
|
||||||
|
return wl_resource_get_user_data(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void token_destroy(struct wlr_xdg_activation_token_v1 *token) {
|
||||||
|
if (token == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (token->resource != NULL) {
|
||||||
|
wl_resource_set_user_data(token->resource, NULL); // make inert
|
||||||
|
}
|
||||||
|
wl_list_remove(&token->link);
|
||||||
|
wl_list_remove(&token->seat_destroy.link);
|
||||||
|
wl_list_remove(&token->surface_destroy.link);
|
||||||
|
free(token->app_id);
|
||||||
|
free(token->token);
|
||||||
|
free(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void token_handle_resource_destroy(struct wl_resource *resource) {
|
||||||
|
struct wlr_xdg_activation_token_v1 *token = token_from_resource(resource);
|
||||||
|
token_destroy(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void token_handle_destroy(struct wl_client *client,
|
||||||
|
struct wl_resource *token_resource) {
|
||||||
|
wl_resource_destroy(token_resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void token_handle_commit(struct wl_client *client,
|
||||||
|
struct wl_resource *token_resource) {
|
||||||
|
struct wlr_xdg_activation_token_v1 *token =
|
||||||
|
token_from_resource(token_resource);
|
||||||
|
if (token == NULL) {
|
||||||
|
wl_resource_post_error(token_resource,
|
||||||
|
XDG_ACTIVATION_TOKEN_V1_ERROR_ALREADY_USED,
|
||||||
|
"The activation token has already been used");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the token resource inert
|
||||||
|
wl_resource_set_user_data(token->resource, NULL);
|
||||||
|
token->resource = NULL;
|
||||||
|
|
||||||
|
char token_str[TOKEN_STRLEN + 1] = {0};
|
||||||
|
if (!generate_token(token_str)) {
|
||||||
|
wl_client_post_no_memory(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token->seat != NULL) {
|
||||||
|
struct wlr_seat_client *seat_client =
|
||||||
|
wlr_seat_client_for_wl_client(token->seat, client);
|
||||||
|
if (seat_client == NULL ||
|
||||||
|
!wlr_seat_client_validate_event_serial(seat_client, token->serial)) {
|
||||||
|
wlr_log(WLR_DEBUG, "Rejecting token commit request: "
|
||||||
|
"serial %"PRIu32" was never given to client", token->serial);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token->surface != NULL &&
|
||||||
|
token->surface != token->seat->keyboard_state.focused_surface) {
|
||||||
|
wlr_log(WLR_DEBUG, "Rejecting token commit request: "
|
||||||
|
"surface doesn't have keyboard focus");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token->token = strdup(token_str);
|
||||||
|
if (token->token == NULL) {
|
||||||
|
wl_client_post_no_memory(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(wl_list_empty(&token->link));
|
||||||
|
wl_list_insert(&token->activation->tokens, &token->link);
|
||||||
|
|
||||||
|
xdg_activation_token_v1_send_done(token_resource, token_str);
|
||||||
|
|
||||||
|
// TODO: figure out when to discard the token
|
||||||
|
// TODO: consider emitting a new_token event
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
error:
|
||||||
|
// Here we send the generated token, but it's invalid and can't be used to
|
||||||
|
// request activation.
|
||||||
|
xdg_activation_token_v1_send_done(token_resource, token_str);
|
||||||
|
token_destroy(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void token_handle_set_app_id(struct wl_client *client,
|
||||||
|
struct wl_resource *token_resource, const char *app_id) {
|
||||||
|
struct wlr_xdg_activation_token_v1 *token =
|
||||||
|
token_from_resource(token_resource);
|
||||||
|
if (token == NULL) {
|
||||||
|
wl_resource_post_error(token_resource,
|
||||||
|
XDG_ACTIVATION_TOKEN_V1_ERROR_ALREADY_USED,
|
||||||
|
"The activation token has already been used");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(token->app_id);
|
||||||
|
token->app_id = strdup(app_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void token_handle_seat_destroy(struct wl_listener *listener, void *data) {
|
||||||
|
struct wlr_xdg_activation_token_v1 *token =
|
||||||
|
wl_container_of(listener, token, seat_destroy);
|
||||||
|
wl_list_remove(&token->seat_destroy.link);
|
||||||
|
wl_list_init(&token->seat_destroy.link);
|
||||||
|
token->serial = 0;
|
||||||
|
token->seat = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void token_handle_set_serial(struct wl_client *client,
|
||||||
|
struct wl_resource *token_resource, uint32_t serial,
|
||||||
|
struct wl_resource *seat_resource) {
|
||||||
|
struct wlr_xdg_activation_token_v1 *token =
|
||||||
|
token_from_resource(token_resource);
|
||||||
|
if (token == NULL) {
|
||||||
|
wl_resource_post_error(token_resource,
|
||||||
|
XDG_ACTIVATION_TOKEN_V1_ERROR_ALREADY_USED,
|
||||||
|
"The activation token has already been used");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_seat_client *seat_client =
|
||||||
|
wlr_seat_client_from_resource(seat_resource);
|
||||||
|
if (seat_client == NULL) {
|
||||||
|
wlr_log(WLR_DEBUG, "Rejecting token set_serial request: seat is inert");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
token->seat = seat_client->seat;
|
||||||
|
token->serial = serial;
|
||||||
|
|
||||||
|
token->seat_destroy.notify = token_handle_seat_destroy;
|
||||||
|
wl_list_remove(&token->seat_destroy.link);
|
||||||
|
wl_signal_add(&token->seat->events.destroy, &token->seat_destroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void token_handle_surface_destroy(struct wl_listener *listener, void *data) {
|
||||||
|
struct wlr_xdg_activation_token_v1 *token =
|
||||||
|
wl_container_of(listener, token, surface_destroy);
|
||||||
|
wl_list_remove(&token->surface_destroy.link);
|
||||||
|
wl_list_init(&token->surface_destroy.link);
|
||||||
|
token->surface = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void token_handle_set_surface(struct wl_client *client,
|
||||||
|
struct wl_resource *token_resource,
|
||||||
|
struct wl_resource *surface_resource) {
|
||||||
|
struct wlr_xdg_activation_token_v1 *token =
|
||||||
|
token_from_resource(token_resource);
|
||||||
|
struct wlr_surface *surface = wlr_surface_from_resource(surface_resource);
|
||||||
|
if (token == NULL) {
|
||||||
|
wl_resource_post_error(token_resource,
|
||||||
|
XDG_ACTIVATION_TOKEN_V1_ERROR_ALREADY_USED,
|
||||||
|
"The activation token has already been used");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
token->surface = surface;
|
||||||
|
|
||||||
|
token->surface_destroy.notify = token_handle_surface_destroy;
|
||||||
|
wl_list_remove(&token->surface_destroy.link);
|
||||||
|
wl_signal_add(&surface->events.destroy, &token->surface_destroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct xdg_activation_token_v1_interface token_impl = {
|
||||||
|
.destroy = token_handle_destroy,
|
||||||
|
.commit = token_handle_commit,
|
||||||
|
.set_app_id = token_handle_set_app_id,
|
||||||
|
.set_serial = token_handle_set_serial,
|
||||||
|
.set_surface = token_handle_set_surface,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct xdg_activation_v1_interface activation_impl;
|
||||||
|
|
||||||
|
static struct wlr_xdg_activation_v1 *activation_from_resource(
|
||||||
|
struct wl_resource *resource) {
|
||||||
|
assert(wl_resource_instance_of(resource,
|
||||||
|
&xdg_activation_v1_interface, &activation_impl));
|
||||||
|
return wl_resource_get_user_data(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void activation_handle_destroy(struct wl_client *client,
|
||||||
|
struct wl_resource *activation_resource) {
|
||||||
|
wl_resource_destroy(activation_resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void activation_handle_get_activation_token(struct wl_client *client,
|
||||||
|
struct wl_resource *activation_resource, uint32_t id) {
|
||||||
|
struct wlr_xdg_activation_v1 *activation =
|
||||||
|
activation_from_resource(activation_resource);
|
||||||
|
|
||||||
|
struct wlr_xdg_activation_token_v1 *token = calloc(1, sizeof(*token));
|
||||||
|
if (token == NULL) {
|
||||||
|
wl_client_post_no_memory(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_list_init(&token->link);
|
||||||
|
wl_list_init(&token->seat_destroy.link);
|
||||||
|
wl_list_init(&token->surface_destroy.link);
|
||||||
|
|
||||||
|
token->activation = activation;
|
||||||
|
|
||||||
|
uint32_t version = wl_resource_get_version(activation_resource);
|
||||||
|
token->resource = wl_resource_create(client,
|
||||||
|
&xdg_activation_token_v1_interface, version, id);
|
||||||
|
if (token->resource == NULL) {
|
||||||
|
free(token);
|
||||||
|
wl_client_post_no_memory(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_resource_set_implementation(token->resource, &token_impl, token,
|
||||||
|
token_handle_resource_destroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void activation_handle_activate(struct wl_client *client,
|
||||||
|
struct wl_resource *activation_resource, const char *token_str,
|
||||||
|
struct wl_resource *surface_resource) {
|
||||||
|
struct wlr_xdg_activation_v1 *activation =
|
||||||
|
activation_from_resource(activation_resource);
|
||||||
|
struct wlr_surface *surface = wlr_surface_from_resource(surface_resource);
|
||||||
|
|
||||||
|
struct wlr_xdg_activation_token_v1 *token;
|
||||||
|
bool found = false;
|
||||||
|
wl_list_for_each(token, &activation->tokens, link) {
|
||||||
|
if (strcmp(token_str, token->token) == 0) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
wlr_log(WLR_DEBUG, "Rejecting activate request: unknown token");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_xdg_activation_v1_request_activate_event event = {
|
||||||
|
.activation = activation,
|
||||||
|
.token = token,
|
||||||
|
.surface = surface,
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&activation->events.request_activate, &event);
|
||||||
|
|
||||||
|
token_destroy(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct xdg_activation_v1_interface activation_impl = {
|
||||||
|
.destroy = activation_handle_destroy,
|
||||||
|
.get_activation_token = activation_handle_get_activation_token,
|
||||||
|
.activate = activation_handle_activate,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void activation_bind(struct wl_client *client, void *data,
|
||||||
|
uint32_t version, uint32_t id) {
|
||||||
|
struct wlr_xdg_activation_v1 *activation = data;
|
||||||
|
|
||||||
|
struct wl_resource *resource = wl_resource_create(client,
|
||||||
|
&xdg_activation_v1_interface, version, id);
|
||||||
|
if (resource == NULL) {
|
||||||
|
wl_client_post_no_memory(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_resource_set_implementation(resource, &activation_impl, activation, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
|
struct wlr_xdg_activation_v1 *activation =
|
||||||
|
wl_container_of(listener, activation, display_destroy);
|
||||||
|
wlr_signal_emit_safe(&activation->events.destroy, NULL);
|
||||||
|
|
||||||
|
struct wlr_xdg_activation_token_v1 *token, *token_tmp;
|
||||||
|
wl_list_for_each_safe(token, token_tmp, &activation->tokens, link) {
|
||||||
|
token_destroy(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_list_remove(&activation->display_destroy.link);
|
||||||
|
wl_global_destroy(activation->global);
|
||||||
|
free(activation);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_xdg_activation_v1 *wlr_xdg_activation_v1_create(
|
||||||
|
struct wl_display *display) {
|
||||||
|
struct wlr_xdg_activation_v1 *activation = calloc(1, sizeof(*activation));
|
||||||
|
if (activation == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_list_init(&activation->tokens);
|
||||||
|
wl_signal_init(&activation->events.destroy);
|
||||||
|
wl_signal_init(&activation->events.request_activate);
|
||||||
|
|
||||||
|
activation->global = wl_global_create(display,
|
||||||
|
&xdg_activation_v1_interface, XDG_ACTIVATION_V1_VERSION, activation,
|
||||||
|
activation_bind);
|
||||||
|
if (activation->global == NULL) {
|
||||||
|
free(activation->global);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
activation->display_destroy.notify = handle_display_destroy;
|
||||||
|
wl_display_add_destroy_listener(display, &activation->display_destroy);
|
||||||
|
|
||||||
|
return activation;
|
||||||
|
}
|
Loading…
Reference in New Issue