wlroots-hyprland/rootston/seat.c
Dominique Martinet a477e5d302 roots_seat_destroy: also destroy seat in public function
Rework the functions a bit so that the handler does the bulk of the work
except for destroying the seat itself, and the main public function
just explicitely calls the handler and destroys the seat
2017-12-29 20:32:01 +01:00

705 lines
21 KiB
C

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-server.h>
#include <wlr/config.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/util/log.h>
#include "rootston/xcursor.h"
#include "rootston/input.h"
#include "rootston/seat.h"
#include "rootston/keyboard.h"
#include "rootston/cursor.h"
static void handle_keyboard_key(struct wl_listener *listener, void *data) {
struct roots_keyboard *keyboard =
wl_container_of(listener, keyboard, keyboard_key);
struct wlr_event_keyboard_key *event = data;
roots_keyboard_handle_key(keyboard, event);
}
static void handle_keyboard_modifiers(struct wl_listener *listener,
void *data) {
struct roots_keyboard *keyboard =
wl_container_of(listener, keyboard, keyboard_modifiers);
roots_keyboard_handle_modifiers(keyboard);
}
static void handle_cursor_motion(struct wl_listener *listener, void *data) {
struct roots_cursor *cursor =
wl_container_of(listener, cursor, motion);
struct wlr_event_pointer_motion *event = data;
roots_cursor_handle_motion(cursor, event);
}
static void handle_cursor_motion_absolute(struct wl_listener *listener,
void *data) {
struct roots_cursor *cursor =
wl_container_of(listener, cursor, motion_absolute);
struct wlr_event_pointer_motion_absolute *event = data;
roots_cursor_handle_motion_absolute(cursor, event);
}
static void handle_cursor_button(struct wl_listener *listener, void *data) {
struct roots_cursor *cursor =
wl_container_of(listener, cursor, button);
struct wlr_event_pointer_button *event = data;
roots_cursor_handle_button(cursor, event);
}
static void handle_cursor_axis(struct wl_listener *listener, void *data) {
struct roots_cursor *cursor =
wl_container_of(listener, cursor, axis);
struct wlr_event_pointer_axis *event = data;
roots_cursor_handle_axis(cursor, event);
}
static void handle_touch_down(struct wl_listener *listener, void *data) {
struct roots_cursor *cursor =
wl_container_of(listener, cursor, touch_down);
struct wlr_event_touch_down *event = data;
roots_cursor_handle_touch_down(cursor, event);
}
static void handle_touch_up(struct wl_listener *listener, void *data) {
struct roots_cursor *cursor =
wl_container_of(listener, cursor, touch_up);
struct wlr_event_touch_up *event = data;
roots_cursor_handle_touch_up(cursor, event);
}
static void handle_touch_motion(struct wl_listener *listener, void *data) {
struct roots_cursor *cursor =
wl_container_of(listener, cursor, touch_motion);
struct wlr_event_touch_motion *event = data;
roots_cursor_handle_touch_motion(cursor, event);
}
static void handle_tool_axis(struct wl_listener *listener, void *data) {
struct roots_cursor *cursor =
wl_container_of(listener, cursor, tool_axis);
struct wlr_event_tablet_tool_axis *event = data;
roots_cursor_handle_tool_axis(cursor, event);
}
static void handle_tool_tip(struct wl_listener *listener, void *data) {
struct roots_cursor *cursor =
wl_container_of(listener, cursor, tool_tip);
struct wlr_event_tablet_tool_tip *event = data;
roots_cursor_handle_tool_tip(cursor, event);
}
static void handle_request_set_cursor(struct wl_listener *listener,
void *data) {
struct roots_cursor *cursor =
wl_container_of(listener, cursor, request_set_cursor);
struct wlr_seat_pointer_request_set_cursor_event *event = data;
roots_cursor_handle_request_set_cursor(cursor, event);
}
static void seat_reset_device_mappings(struct roots_seat *seat,
struct wlr_input_device *device) {
struct wlr_cursor *cursor = seat->cursor->cursor;
struct roots_config *config = seat->input->config;
wlr_cursor_map_input_to_output(cursor, device, NULL);
struct roots_device_config *dconfig;
if ((dconfig = roots_config_get_device(config, device))) {
wlr_cursor_map_input_to_region(cursor, device, dconfig->mapped_box);
}
}
static void seat_set_device_output_mappings(struct roots_seat *seat,
struct wlr_input_device *device, struct wlr_output *output) {
struct wlr_cursor *cursor = seat->cursor->cursor;
struct roots_config *config = seat->input->config;
struct roots_device_config *dconfig;
dconfig = roots_config_get_device(config, device);
if (dconfig && dconfig->mapped_output &&
strcmp(dconfig->mapped_output, output->name) == 0) {
wlr_cursor_map_input_to_output(cursor, device, output);
}
}
void roots_seat_configure_cursor(struct roots_seat *seat) {
struct roots_config *config = seat->input->config;
struct roots_desktop *desktop = seat->input->server->desktop;
struct wlr_cursor *cursor = seat->cursor->cursor;
struct roots_pointer *pointer;
struct roots_touch *touch;
struct roots_tablet_tool *tablet_tool;
struct roots_output *output;
// reset mappings
wlr_cursor_map_to_output(cursor, NULL);
wl_list_for_each(pointer, &seat->pointers, link) {
seat_reset_device_mappings(seat, pointer->device);
}
wl_list_for_each(touch, &seat->touch, link) {
seat_reset_device_mappings(seat, touch->device);
}
wl_list_for_each(tablet_tool, &seat->tablet_tools, link) {
seat_reset_device_mappings(seat, tablet_tool->device);
}
// configure device to output mappings
const char *mapped_output = NULL;
struct roots_cursor_config *cc =
roots_config_get_cursor(config, seat->seat->name);
if (cc != NULL) {
mapped_output = cc->mapped_output;
}
wl_list_for_each(output, &desktop->outputs, link) {
if (mapped_output &&
strcmp(mapped_output, output->wlr_output->name) == 0) {
wlr_cursor_map_to_output(cursor, output->wlr_output);
}
wl_list_for_each(pointer, &seat->pointers, link) {
seat_set_device_output_mappings(seat, pointer->device,
output->wlr_output);
}
wl_list_for_each(tablet_tool, &seat->tablet_tools, link) {
seat_set_device_output_mappings(seat, tablet_tool->device,
output->wlr_output);
}
wl_list_for_each(touch, &seat->touch, link) {
seat_set_device_output_mappings(seat, touch->device,
output->wlr_output);
}
}
}
static void roots_seat_init_cursor(struct roots_seat *seat) {
seat->cursor = roots_cursor_create(seat);
if (!seat->cursor) {
return;
}
seat->cursor->seat = seat;
struct wlr_cursor *wlr_cursor = seat->cursor->cursor;
struct roots_desktop *desktop = seat->input->server->desktop;
wlr_cursor_attach_output_layout(wlr_cursor, desktop->layout);
roots_seat_configure_cursor(seat);
roots_seat_configure_xcursor(seat);
// add input signals
wl_signal_add(&wlr_cursor->events.motion, &seat->cursor->motion);
seat->cursor->motion.notify = handle_cursor_motion;
wl_signal_add(&wlr_cursor->events.motion_absolute,
&seat->cursor->motion_absolute);
seat->cursor->motion_absolute.notify = handle_cursor_motion_absolute;
wl_signal_add(&wlr_cursor->events.button, &seat->cursor->button);
seat->cursor->button.notify = handle_cursor_button;
wl_signal_add(&wlr_cursor->events.axis, &seat->cursor->axis);
seat->cursor->axis.notify = handle_cursor_axis;
wl_signal_add(&wlr_cursor->events.touch_down, &seat->cursor->touch_down);
seat->cursor->touch_down.notify = handle_touch_down;
wl_signal_add(&wlr_cursor->events.touch_up, &seat->cursor->touch_up);
seat->cursor->touch_up.notify = handle_touch_up;
wl_signal_add(&wlr_cursor->events.touch_motion,
&seat->cursor->touch_motion);
seat->cursor->touch_motion.notify = handle_touch_motion;
wl_signal_add(&wlr_cursor->events.tablet_tool_axis,
&seat->cursor->tool_axis);
seat->cursor->tool_axis.notify = handle_tool_axis;
wl_signal_add(&wlr_cursor->events.tablet_tool_tip, &seat->cursor->tool_tip);
seat->cursor->tool_tip.notify = handle_tool_tip;
wl_signal_add(&seat->seat->events.request_set_cursor,
&seat->cursor->request_set_cursor);
seat->cursor->request_set_cursor.notify = handle_request_set_cursor;
}
static void seat_view_destroy(struct roots_seat_view *seat_view);
static void roots_seat_handle_seat_destroy(struct wl_listener *listener,
void *data) {
struct roots_seat *seat =
wl_container_of(listener, seat, seat_destroy);
// TODO: probably more to be freed here
wl_list_remove(&seat->seat_destroy.link);
struct roots_seat_view *view, *nview;
wl_list_for_each_safe(view, nview, &seat->views, link) {
seat_view_destroy(view);
}
}
void roots_seat_destroy(struct roots_seat *seat) {
roots_seat_handle_seat_destroy(&seat->seat_destroy, seat->seat);
wlr_seat_destroy(seat->seat);
}
struct roots_seat *roots_seat_create(struct roots_input *input, char *name) {
struct roots_seat *seat = calloc(1, sizeof(struct roots_seat));
if (!seat) {
return NULL;
}
wl_list_init(&seat->keyboards);
wl_list_init(&seat->pointers);
wl_list_init(&seat->touch);
wl_list_init(&seat->tablet_tools);
wl_list_init(&seat->views);
seat->input = input;
seat->seat = wlr_seat_create(input->server->wl_display, name);
if (!seat->seat) {
free(seat);
return NULL;
}
roots_seat_init_cursor(seat);
if (!seat->cursor) {
wlr_seat_destroy(seat->seat);
free(seat);
return NULL;
}
wlr_seat_set_capabilities(seat->seat,
WL_SEAT_CAPABILITY_KEYBOARD |
WL_SEAT_CAPABILITY_POINTER |
WL_SEAT_CAPABILITY_TOUCH);
wl_list_insert(&input->seats, &seat->link);
seat->seat_destroy.notify = roots_seat_handle_seat_destroy;
wl_signal_add(&seat->seat->events.destroy, &seat->seat_destroy);
return seat;
}
static void seat_add_keyboard(struct roots_seat *seat,
struct wlr_input_device *device) {
assert(device->type == WLR_INPUT_DEVICE_KEYBOARD);
struct roots_keyboard *keyboard =
roots_keyboard_create(device, seat->input);
if (keyboard == NULL) {
wlr_log(L_ERROR, "could not allocate keyboard for seat");
return;
}
keyboard->seat = seat;
wl_list_insert(&seat->keyboards, &keyboard->link);
keyboard->keyboard_key.notify = handle_keyboard_key;
wl_signal_add(&keyboard->device->keyboard->events.key,
&keyboard->keyboard_key);
keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
wl_signal_add(&keyboard->device->keyboard->events.modifiers,
&keyboard->keyboard_modifiers);
wlr_seat_set_keyboard(seat->seat, device);
}
static void seat_add_pointer(struct roots_seat *seat,
struct wlr_input_device *device) {
struct roots_pointer *pointer = calloc(sizeof(struct roots_pointer), 1);
if (!pointer) {
wlr_log(L_ERROR, "could not allocate pointer for seat");
return;
}
device->data = pointer;
pointer->device = device;
pointer->seat = seat;
wl_list_insert(&seat->pointers, &pointer->link);
wlr_cursor_attach_input_device(seat->cursor->cursor, device);
roots_seat_configure_cursor(seat);
}
static void seat_add_touch(struct roots_seat *seat,
struct wlr_input_device *device) {
struct roots_touch *touch = calloc(sizeof(struct roots_touch), 1);
if (!touch) {
wlr_log(L_ERROR, "could not allocate touch for seat");
return;
}
device->data = touch;
touch->device = device;
touch->seat = seat;
wl_list_insert(&seat->touch, &touch->link);
wlr_cursor_attach_input_device(seat->cursor->cursor, device);
roots_seat_configure_cursor(seat);
}
static void seat_add_tablet_pad(struct roots_seat *seat,
struct wlr_input_device *device) {
// TODO
}
static void seat_add_tablet_tool(struct roots_seat *seat,
struct wlr_input_device *device) {
struct roots_tablet_tool *tablet_tool =
calloc(sizeof(struct roots_tablet_tool), 1);
if (!tablet_tool) {
wlr_log(L_ERROR, "could not allocate tablet_tool for seat");
return;
}
device->data = tablet_tool;
tablet_tool->device = device;
tablet_tool->seat = seat;
wl_list_insert(&seat->tablet_tools, &tablet_tool->link);
wlr_cursor_attach_input_device(seat->cursor->cursor, device);
roots_seat_configure_cursor(seat);
}
void roots_seat_add_device(struct roots_seat *seat,
struct wlr_input_device *device) {
switch (device->type) {
case WLR_INPUT_DEVICE_KEYBOARD:
seat_add_keyboard(seat, device);
break;
case WLR_INPUT_DEVICE_POINTER:
seat_add_pointer(seat, device);
break;
case WLR_INPUT_DEVICE_TOUCH:
seat_add_touch(seat, device);
break;
case WLR_INPUT_DEVICE_TABLET_PAD:
seat_add_tablet_pad(seat, device);
break;
case WLR_INPUT_DEVICE_TABLET_TOOL:
seat_add_tablet_tool(seat, device);
break;
}
}
static void seat_remove_keyboard(struct roots_seat *seat,
struct wlr_input_device *device) {
struct roots_keyboard *keyboard;
wl_list_for_each(keyboard, &seat->keyboards, link) {
if (keyboard->device == device) {
roots_keyboard_destroy(keyboard);
return;
}
}
}
static void seat_remove_pointer(struct roots_seat *seat,
struct wlr_input_device *device) {
struct roots_pointer *pointer;
wl_list_for_each(pointer, &seat->pointers, link) {
if (pointer->device == device) {
wl_list_remove(&pointer->link);
wlr_cursor_detach_input_device(seat->cursor->cursor, device);
free(pointer);
return;
}
}
}
static void seat_remove_touch(struct roots_seat *seat,
struct wlr_input_device *device) {
struct roots_touch *touch;
wl_list_for_each(touch, &seat->touch, link) {
if (touch->device == device) {
wl_list_remove(&touch->link);
wlr_cursor_detach_input_device(seat->cursor->cursor, device);
free(touch);
return;
}
}
}
static void seat_remove_tablet_pad(struct roots_seat *seat,
struct wlr_input_device *device) {
// TODO
}
static void seat_remove_tablet_tool(struct roots_seat *seat,
struct wlr_input_device *device) {
struct roots_tablet_tool *tablet_tool;
wl_list_for_each(tablet_tool, &seat->tablet_tools, link) {
if (tablet_tool->device == device) {
wl_list_remove(&tablet_tool->link);
wlr_cursor_detach_input_device(seat->cursor->cursor, device);
free(tablet_tool);
return;
}
}
}
void roots_seat_remove_device(struct roots_seat *seat,
struct wlr_input_device *device) {
switch (device->type) {
case WLR_INPUT_DEVICE_KEYBOARD:
seat_remove_keyboard(seat, device);
break;
case WLR_INPUT_DEVICE_POINTER:
seat_remove_pointer(seat, device);
break;
case WLR_INPUT_DEVICE_TOUCH:
seat_remove_touch(seat, device);
break;
case WLR_INPUT_DEVICE_TABLET_PAD:
seat_remove_tablet_pad(seat, device);
break;
case WLR_INPUT_DEVICE_TABLET_TOOL:
seat_remove_tablet_tool(seat, device);
break;
}
}
void roots_seat_configure_xcursor(struct roots_seat *seat) {
const char *cursor_theme = NULL;
struct roots_cursor_config *cc =
roots_config_get_cursor(seat->input->config, seat->seat->name);
if (cc != NULL) {
cursor_theme = cc->theme;
if (cc->default_image != NULL) {
seat->cursor->default_xcursor = cc->default_image;
}
}
if (!seat->cursor->xcursor_manager) {
seat->cursor->xcursor_manager =
wlr_xcursor_manager_create(cursor_theme, ROOTS_XCURSOR_SIZE);
if (seat->cursor->xcursor_manager == NULL) {
wlr_log(L_ERROR, "Cannot create XCursor manager for theme %s",
cursor_theme);
return;
}
}
struct roots_output *output;
wl_list_for_each(output, &seat->input->server->desktop->outputs, link) {
float scale = output->wlr_output->scale;
if (wlr_xcursor_manager_load(seat->cursor->xcursor_manager, scale)) {
wlr_log(L_ERROR, "Cannot load xcursor theme for output '%s' "
"with scale %f", output->wlr_output->name, scale);
}
}
wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_manager,
seat->cursor->default_xcursor, seat->cursor->cursor);
wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x,
seat->cursor->cursor->y);
}
bool roots_seat_has_meta_pressed(struct roots_seat *seat) {
struct roots_keyboard *keyboard;
wl_list_for_each(keyboard, &seat->keyboards, link) {
if (!keyboard->config->meta_key) {
continue;
}
uint32_t modifiers =
wlr_keyboard_get_modifiers(keyboard->device->keyboard);
if ((modifiers ^ keyboard->config->meta_key) == 0) {
return true;
}
}
return false;
}
struct roots_view *roots_seat_get_focus(struct roots_seat *seat) {
if (!seat->has_focus || wl_list_empty(&seat->views)) {
return NULL;
}
struct roots_seat_view *seat_view =
wl_container_of(seat->views.next, seat_view, link);
return seat_view->view;
}
static void seat_view_destroy(struct roots_seat_view *seat_view) {
struct roots_seat *seat = seat_view->seat;
if (seat_view->view == roots_seat_get_focus(seat)) {
seat->has_focus = false;
seat->cursor->mode = ROOTS_CURSOR_PASSTHROUGH;
}
wl_list_remove(&seat_view->view_destroy.link);
wl_list_remove(&seat_view->link);
free(seat_view);
// Focus first view
if (!wl_list_empty(&seat->views)) {
struct roots_seat_view *first_seat_view = wl_container_of(
seat->views.next, first_seat_view, link);
roots_seat_set_focus(seat, first_seat_view->view);
}
}
static void seat_view_handle_destroy(struct wl_listener *listener, void *data) {
struct roots_seat_view *seat_view =
wl_container_of(listener, seat_view, view_destroy);
seat_view_destroy(seat_view);
}
static struct roots_seat_view *seat_add_view(struct roots_seat *seat,
struct roots_view *view) {
struct roots_seat_view *seat_view =
calloc(1, sizeof(struct roots_seat_view));
if (seat_view == NULL) {
return NULL;
}
seat_view->seat = seat;
seat_view->view = view;
wl_list_insert(&seat->views, &seat_view->link);
seat_view->view_destroy.notify = seat_view_handle_destroy;
wl_signal_add(&view->events.destroy, &seat_view->view_destroy);
return seat_view;
}
void roots_seat_set_focus(struct roots_seat *seat, struct roots_view *view) {
// Make sure the view will be rendered on top of others, even if it's
// already focused in this seat
if (view != NULL) {
wl_list_remove(&view->link);
wl_list_insert(&seat->input->server->desktop->views, &view->link);
}
struct roots_view *prev_focus = roots_seat_get_focus(seat);
if (view == prev_focus) {
return;
}
if (view && view->type == ROOTS_XWAYLAND_VIEW &&
view->xwayland_surface->override_redirect) {
return;
}
struct roots_seat_view *seat_view = NULL;
if (view != NULL) {
bool found = false;
wl_list_for_each(seat_view, &seat->views, link) {
if (seat_view->view == view) {
found = true;
break;
}
}
if (!found) {
seat_view = seat_add_view(seat, view);
if (seat_view == NULL) {
wlr_log(L_ERROR, "Allocation failed");
return;
}
}
}
seat->has_focus = false;
// Deactivate the old view if it is not focused by some other seat
if (prev_focus != NULL && !input_view_has_focus(seat->input, prev_focus)) {
view_activate(prev_focus, false);
}
if (view == NULL) {
seat->cursor->mode = ROOTS_CURSOR_PASSTHROUGH;
return;
}
view_activate(view, true);
seat->has_focus = true;
wl_list_remove(&seat_view->link);
wl_list_insert(&seat->views, &seat_view->link);
wlr_seat_keyboard_notify_enter(seat->seat, view->wlr_surface);
}
void roots_seat_cycle_focus(struct roots_seat *seat) {
if (wl_list_empty(&seat->views)) {
return;
}
struct roots_seat_view *first_seat_view = wl_container_of(
seat->views.next, first_seat_view, link);
if (!seat->has_focus) {
roots_seat_set_focus(seat, first_seat_view->view);
return;
}
if (wl_list_length(&seat->views) < 2) {
return;
}
// Focus the next view
struct roots_seat_view *next_seat_view = wl_container_of(
first_seat_view->link.next, next_seat_view, link);
roots_seat_set_focus(seat, next_seat_view->view);
// Move the first view to the end of the list
wl_list_remove(&first_seat_view->link);
wl_list_insert(seat->views.prev, &first_seat_view->link);
}
void roots_seat_begin_move(struct roots_seat *seat, struct roots_view *view) {
struct roots_cursor *cursor = seat->cursor;
cursor->mode = ROOTS_CURSOR_MOVE;
cursor->offs_x = cursor->cursor->x;
cursor->offs_y = cursor->cursor->y;
if (view->maximized) {
cursor->view_x = view->saved.x;
cursor->view_y = view->saved.y;
} else {
cursor->view_x = view->x;
cursor->view_y = view->y;
}
view_maximize(view, false);
wlr_seat_pointer_clear_focus(seat->seat);
wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_manager,
ROOTS_XCURSOR_MOVE, seat->cursor->cursor);
}
void roots_seat_begin_resize(struct roots_seat *seat, struct roots_view *view,
uint32_t edges) {
struct roots_cursor *cursor = seat->cursor;
cursor->mode = ROOTS_CURSOR_RESIZE;
cursor->offs_x = cursor->cursor->x;
cursor->offs_y = cursor->cursor->y;
if (view->maximized) {
cursor->view_x = view->saved.x;
cursor->view_y = view->saved.y;
cursor->view_width = view->saved.width;
cursor->view_height = view->saved.height;
} else {
cursor->view_x = view->x;
cursor->view_y = view->y;
struct wlr_box box;
view_get_box(view, &box);
cursor->view_width = box.width;
cursor->view_height = box.height;
}
cursor->resize_edges = edges;
view_maximize(view, false);
wlr_seat_pointer_clear_focus(seat->seat);
const char *resize_name = wlr_xcursor_get_resize_name(edges);
wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_manager,
resize_name, seat->cursor->cursor);
}
void roots_seat_begin_rotate(struct roots_seat *seat, struct roots_view *view) {
struct roots_cursor *cursor = seat->cursor;
cursor->mode = ROOTS_CURSOR_ROTATE;
cursor->offs_x = cursor->cursor->x;
cursor->offs_y = cursor->cursor->y;
cursor->view_rotation = view->rotation;
view_maximize(view, false);
wlr_seat_pointer_clear_focus(seat->seat);
wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_manager,
ROOTS_XCURSOR_ROTATE, seat->cursor->cursor);
}