labwc/src/seat.c
Consolatis 9e6aaa689a project wide: clean up event listeners on shutdown
This ensures all event listeners are removed before the emitting
wlroots object is being destroyed. This will be enforced with asserts
in wlroots 0.19 but there is no reason to not do it right now either.

This change in wlroots 0.19 is implemented via commit
8f56f7ca43257cc05c7c4eb57a0f541e05cf9a79
"Assert (almost all) signals have no attached listeners on destroy"
2025-03-13 17:33:51 +09:00

870 lines
25 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
#include <assert.h>
#include <stdbool.h>
#include <strings.h>
#include <wlr/backend/libinput.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/util/log.h>
#include "common/mem.h"
#include "input/ime.h"
#include "input/tablet.h"
#include "input/tablet-pad.h"
#include "input/input.h"
#include "input/keyboard.h"
#include "input/key-state.h"
#include "labwc.h"
#include "view.h"
static void
input_device_destroy(struct wl_listener *listener, void *data)
{
struct input *input = wl_container_of(listener, input, destroy);
wl_list_remove(&input->link);
wl_list_remove(&input->destroy.link);
/* `struct keyboard` is derived and has some extra clean up to do */
if (input->wlr_input_device->type == WLR_INPUT_DEVICE_KEYBOARD) {
struct keyboard *keyboard = (struct keyboard *)input;
wl_list_remove(&keyboard->key.link);
wl_list_remove(&keyboard->modifier.link);
keyboard_cancel_keybind_repeat(keyboard);
}
free(input);
}
static enum lab_libinput_device_type
device_type_from_wlr_device(struct wlr_input_device *wlr_input_device)
{
switch (wlr_input_device->type) {
case WLR_INPUT_DEVICE_TOUCH:
case WLR_INPUT_DEVICE_TABLET:
return LAB_LIBINPUT_DEVICE_TOUCH;
default:
break;
}
if (wlr_input_device->type == WLR_INPUT_DEVICE_POINTER &&
wlr_input_device_is_libinput(wlr_input_device)) {
struct libinput_device *libinput_device =
wlr_libinput_get_device_handle(wlr_input_device);
if (libinput_device_config_tap_get_finger_count(libinput_device) > 0) {
return LAB_LIBINPUT_DEVICE_TOUCHPAD;
}
}
return LAB_LIBINPUT_DEVICE_NON_TOUCH;
}
/*
* Get applicable profile (category) by matching first by name and secondly be
* type (e.g. 'touch' and 'non-touch'). If not suitable match is found based on
* those two criteria we fallback on 'default'.
*/
static struct libinput_category *
get_category(struct wlr_input_device *device)
{
/* By name */
struct libinput_category *category;
wl_list_for_each_reverse(category, &rc.libinput_categories, link) {
if (category->name) {
if (!strcasecmp(device->name, category->name)) {
return category;
}
}
}
/* By type */
enum lab_libinput_device_type type = device_type_from_wlr_device(device);
wl_list_for_each_reverse(category, &rc.libinput_categories, link) {
if (category->type == type) {
return category;
}
}
/* Use default profile as a fallback */
return libinput_category_get_default();
}
static void
configure_libinput(struct wlr_input_device *wlr_input_device)
{
/*
* TODO: We do not check any return values for the various
* libinput_device_config_*_set_*() calls. It would
* be nice if we could inform the users via log file
* that some libinput setting could not be applied.
*
* TODO: We are currently using int32_t with -1 as default
* to describe the not-configured state. This is not
* really optimal as we can't properly deal with
* enum values that are 0. After some discussion via
* IRC the best way forward seem to be to use a
* uint32_t instead and UINT32_MAX as indicator for
* a not-configured state. This allows to properly
* test the enum being a member of a bitset via
* mask & value == value. All libinput enums are
* way below UINT32_MAX.
*/
if (!wlr_input_device) {
wlr_log(WLR_ERROR, "no wlr_input_device");
return;
}
struct input *input = wlr_input_device->data;
/* Set scroll factor to 1.0 for Wayland/X11 backends or virtual pointers */
if (!wlr_input_device_is_libinput(wlr_input_device)) {
input->scroll_factor = 1.0;
return;
}
struct libinput_device *libinput_dev =
wlr_libinput_get_device_handle(wlr_input_device);
if (!libinput_dev) {
wlr_log(WLR_ERROR, "no libinput_dev");
return;
}
struct libinput_category *dc = get_category(wlr_input_device);
/*
* The above logic should have always matched SOME category
* (the default category if none other took precedence)
*/
assert(dc);
if (libinput_device_config_tap_get_finger_count(libinput_dev) <= 0) {
wlr_log(WLR_INFO, "tap unavailable");
} else {
wlr_log(WLR_INFO, "tap configured");
libinput_device_config_tap_set_enabled(libinput_dev, dc->tap);
libinput_device_config_tap_set_button_map(libinput_dev,
dc->tap_button_map);
}
if (libinput_device_config_tap_get_finger_count(libinput_dev) <= 0
|| dc->tap_and_drag < 0) {
wlr_log(WLR_INFO, "tap-and-drag not configured");
} else {
wlr_log(WLR_INFO, "tap-and-drag configured");
libinput_device_config_tap_set_drag_enabled(
libinput_dev, dc->tap_and_drag);
}
if (libinput_device_config_tap_get_finger_count(libinput_dev) <= 0
|| dc->drag_lock < 0) {
wlr_log(WLR_INFO, "drag lock not configured");
} else {
wlr_log(WLR_INFO, "drag lock configured");
libinput_device_config_tap_set_drag_lock_enabled(
libinput_dev, dc->drag_lock);
}
if (libinput_device_config_scroll_has_natural_scroll(libinput_dev) <= 0
|| dc->natural_scroll < 0) {
wlr_log(WLR_INFO, "natural scroll not configured");
} else {
wlr_log(WLR_INFO, "natural scroll configured");
libinput_device_config_scroll_set_natural_scroll_enabled(
libinput_dev, dc->natural_scroll);
}
if (libinput_device_config_left_handed_is_available(libinput_dev) <= 0
|| dc->left_handed < 0) {
wlr_log(WLR_INFO, "left-handed mode not configured");
} else {
wlr_log(WLR_INFO, "left-handed mode configured");
libinput_device_config_left_handed_set(libinput_dev,
dc->left_handed);
}
if (libinput_device_config_accel_is_available(libinput_dev) == 0) {
wlr_log(WLR_INFO, "pointer acceleration unavailable");
} else {
wlr_log(WLR_INFO, "pointer acceleration configured");
if (dc->pointer_speed >= -1) {
libinput_device_config_accel_set_speed(libinput_dev,
dc->pointer_speed);
}
if (dc->accel_profile > 0) {
libinput_device_config_accel_set_profile(libinput_dev,
dc->accel_profile);
}
}
if (libinput_device_config_middle_emulation_is_available(libinput_dev)
== 0 || dc->middle_emu < 0) {
wlr_log(WLR_INFO, "middle emulation not configured");
} else {
wlr_log(WLR_INFO, "middle emulation configured");
libinput_device_config_middle_emulation_set_enabled(
libinput_dev, dc->middle_emu);
}
if (libinput_device_config_dwt_is_available(libinput_dev) == 0
|| dc->dwt < 0) {
wlr_log(WLR_INFO, "dwt not configured");
} else {
wlr_log(WLR_INFO, "dwt configured");
libinput_device_config_dwt_set_enabled(libinput_dev, dc->dwt);
}
if ((dc->click_method != LIBINPUT_CONFIG_CLICK_METHOD_NONE
&& (libinput_device_config_click_get_methods(libinput_dev)
& dc->click_method) == 0)
|| dc->click_method < 0) {
wlr_log(WLR_INFO, "click method not configured");
} else {
wlr_log(WLR_INFO, "click method configured");
/*
* Note, the documentation claims that:
* > [...] The device may require changing to a neutral state
* > first before activating the new method.
*
* However, just setting the method seems to work without
* issues.
*/
libinput_device_config_click_set_method(libinput_dev, dc->click_method);
}
if ((dc->send_events_mode != LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
&& (libinput_device_config_send_events_get_modes(libinput_dev)
& dc->send_events_mode) == 0)
|| dc->send_events_mode < 0) {
wlr_log(WLR_INFO, "send events mode not configured");
} else {
wlr_log(WLR_INFO, "send events mode configured");
libinput_device_config_send_events_set_mode(libinput_dev, dc->send_events_mode);
}
/* Non-zero if the device can be calibrated, zero otherwise. */
if (libinput_device_config_calibration_has_matrix(libinput_dev) == 0
|| !dc->have_calibration_matrix) {
wlr_log(WLR_INFO, "calibration matrix not configured");
} else {
wlr_log(WLR_INFO, "calibration matrix configured");
libinput_device_config_calibration_set_matrix(libinput_dev, dc->calibration_matrix);
}
wlr_log(WLR_INFO, "scroll factor configured");
input->scroll_factor = dc->scroll_factor;
}
static struct wlr_output *
output_by_name(struct server *server, const char *name)
{
assert(name);
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
if (!strcasecmp(output->wlr_output->name, name)) {
return output->wlr_output;
}
}
return NULL;
}
static void
map_input_to_output(struct seat *seat, struct wlr_input_device *dev, char *output_name)
{
struct wlr_output *output = NULL;
if (output_name) {
output = output_by_name(seat->server, output_name);
}
wlr_cursor_map_input_to_output(seat->cursor, dev, output);
wlr_cursor_map_input_to_region(seat->cursor, dev, NULL);
}
static void
map_pointer_to_output(struct seat *seat, struct wlr_input_device *dev)
{
struct wlr_pointer *pointer = wlr_pointer_from_input_device(dev);
wlr_log(WLR_INFO, "map pointer to output %s", pointer->output_name);
map_input_to_output(seat, dev, pointer->output_name);
}
static struct input *
new_pointer(struct seat *seat, struct wlr_input_device *dev)
{
struct input *input = znew(*input);
input->wlr_input_device = dev;
dev->data = input;
configure_libinput(dev);
wlr_cursor_attach_input_device(seat->cursor, dev);
/* In support of running with WLR_WL_OUTPUTS set to >=2 */
if (dev->type == WLR_INPUT_DEVICE_POINTER) {
map_pointer_to_output(seat, dev);
}
return input;
}
static struct input *
new_keyboard(struct seat *seat, struct wlr_input_device *device, bool is_virtual)
{
struct wlr_keyboard *kb = wlr_keyboard_from_input_device(device);
struct keyboard *keyboard = znew(*keyboard);
keyboard->base.wlr_input_device = device;
keyboard->wlr_keyboard = kb;
keyboard->is_virtual = is_virtual;
if (!seat->keyboard_group->keyboard.keymap) {
wlr_log(WLR_ERROR, "cannot set keymap");
exit(EXIT_FAILURE);
}
wlr_keyboard_set_keymap(kb, seat->keyboard_group->keyboard.keymap);
/*
* This needs to be before wlr_keyboard_group_add_keyboard().
* For some reason, wlroots takes the modifier state from the
* new keyboard and syncs it to the others in the group, rather
* than the other way around.
*/
keyboard_set_numlock(kb);
if (is_virtual) {
/* key repeat information is usually synchronized via the keyboard group */
wlr_keyboard_set_repeat_info(kb, rc.repeat_rate, rc.repeat_delay);
} else {
wlr_keyboard_group_add_keyboard(seat->keyboard_group, kb);
}
keyboard_setup_handlers(keyboard);
wlr_seat_set_keyboard(seat->seat, kb);
return (struct input *)keyboard;
}
static void
map_touch_to_output(struct seat *seat, struct wlr_input_device *dev)
{
struct wlr_touch *touch = wlr_touch_from_input_device(dev);
char *touch_config_output_name = NULL;
struct touch_config_entry *config_entry =
touch_find_config_for_device(touch->base.name);
if (config_entry) {
touch_config_output_name = config_entry->output_name;
}
char *output_name = touch->output_name ? touch->output_name : touch_config_output_name;
wlr_log(WLR_INFO, "map touch to output %s", output_name ? output_name : "unknown");
map_input_to_output(seat, dev, output_name);
}
static struct input *
new_touch(struct seat *seat, struct wlr_input_device *dev)
{
struct input *input = znew(*input);
input->wlr_input_device = dev;
dev->data = input;
configure_libinput(dev);
wlr_cursor_attach_input_device(seat->cursor, dev);
/* In support of running with WLR_WL_OUTPUTS set to >=2 */
map_touch_to_output(seat, dev);
return input;
}
static struct input *
new_tablet(struct seat *seat, struct wlr_input_device *dev)
{
struct input *input = znew(*input);
input->wlr_input_device = dev;
tablet_create(seat, dev);
wlr_cursor_attach_input_device(seat->cursor, dev);
wlr_log(WLR_INFO, "map tablet to output %s", rc.tablet.output_name);
map_input_to_output(seat, dev, rc.tablet.output_name);
return input;
}
static struct input *
new_tablet_pad(struct seat *seat, struct wlr_input_device *dev)
{
struct input *input = znew(*input);
input->wlr_input_device = dev;
tablet_pad_create(seat, dev);
return input;
}
static void
seat_update_capabilities(struct seat *seat)
{
struct input *input = NULL;
uint32_t caps = 0;
wl_list_for_each(input, &seat->inputs, link) {
switch (input->wlr_input_device->type) {
case WLR_INPUT_DEVICE_KEYBOARD:
caps |= WL_SEAT_CAPABILITY_KEYBOARD;
break;
case WLR_INPUT_DEVICE_POINTER:
case WLR_INPUT_DEVICE_TABLET:
caps |= WL_SEAT_CAPABILITY_POINTER;
break;
case WLR_INPUT_DEVICE_TOUCH:
caps |= WL_SEAT_CAPABILITY_TOUCH;
break;
default:
break;
}
}
wlr_seat_set_capabilities(seat->seat, caps);
}
static void
seat_add_device(struct seat *seat, struct input *input)
{
input->seat = seat;
input->destroy.notify = input_device_destroy;
wl_signal_add(&input->wlr_input_device->events.destroy, &input->destroy);
wl_list_insert(&seat->inputs, &input->link);
seat_update_capabilities(seat);
}
static void
new_input_notify(struct wl_listener *listener, void *data)
{
struct seat *seat = wl_container_of(listener, seat, new_input);
struct wlr_input_device *device = data;
struct input *input = NULL;
switch (device->type) {
case WLR_INPUT_DEVICE_KEYBOARD:
input = new_keyboard(seat, device, false);
break;
case WLR_INPUT_DEVICE_POINTER:
input = new_pointer(seat, device);
break;
case WLR_INPUT_DEVICE_TOUCH:
input = new_touch(seat, device);
break;
case WLR_INPUT_DEVICE_TABLET:
input = new_tablet(seat, device);
break;
case WLR_INPUT_DEVICE_TABLET_PAD:
input = new_tablet_pad(seat, device);
break;
default:
wlr_log(WLR_INFO, "unsupported input device");
return;
}
seat_add_device(seat, input);
}
static void
new_virtual_pointer(struct wl_listener *listener, void *data)
{
struct seat *seat = wl_container_of(listener, seat, virtual_pointer_new);
struct wlr_virtual_pointer_v1_new_pointer_event *event = data;
struct wlr_virtual_pointer_v1 *pointer = event->new_pointer;
struct wlr_input_device *device = &pointer->pointer.base;
struct input *input = new_pointer(seat, device);
device->data = input;
seat_add_device(seat, input);
if (event->suggested_output) {
wlr_cursor_map_input_to_output(seat->cursor, device,
event->suggested_output);
}
}
static void
new_virtual_keyboard(struct wl_listener *listener, void *data)
{
struct seat *seat = wl_container_of(listener, seat, virtual_keyboard_new);
struct wlr_virtual_keyboard_v1 *virtual_keyboard = data;
struct wlr_input_device *device = &virtual_keyboard->keyboard.base;
struct input *input = new_keyboard(seat, device, true);
device->data = input;
seat_add_device(seat, input);
}
static void
focus_change_notify(struct wl_listener *listener, void *data)
{
struct seat *seat = wl_container_of(listener, seat, focus_change);
struct wlr_seat_keyboard_focus_change_event *event = data;
struct server *server = seat->server;
struct wlr_surface *surface = event->new_surface;
struct view *view = surface ? view_from_wlr_surface(surface) : NULL;
/*
* Prevent focus switch to non-view surface (e.g. layer-shell
* or xwayland-unmanaged) from updating view state
*/
if (surface && !view) {
return;
}
/*
* We clear the keyboard focus at the beginning of Move/Resize, window
* switcher and opening menus, but don't want to deactivate the view.
*/
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
return;
}
if (view != server->active_view) {
if (server->active_view) {
view_set_activated(server->active_view, false);
}
if (view) {
view_set_activated(view, true);
tablet_pad_enter_surface(seat, surface);
}
server->active_view = view;
}
}
void
seat_init(struct server *server)
{
struct seat *seat = &server->seat;
seat->server = server;
seat->seat = wlr_seat_create(server->wl_display, "seat0");
if (!seat->seat) {
wlr_log(WLR_ERROR, "cannot allocate seat");
exit(EXIT_FAILURE);
}
wl_list_init(&seat->touch_points);
wl_list_init(&seat->constraint_commit.link);
wl_list_init(&seat->inputs);
seat->new_input.notify = new_input_notify;
wl_signal_add(&server->backend->events.new_input, &seat->new_input);
seat->focus_change.notify = focus_change_notify;
wl_signal_add(&seat->seat->keyboard_state.events.focus_change,
&seat->focus_change);
seat->virtual_pointer = wlr_virtual_pointer_manager_v1_create(
server->wl_display);
wl_signal_add(&seat->virtual_pointer->events.new_virtual_pointer,
&seat->virtual_pointer_new);
seat->virtual_pointer_new.notify = new_virtual_pointer;
seat->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create(
server->wl_display);
wl_signal_add(&seat->virtual_keyboard->events.new_virtual_keyboard,
&seat->virtual_keyboard_new);
seat->virtual_keyboard_new.notify = new_virtual_keyboard;
seat->input_method_relay = input_method_relay_create(seat);
seat->xcursor_manager = NULL;
seat->cursor_visible = true;
seat->cursor = wlr_cursor_create();
if (!seat->cursor) {
wlr_log(WLR_ERROR, "unable to create cursor");
exit(EXIT_FAILURE);
}
wlr_cursor_attach_output_layout(seat->cursor, server->output_layout);
wl_list_init(&seat->tablets);
wl_list_init(&seat->tablet_tools);
wl_list_init(&seat->tablet_pads);
input_handlers_init(seat);
}
void
seat_finish(struct server *server)
{
struct seat *seat = &server->seat;
wl_list_remove(&seat->new_input.link);
wl_list_remove(&seat->focus_change.link);
wl_list_remove(&seat->virtual_pointer_new.link);
wl_list_remove(&seat->virtual_keyboard_new.link);
struct input *input, *next;
wl_list_for_each_safe(input, next, &seat->inputs, link) {
input_device_destroy(&input->destroy, NULL);
}
if (seat->workspace_osd_timer) {
wl_event_source_remove(seat->workspace_osd_timer);
seat->workspace_osd_timer = NULL;
}
overlay_finish(seat);
input_handlers_finish(seat);
input_method_relay_finish(seat->input_method_relay);
}
static void
configure_keyboard(struct seat *seat, struct input *input)
{
struct wlr_input_device *device = input->wlr_input_device;
assert(device->type == WLR_INPUT_DEVICE_KEYBOARD);
struct keyboard *keyboard = (struct keyboard *)input;
struct wlr_keyboard *kb = wlr_keyboard_from_input_device(device);
keyboard_configure(seat, kb, keyboard->is_virtual);
}
void
seat_pointer_end_grab(struct seat *seat, struct wlr_surface *surface)
{
if (!surface || !wlr_seat_pointer_has_grab(seat->seat)) {
return;
}
struct wlr_xdg_surface *xdg_surface =
wlr_xdg_surface_try_from_wlr_surface(surface);
if (!xdg_surface || xdg_surface->role != WLR_XDG_SURFACE_ROLE_POPUP) {
/*
* If we have an active popup grab (an open popup) and we are
* not on the popup itself, end that grab to close the popup.
* Contrary to pointer button notifications, a tablet/touch
* button notification sometimes doesn't end grabs automatically
* on button notifications in another client (observed in GTK4),
* so end the grab manually.
*/
wlr_seat_pointer_end_grab(seat->seat);
}
}
/* This is called on SIGHUP (generally in response to labwc --reconfigure */
void
seat_reconfigure(struct server *server)
{
struct seat *seat = &server->seat;
struct input *input;
cursor_reload(seat);
overlay_reconfigure(seat);
keyboard_reset_current_keybind();
wl_list_for_each(input, &seat->inputs, link) {
switch (input->wlr_input_device->type) {
case WLR_INPUT_DEVICE_KEYBOARD:
configure_keyboard(seat, input);
break;
case WLR_INPUT_DEVICE_POINTER:
configure_libinput(input->wlr_input_device);
map_pointer_to_output(seat, input->wlr_input_device);
break;
case WLR_INPUT_DEVICE_TOUCH:
configure_libinput(input->wlr_input_device);
map_touch_to_output(seat, input->wlr_input_device);
break;
case WLR_INPUT_DEVICE_TABLET:
map_input_to_output(seat, input->wlr_input_device, rc.tablet.output_name);
break;
default:
break;
}
}
}
static void
seat_focus(struct seat *seat, struct wlr_surface *surface,
bool replace_exclusive_layer, bool is_lock_surface)
{
/* Respect layer-shell exclusive keyboard-interactivity. */
if (seat->focused_layer && seat->focused_layer->current.keyboard_interactive
== ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE
&& !replace_exclusive_layer) {
return;
}
/*
* Respect session lock. This check is critical, DO NOT REMOVE.
* It should also come before the !surface condition, or the
* lock screen may lose focus and become impossible to unlock.
*/
struct server *server = seat->server;
if (server->session_lock_manager->locked && !is_lock_surface) {
return;
}
if (!surface) {
wlr_seat_keyboard_notify_clear_focus(seat->seat);
input_method_relay_set_focus(seat->input_method_relay, NULL);
return;
}
if (!wlr_seat_get_keyboard(seat->seat)) {
/*
* wlr_seat_keyboard_notify_enter() sends wl_keyboard.modifiers,
* but it may crash some apps (e.g. Chromium) if
* wl_keyboard.keymap is not sent beforehand.
*/
wlr_seat_set_keyboard(seat->seat, &seat->keyboard_group->keyboard);
}
/*
* Key events associated with keybindings (both pressed and released)
* are not sent to clients. When changing surface-focus it is therefore
* important not to send the keycodes of _all_ pressed keys, but only
* those that were actually _sent_ to clients (that is, those that were
* not bound).
*/
uint32_t *pressed_sent_keycodes = key_state_pressed_sent_keycodes();
int nr_pressed_sent_keycodes = key_state_nr_pressed_sent_keycodes();
struct wlr_keyboard *kb = &seat->keyboard_group->keyboard;
wlr_seat_keyboard_notify_enter(seat->seat, surface,
pressed_sent_keycodes, nr_pressed_sent_keycodes, &kb->modifiers);
input_method_relay_set_focus(seat->input_method_relay, surface);
struct wlr_pointer_constraint_v1 *constraint =
wlr_pointer_constraints_v1_constraint_for_surface(server->constraints,
surface, seat->seat);
constrain_cursor(server, constraint);
}
void
seat_focus_surface(struct seat *seat, struct wlr_surface *surface)
{
/* Don't update focus while window switcher, Move/Resize and menu interaction */
if (seat->server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
return;
}
seat_focus(seat, surface, /*replace_exclusive_layer*/ false,
/*is_lock_surface*/ false);
}
void
seat_focus_lock_surface(struct seat *seat, struct wlr_surface *surface)
{
seat_focus(seat, surface, /*replace_exclusive_layer*/ true,
/*is_lock_surface*/ true);
}
void
seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer)
{
if (!layer) {
seat->focused_layer = NULL;
desktop_focus_topmost_view(seat->server);
return;
}
seat_focus(seat, layer->surface, /*replace_exclusive_layer*/ true,
/*is_lock_surface*/ false);
seat->focused_layer = layer;
}
static void
pressed_surface_destroy(struct wl_listener *listener, void *data)
{
struct seat *seat = wl_container_of(listener, seat,
pressed_surface_destroy);
/*
* Using data directly prevents 'unused variable'
* warning when compiling without asserts
*/
assert(data == seat->pressed.surface);
seat_reset_pressed(seat);
}
void
seat_set_pressed(struct seat *seat, struct cursor_context *ctx)
{
assert(ctx);
assert(ctx->view || ctx->surface);
seat_reset_pressed(seat);
seat->pressed = *ctx;
if (ctx->surface) {
seat->pressed_surface_destroy.notify = pressed_surface_destroy;
wl_signal_add(&ctx->surface->events.destroy,
&seat->pressed_surface_destroy);
}
}
void
seat_reset_pressed(struct seat *seat)
{
if (seat->pressed.surface) {
wl_list_remove(&seat->pressed_surface_destroy.link);
}
seat->pressed = (struct cursor_context){0};
}
void
seat_output_layout_changed(struct seat *seat)
{
struct input *input = NULL;
wl_list_for_each(input, &seat->inputs, link) {
switch (input->wlr_input_device->type) {
case WLR_INPUT_DEVICE_POINTER:
map_pointer_to_output(seat, input->wlr_input_device);
break;
case WLR_INPUT_DEVICE_TOUCH:
map_touch_to_output(seat, input->wlr_input_device);
break;
case WLR_INPUT_DEVICE_TABLET:
map_input_to_output(seat, input->wlr_input_device, rc.tablet.output_name);
break;
default:
break;
}
}
}
static void
handle_focus_override_surface_destroy(struct wl_listener *listener, void *data)
{
struct seat *seat = wl_container_of(listener, seat,
focus_override.surface_destroy);
wl_list_remove(&seat->focus_override.surface_destroy.link);
seat->focus_override.surface = NULL;
}
void
seat_focus_override_begin(struct seat *seat, enum input_mode input_mode,
enum lab_cursors cursor_shape)
{
assert(!seat->focus_override.surface);
assert(seat->server->input_mode == LAB_INPUT_STATE_PASSTHROUGH);
seat->server->input_mode = input_mode;
seat->focus_override.surface = seat->seat->keyboard_state.focused_surface;
if (seat->focus_override.surface) {
seat->focus_override.surface_destroy.notify =
handle_focus_override_surface_destroy;
wl_signal_add(&seat->focus_override.surface->events.destroy,
&seat->focus_override.surface_destroy);
}
seat_focus(seat, NULL, /*replace_exclusive_layer*/ false,
/*is_lock_surface*/ false);
wlr_seat_pointer_clear_focus(seat->seat);
cursor_set(seat, cursor_shape);
}
void
seat_focus_override_end(struct seat *seat)
{
seat->server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
if (seat->focus_override.surface) {
if (!seat->seat->keyboard_state.focused_surface) {
seat_focus(seat, seat->focus_override.surface,
/*replace_exclusive_layer*/ false,
/*is_lock_surface*/ false);
}
wl_list_remove(&seat->focus_override.surface_destroy.link);
seat->focus_override.surface = NULL;
}
cursor_update_focus(seat->server);
}