#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #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; }