wlroots-hyprland/types/seat/wlr_seat.c

493 lines
15 KiB
C
Raw Permalink Normal View History

2018-05-02 11:03:26 +02:00
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wayland-server-core.h>
2018-05-02 11:03:26 +02:00
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_primary_selection.h>
2018-05-02 11:03:26 +02:00
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_compositor.h>
2018-05-02 11:03:26 +02:00
#include <wlr/util/log.h>
#include "types/wlr_seat.h"
2020-02-03 16:42:36 +01:00
#include "util/global.h"
2018-05-02 11:03:26 +02:00
#define SEAT_VERSION 9
2018-05-03 23:03:44 +02:00
2018-05-02 11:03:26 +02:00
static void seat_handle_get_pointer(struct wl_client *client,
struct wl_resource *seat_resource, uint32_t id) {
uint32_t version = wl_resource_get_version(seat_resource);
2018-05-02 11:03:26 +02:00
struct wlr_seat_client *seat_client =
wlr_seat_client_from_resource(seat_resource);
if (!seat_client) {
// The client still needs a resource, so here's a dummy:
seat_client_create_inert_pointer(client, version, id);
return;
}
if (!(seat_client->seat->accumulated_capabilities & WL_SEAT_CAPABILITY_POINTER)) {
wl_resource_post_error(seat_resource, WL_SEAT_ERROR_MISSING_CAPABILITY,
"wl_seat.get_pointer called when no pointer capability has existed");
2018-05-02 11:03:26 +02:00
return;
}
seat_client_create_pointer(seat_client, version, id);
}
static void seat_handle_get_keyboard(struct wl_client *client,
struct wl_resource *seat_resource, uint32_t id) {
uint32_t version = wl_resource_get_version(seat_resource);
2018-05-02 11:03:26 +02:00
struct wlr_seat_client *seat_client =
wlr_seat_client_from_resource(seat_resource);
if (!seat_client) {
seat_client_create_inert_keyboard(client, version, id);
return;
}
if (!(seat_client->seat->accumulated_capabilities & WL_SEAT_CAPABILITY_KEYBOARD)) {
wl_resource_post_error(seat_resource, WL_SEAT_ERROR_MISSING_CAPABILITY,
"wl_seat.get_keyboard called when no keyboard capability has existed");
2018-05-02 11:03:26 +02:00
return;
}
seat_client_create_keyboard(seat_client, version, id);
}
static void seat_handle_get_touch(struct wl_client *client,
struct wl_resource *seat_resource, uint32_t id) {
uint32_t version = wl_resource_get_version(seat_resource);
2018-05-02 11:03:26 +02:00
struct wlr_seat_client *seat_client =
wlr_seat_client_from_resource(seat_resource);
if (!seat_client) {
seat_client_create_inert_touch(client, version, id);
return;
}
if (!(seat_client->seat->accumulated_capabilities & WL_SEAT_CAPABILITY_TOUCH)) {
wl_resource_post_error(seat_resource, WL_SEAT_ERROR_MISSING_CAPABILITY,
"wl_seat.get_touch called when no touch capability has existed");
2018-05-02 11:03:26 +02:00
return;
}
seat_client_create_touch(seat_client, version, id);
}
static void seat_client_destroy(struct wlr_seat_client *client) {
2022-08-18 13:16:16 +02:00
wl_signal_emit_mutable(&client->events.destroy, client);
2018-05-02 11:03:26 +02:00
if (client == client->seat->pointer_state.focused_client) {
client->seat->pointer_state.focused_client = NULL;
}
if (client == client->seat->keyboard_state.focused_client) {
client->seat->keyboard_state.focused_client = NULL;
}
if (client->seat->drag && client == client->seat->drag->seat_client) {
client->seat->drag->seat_client = NULL;
}
2018-05-02 11:03:26 +02:00
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe(resource, tmp, &client->pointers) {
seat_client_destroy_pointer(resource);
2018-05-02 11:03:26 +02:00
}
wl_resource_for_each_safe(resource, tmp, &client->keyboards) {
seat_client_destroy_keyboard(resource);
2018-05-02 11:03:26 +02:00
}
wl_resource_for_each_safe(resource, tmp, &client->touches) {
seat_client_destroy_touch(resource);
2018-05-02 11:03:26 +02:00
}
wl_resource_for_each_safe(resource, tmp, &client->data_devices) {
// Make the data device inert
wl_list_remove(wl_resource_get_link(resource));
wl_list_init(wl_resource_get_link(resource));
wl_resource_set_user_data(resource, NULL);
}
wl_resource_for_each_safe(resource, tmp, &client->resources) {
// Make the seat resource inert
wl_list_remove(wl_resource_get_link(resource));
wl_list_init(wl_resource_get_link(resource));
wl_resource_set_user_data(resource, NULL);
2018-05-02 11:03:26 +02:00
}
wl_list_remove(&client->link);
free(client);
}
static void seat_client_handle_resource_destroy(
struct wl_resource *seat_resource) {
struct wlr_seat_client *client =
wlr_seat_client_from_resource(seat_resource);
if (!client) {
return;
}
wl_list_remove(wl_resource_get_link(seat_resource));
if (!wl_list_empty(&client->resources)) {
return;
}
seat_client_destroy(client);
}
2018-05-02 11:03:26 +02:00
static void seat_handle_release(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static const struct wl_seat_interface seat_impl = {
.get_pointer = seat_handle_get_pointer,
.get_keyboard = seat_handle_get_keyboard,
.get_touch = seat_handle_get_touch,
.release = seat_handle_release,
};
static struct wlr_seat_client *seat_client_create(struct wlr_seat *wlr_seat,
struct wl_client *client, struct wl_resource *wl_resource) {
struct wlr_seat_client *seat_client = calloc(1, sizeof(*seat_client));
if (!seat_client) {
return NULL;
}
seat_client->client = client;
seat_client->seat = wlr_seat;
wl_list_init(&seat_client->resources);
wl_list_init(&seat_client->pointers);
wl_list_init(&seat_client->keyboards);
wl_list_init(&seat_client->touches);
wl_list_init(&seat_client->data_devices);
wl_signal_init(&seat_client->events.destroy);
wl_list_insert(&wlr_seat->clients, &seat_client->link);
struct wlr_surface *pointer_focus =
wlr_seat->pointer_state.focused_surface;
if (pointer_focus != NULL &&
wl_resource_get_client(pointer_focus->resource) == client) {
wlr_seat->pointer_state.focused_client = seat_client;
}
struct wlr_surface *keyboard_focus =
wlr_seat->keyboard_state.focused_surface;
if (keyboard_focus != NULL &&
wl_resource_get_client(keyboard_focus->resource) == client) {
wlr_seat->keyboard_state.focused_client = seat_client;
}
return seat_client;
}
2018-05-02 11:03:26 +02:00
static void seat_handle_bind(struct wl_client *client, void *_wlr_seat,
uint32_t version, uint32_t id) {
// `wlr_seat` can be NULL if the seat global is being destroyed
2018-05-02 11:03:26 +02:00
struct wlr_seat *wlr_seat = _wlr_seat;
struct wl_resource *wl_resource =
2018-05-02 11:03:26 +02:00
wl_resource_create(client, &wl_seat_interface, version, id);
if (wl_resource == NULL) {
2018-05-02 11:03:26 +02:00
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(wl_resource, &seat_impl, NULL,
seat_client_handle_resource_destroy);
wl_list_init(wl_resource_get_link(wl_resource));
if (wlr_seat == NULL) {
return;
}
struct wlr_seat_client *seat_client =
wlr_seat_client_for_wl_client(wlr_seat, client);
if (!seat_client) {
seat_client = seat_client_create(wlr_seat, client, wl_resource);
}
if (seat_client == NULL) {
wl_resource_destroy(wl_resource);
wl_client_post_no_memory(client);
return;
}
wl_resource_set_user_data(wl_resource, seat_client);
wl_list_insert(&seat_client->resources, wl_resource_get_link(wl_resource));
2018-05-02 11:03:26 +02:00
if (version >= WL_SEAT_NAME_SINCE_VERSION) {
wl_seat_send_name(wl_resource, wlr_seat->name);
2018-05-02 11:03:26 +02:00
}
wl_seat_send_capabilities(wl_resource, wlr_seat->capabilities);
2018-05-02 11:03:26 +02:00
}
void wlr_seat_destroy(struct wlr_seat *seat) {
if (!seat) {
return;
}
wlr_seat_pointer_clear_focus(seat);
wlr_seat_keyboard_clear_focus(seat);
wlr_seat_set_keyboard(seat, NULL);
struct wlr_touch_point *point;
wl_list_for_each(point, &seat->touch_state.touch_points, link) {
wlr_seat_touch_point_clear_focus(seat, 0, point->touch_id);
}
2022-08-18 13:16:16 +02:00
wl_signal_emit_mutable(&seat->events.destroy, seat);
2018-05-02 11:03:26 +02:00
wl_list_remove(&seat->display_destroy.link);
2018-12-09 16:45:31 +01:00
wlr_data_source_destroy(seat->selection_source);
wlr_primary_selection_source_destroy(seat->primary_selection_source);
2018-05-02 11:03:26 +02:00
struct wlr_seat_client *client, *tmp;
wl_list_for_each_safe(client, tmp, &seat->clients, link) {
seat_client_destroy(client);
2018-05-02 11:03:26 +02:00
}
wlr_global_destroy_safe(seat->global);
2018-05-02 11:03:26 +02:00
free(seat->pointer_state.default_grab);
free(seat->keyboard_state.default_grab);
free(seat->touch_state.default_grab);
free(seat->name);
free(seat);
}
static void handle_display_destroy(struct wl_listener *listener, void *data) {
struct wlr_seat *seat =
wl_container_of(listener, seat, display_destroy);
wlr_seat_destroy(seat);
}
struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) {
struct wlr_seat *seat = calloc(1, sizeof(*seat));
2018-05-03 23:03:44 +02:00
if (!seat) {
2018-05-02 11:03:26 +02:00
return NULL;
}
// pointer state
2018-05-03 23:03:44 +02:00
seat->pointer_state.seat = seat;
wl_list_init(&seat->pointer_state.surface_destroy.link);
2018-05-02 11:03:26 +02:00
struct wlr_seat_pointer_grab *pointer_grab = calloc(1, sizeof(*pointer_grab));
2018-05-02 11:03:26 +02:00
if (!pointer_grab) {
2018-05-03 23:03:44 +02:00
free(seat);
2018-05-02 11:03:26 +02:00
return NULL;
}
pointer_grab->interface = &default_pointer_grab_impl;
2018-05-03 23:03:44 +02:00
pointer_grab->seat = seat;
seat->pointer_state.default_grab = pointer_grab;
seat->pointer_state.grab = pointer_grab;
2018-05-02 11:03:26 +02:00
2018-08-04 23:22:21 +02:00
wl_signal_init(&seat->pointer_state.events.focus_change);
2018-05-02 11:03:26 +02:00
// keyboard state
struct wlr_seat_keyboard_grab *keyboard_grab = calloc(1, sizeof(*keyboard_grab));
2018-05-02 11:03:26 +02:00
if (!keyboard_grab) {
free(pointer_grab);
2018-05-03 23:03:44 +02:00
free(seat);
2018-05-02 11:03:26 +02:00
return NULL;
}
keyboard_grab->interface = &default_keyboard_grab_impl;
2018-05-03 23:03:44 +02:00
keyboard_grab->seat = seat;
seat->keyboard_state.default_grab = keyboard_grab;
seat->keyboard_state.grab = keyboard_grab;
2018-05-02 11:03:26 +02:00
2018-05-03 23:03:44 +02:00
seat->keyboard_state.seat = seat;
wl_list_init(&seat->keyboard_state.surface_destroy.link);
2018-05-02 11:03:26 +02:00
gtk-primary-selection: refactor everything, untie from seat This commits completely refactors wlr_gtk_primary_selection. The goal is to remove gtk-primary-selection state from the seat and better handle inert resources where it makes sense. wlr_seat_client.primary_selection_devices has been removed and replaced by wlr_gtk_primary_selection_device. This allows us to make offers inert when the current selection is replaced. wlr_seat_set_primary_selection has been removed because it relied on wlr_seat instead of wlr_gtk_primary_selection_device_manager. A new function, wlr_gtk_primary_selection_device_manager_set_selection (candidate for the longest function name in wlroots) has been added. It doesn't take a serial anymore as serial checking only makes sense for set_selection requests coming from Wayland clients (serial checking is now done in the Wayland interface implementation). Since wlr_gtk_primary_selection_device_manager is now required to set the selection, a new function wlr_xwayland_set_gtk_primary_selection_device_manager (candidate number two for longest function name) has been added. Devices are now made inert when the seat goes away. Future work includes removing the last primary selection bits from the seat, mainly wlr_seat.primary_selection_source and wlr_seat.events.primary_selection, replacing those with new fields in wlr_gtk_primary_selection_device. Or maybe we could keep those in the seat and replace them with a re-usable interface (for future zwp_primary_selection_v1 support). We need to think how we'll sync these three protocols (GTK, X11 and wayland-protocols). See https://github.com/swaywm/wlroots/issues/1388
2018-11-27 18:41:46 +01:00
wl_signal_init(&seat->keyboard_state.events.focus_change);
2018-05-02 11:03:26 +02:00
// touch state
struct wlr_seat_touch_grab *touch_grab = calloc(1, sizeof(*touch_grab));
2018-05-02 11:03:26 +02:00
if (!touch_grab) {
free(pointer_grab);
free(keyboard_grab);
2018-05-03 23:03:44 +02:00
free(seat);
2018-05-02 11:03:26 +02:00
return NULL;
}
touch_grab->interface = &default_touch_grab_impl;
2018-05-03 23:03:44 +02:00
touch_grab->seat = seat;
seat->touch_state.default_grab = touch_grab;
seat->touch_state.grab = touch_grab;
2018-05-02 11:03:26 +02:00
2018-05-03 23:03:44 +02:00
seat->touch_state.seat = seat;
wl_list_init(&seat->touch_state.touch_points);
2018-05-02 11:03:26 +02:00
seat->global = wl_global_create(display, &wl_seat_interface,
SEAT_VERSION, seat, seat_handle_bind);
if (seat->global == NULL) {
2018-05-03 23:03:44 +02:00
free(touch_grab);
free(pointer_grab);
free(keyboard_grab);
free(seat);
2018-05-02 11:03:26 +02:00
return NULL;
}
2018-05-03 23:03:44 +02:00
seat->display = display;
seat->name = strdup(name);
wl_list_init(&seat->clients);
wl_list_init(&seat->selection_offers);
wl_list_init(&seat->drag_offers);
2018-05-02 11:03:26 +02:00
2019-01-30 18:36:19 +01:00
wl_signal_init(&seat->events.request_start_drag);
2018-05-03 23:03:44 +02:00
wl_signal_init(&seat->events.start_drag);
2018-05-02 11:03:26 +02:00
2018-05-03 23:03:44 +02:00
wl_signal_init(&seat->events.request_set_cursor);
2018-05-02 11:03:26 +02:00
wl_signal_init(&seat->events.request_set_selection);
wl_signal_init(&seat->events.set_selection);
wl_signal_init(&seat->events.request_set_primary_selection);
wl_signal_init(&seat->events.set_primary_selection);
2018-05-02 11:03:26 +02:00
2018-05-03 23:03:44 +02:00
wl_signal_init(&seat->events.pointer_grab_begin);
wl_signal_init(&seat->events.pointer_grab_end);
2018-05-02 11:03:26 +02:00
2018-05-03 23:03:44 +02:00
wl_signal_init(&seat->events.keyboard_grab_begin);
wl_signal_init(&seat->events.keyboard_grab_end);
2018-05-02 11:03:26 +02:00
2018-05-03 23:03:44 +02:00
wl_signal_init(&seat->events.touch_grab_begin);
wl_signal_init(&seat->events.touch_grab_end);
2018-05-02 11:03:26 +02:00
2018-05-03 23:03:44 +02:00
wl_signal_init(&seat->events.destroy);
2018-05-02 11:03:26 +02:00
2018-05-03 23:03:44 +02:00
seat->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &seat->display_destroy);
2018-05-02 11:03:26 +02:00
2018-05-03 23:03:44 +02:00
return seat;
2018-05-02 11:03:26 +02:00
}
struct wlr_seat_client *wlr_seat_client_for_wl_client(struct wlr_seat *wlr_seat,
struct wl_client *wl_client) {
struct wlr_seat_client *seat_client;
wl_list_for_each(seat_client, &wlr_seat->clients, link) {
if (seat_client->client == wl_client) {
return seat_client;
}
}
return NULL;
}
void wlr_seat_set_capabilities(struct wlr_seat *wlr_seat,
uint32_t capabilities) {
// if the capabilities haven't changed (i.e a redundant mouse was removed),
// we don't actually have to do anything
if (capabilities == wlr_seat->capabilities) {
return;
}
2018-05-02 11:03:26 +02:00
wlr_seat->capabilities = capabilities;
wlr_seat->accumulated_capabilities |= capabilities;
2018-05-03 23:03:44 +02:00
2018-05-02 11:03:26 +02:00
struct wlr_seat_client *client;
wl_list_for_each(client, &wlr_seat->clients, link) {
2018-05-03 23:03:44 +02:00
// Make resources inert if necessary
if ((capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) {
struct wlr_seat_client *focused_client =
wlr_seat->pointer_state.focused_client;
struct wlr_surface *focused_surface =
wlr_seat->pointer_state.focused_surface;
if (focused_client != NULL && focused_surface != NULL) {
seat_client_send_pointer_leave_raw(focused_client, focused_surface);
}
// Note: we don't set focused client/surface to NULL since we need
// them to send the enter event if the pointer is recreated
2018-05-03 23:03:44 +02:00
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe(resource, tmp, &client->pointers) {
seat_client_destroy_pointer(resource);
}
}
if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) == 0) {
struct wlr_seat_client *focused_client =
wlr_seat->keyboard_state.focused_client;
struct wlr_surface *focused_surface =
wlr_seat->keyboard_state.focused_surface;
if (focused_client != NULL && focused_surface != NULL) {
seat_client_send_keyboard_leave_raw(focused_client,
focused_surface);
}
// Note: we don't set focused client/surface to NULL since we need
// them to send the enter event if the keyboard is recreated
2018-05-03 23:03:44 +02:00
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe(resource, tmp, &client->keyboards) {
seat_client_destroy_keyboard(resource);
}
}
if ((capabilities & WL_SEAT_CAPABILITY_TOUCH) == 0) {
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe(resource, tmp, &client->touches) {
seat_client_destroy_touch(resource);
}
}
struct wl_resource *resource;
wl_resource_for_each(resource, &client->resources) {
wl_seat_send_capabilities(resource, capabilities);
}
2018-05-02 11:03:26 +02:00
}
}
void wlr_seat_set_name(struct wlr_seat *wlr_seat, const char *name) {
free(wlr_seat->name);
wlr_seat->name = strdup(name);
struct wlr_seat_client *client;
wl_list_for_each(client, &wlr_seat->clients, link) {
struct wl_resource *resource;
wl_resource_for_each(resource, &client->resources) {
wl_seat_send_name(resource, name);
}
2018-05-02 11:03:26 +02:00
}
}
struct wlr_seat_client *wlr_seat_client_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, &wl_seat_interface,
&seat_impl));
return wl_resource_get_user_data(resource);
}
uint32_t wlr_seat_client_next_serial(struct wlr_seat_client *client) {
uint32_t serial = wl_display_next_serial(wl_client_get_display(client->client));
struct wlr_serial_ringset *set = &client->serials;
if (set->count == 0) {
set->data[0].min_incl = serial;
set->data[0].max_incl = serial;
set->count = 1;
set->end = 0;
} else if (set->data[set->end].max_incl + 1 != serial) {
if (set->count < WLR_SERIAL_RINGSET_SIZE) {
set->count++;
}
set->end = (set->end + 1) % WLR_SERIAL_RINGSET_SIZE;
set->data[set->end].min_incl = serial;
set->data[set->end].max_incl = serial;
} else {
set->data[set->end].max_incl = serial;
}
return serial;
}
bool wlr_seat_client_validate_event_serial(struct wlr_seat_client *client, uint32_t serial) {
uint32_t cur = wl_display_get_serial(wl_client_get_display(client->client));
struct wlr_serial_ringset *set = &client->serials;
uint32_t rev_dist = cur - serial;
if (rev_dist >= UINT32_MAX / 2) {
// serial is closer to being 'newer' instead of 'older' than
// the current serial, so it's either invalid or incredibly old
return false;
}
for (int i = 0; i < set->count; i++) {
int j = (set->end - i + WLR_SERIAL_RINGSET_SIZE) % WLR_SERIAL_RINGSET_SIZE;
if (rev_dist < cur - set->data[j].max_incl) {
return false;
}
if (rev_dist <= cur - set->data[j].min_incl) {
return true;
}
}
// Iff the set is full, then `rev_dist` is large enough that serial
// could already have been recycled out of the set.
return set->count == WLR_SERIAL_RINGSET_SIZE;
}