aboutsummaryrefslogtreecommitdiff
path: root/rootston
diff options
context:
space:
mode:
Diffstat (limited to 'rootston')
-rw-r--r--rootston/config.c3
-rw-r--r--rootston/cursor.c219
-rw-r--r--rootston/desktop.c99
-rw-r--r--rootston/keyboard.c22
-rw-r--r--rootston/meson.build1
-rw-r--r--rootston/output.c185
-rw-r--r--rootston/rootston.ini.example2
-rw-r--r--rootston/seat.c47
-rw-r--r--rootston/text_input.c310
9 files changed, 792 insertions, 96 deletions
diff --git a/rootston/config.c b/rootston/config.c
index 92d90de1..119a9e2c 100644
--- a/rootston/config.c
+++ b/rootston/config.c
@@ -506,6 +506,7 @@ struct roots_config *roots_config_create_from_args(int argc, char *argv[]) {
add_binding_config(&config->bindings, "Logo+Shift+E", "exit");
add_binding_config(&config->bindings, "Ctrl+q", "close");
add_binding_config(&config->bindings, "Alt+Tab", "next_window");
+ add_binding_config(&config->bindings, "Logo+Escape", "break_pointer_constraint");
struct roots_keyboard_config *kc =
calloc(1, sizeof(struct roots_keyboard_config));
kc->meta_key = WLR_MODIFIER_LOGO;
@@ -576,7 +577,7 @@ void roots_config_destroy(struct roots_config *config) {
struct roots_output_config *roots_config_get_output(struct roots_config *config,
struct wlr_output *output) {
- char name[83];
+ char name[88];
snprintf(name, sizeof(name), "%s %s %s", output->make, output->model,
output->serial);
diff --git a/rootston/cursor.c b/rootston/cursor.c
index 2b8d9a3e..9a163c63 100644
--- a/rootston/cursor.c
+++ b/rootston/cursor.c
@@ -1,9 +1,12 @@
#define _XOPEN_SOURCE 700
+#include <assert.h>
#include <math.h>
#include <stdlib.h>
+#include <wlr/types/wlr_region.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/util/edges.h>
#include <wlr/util/log.h>
+#include <wlr/util/region.h>
#ifdef __linux__
#include <linux/input-event-codes.h>
#elif __FreeBSD__
@@ -11,6 +14,7 @@
#endif
#include "rootston/cursor.h"
#include "rootston/desktop.h"
+#include "rootston/view.h"
#include "rootston/xcursor.h"
struct roots_cursor *roots_cursor_create(struct roots_seat *seat) {
@@ -100,7 +104,7 @@ static void seat_view_deco_button(struct roots_seat_view *view, double sx,
}
static void roots_passthrough_cursor(struct roots_cursor *cursor,
- uint32_t time) {
+ int64_t time) {
bool focus_changed;
double sx, sy;
struct roots_view *view = NULL;
@@ -108,11 +112,13 @@ static void roots_passthrough_cursor(struct roots_cursor *cursor,
struct roots_desktop *desktop = seat->input->server->desktop;
struct wlr_surface *surface = desktop_surface_at(desktop,
cursor->cursor->x, cursor->cursor->y, &sx, &sy, &view);
+
struct wl_client *client = NULL;
if (surface) {
client = wl_resource_get_client(surface->resource);
}
- if (surface && !roots_seat_allow_input(cursor->seat, surface->resource)) {
+
+ if (surface && !roots_seat_allow_input(seat, surface->resource)) {
return;
}
@@ -125,21 +131,27 @@ static void roots_passthrough_cursor(struct roots_cursor *cursor,
if (view) {
struct roots_seat_view *seat_view =
roots_seat_view_from_view(seat, view);
- if (cursor->pointer_view && (surface ||
- seat_view != cursor->pointer_view)) {
+
+ if (cursor->pointer_view &&
+ !cursor->wlr_surface && (surface || seat_view != cursor->pointer_view)) {
seat_view_deco_leave(cursor->pointer_view);
- cursor->pointer_view = NULL;
}
+
+ cursor->pointer_view = seat_view;
+
if (!surface) {
- cursor->pointer_view = seat_view;
seat_view_deco_motion(seat_view, sx, sy);
}
+ } else {
+ cursor->pointer_view = NULL;
}
+ cursor->wlr_surface = surface;
+
if (surface) {
focus_changed = (seat->seat->pointer_state.focused_surface != surface);
wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy);
- if (!focus_changed) {
+ if (!focus_changed && time > 0) {
wlr_seat_pointer_notify_motion(seat->seat, time, sx, sy);
}
} else {
@@ -152,6 +164,10 @@ static void roots_passthrough_cursor(struct roots_cursor *cursor,
}
}
+void roots_cursor_update_focus(struct roots_cursor *cursor) {
+ roots_passthrough_cursor(cursor, -1);
+}
+
void roots_cursor_update_position(struct roots_cursor *cursor,
uint32_t time) {
struct roots_seat *seat = cursor->seat;
@@ -262,7 +278,7 @@ static void roots_cursor_press_button(struct roots_cursor *cursor,
} else {
if (view && !surface && cursor->pointer_view) {
seat_view_deco_button(cursor->pointer_view,
- sx, sy, button, state);
+ sx, sy, button, state);
}
if (state == WLR_BUTTON_RELEASED &&
@@ -291,15 +307,59 @@ static void roots_cursor_press_button(struct roots_cursor *cursor,
void roots_cursor_handle_motion(struct roots_cursor *cursor,
struct wlr_event_pointer_motion *event) {
- wlr_cursor_move(cursor->cursor, event->device,
- event->delta_x, event->delta_y);
+ double dx = event->delta_x;
+ double dy = event->delta_y;
+
+ if (cursor->active_constraint) {
+ struct roots_view *view = cursor->pointer_view->view;
+ assert(view);
+
+ // TODO: handle rotated views
+ if (view->rotation == 0.0) {
+ double lx1 = cursor->cursor->x;
+ double ly1 = cursor->cursor->y;
+
+ double lx2 = lx1 + dx;
+ double ly2 = ly1 + dy;
+
+ double sx1 = lx1 - view->x;
+ double sy1 = ly1 - view->y;
+
+ double sx2 = lx2 - view->x;
+ double sy2 = ly2 - view->y;
+
+ double sx2_confined, sy2_confined;
+ if (!wlr_region_confine(&cursor->confine, sx1, sy1, sx2, sy2,
+ &sx2_confined, &sy2_confined)) {
+ return;
+ }
+
+ dx = sx2_confined - sx1;
+ dy = sy2_confined - sy1;
+ }
+ }
+
+ wlr_cursor_move(cursor->cursor, event->device, dx, dy);
roots_cursor_update_position(cursor, event->time_msec);
}
void roots_cursor_handle_motion_absolute(struct roots_cursor *cursor,
struct wlr_event_pointer_motion_absolute *event) {
- wlr_cursor_warp_absolute(cursor->cursor,
- event->device, event->x, event->y);
+ double lx, ly;
+ wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, event->x,
+ event->y, &lx, &ly);
+
+ if (cursor->pointer_view) {
+ struct roots_view *view = cursor->pointer_view->view;
+
+ if (cursor->active_constraint &&
+ !pixman_region32_contains_point(&cursor->confine,
+ floor(lx - view->x), floor(ly - view->y), NULL)) {
+ return;
+ }
+ }
+
+ wlr_cursor_warp_closest(cursor->cursor, event->device, lx, ly);
roots_cursor_update_position(cursor, event->time_msec);
}
@@ -324,7 +384,7 @@ void roots_cursor_handle_touch_down(struct roots_cursor *cursor,
double sx, sy;
struct wlr_surface *surface = desktop_surface_at(
- desktop, lx, ly, &sx, &sy, NULL);
+ desktop, lx, ly, &sx, &sy, NULL);
uint32_t serial = 0;
if (surface && roots_seat_allow_input(cursor->seat, surface->resource)) {
@@ -393,18 +453,34 @@ void roots_cursor_handle_touch_motion(struct roots_cursor *cursor,
void roots_cursor_handle_tool_axis(struct roots_cursor *cursor,
struct wlr_event_tablet_tool_axis *event) {
+ double x = NAN, y = NAN;
if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) &&
(event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
- wlr_cursor_warp_absolute(cursor->cursor, event->device,
- event->x, event->y);
- roots_cursor_update_position(cursor, event->time_msec);
+ x = event->x;
+ y = event->y;
} else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) {
- wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, -1);
- roots_cursor_update_position(cursor, event->time_msec);
+ x = event->x;
} else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
- wlr_cursor_warp_absolute(cursor->cursor, event->device, -1, event->y);
- roots_cursor_update_position(cursor, event->time_msec);
+ y = event->y;
+ }
+
+ double lx, ly;
+ wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device,
+ x, y, &lx, &ly);
+
+
+ if (cursor->pointer_view) {
+ struct roots_view *view = cursor->pointer_view->view;
+
+ if (cursor->active_constraint &&
+ !pixman_region32_contains_point(&cursor->confine,
+ floor(lx - view->x), floor(ly - view->y), NULL)) {
+ return;
+ }
}
+
+ wlr_cursor_warp_closest(cursor->cursor, event->device, lx, ly);
+ roots_cursor_update_position(cursor, event->time_msec);
}
void roots_cursor_handle_tool_tip(struct roots_cursor *cursor,
@@ -434,3 +510,106 @@ void roots_cursor_handle_request_set_cursor(struct roots_cursor *cursor,
event->hotspot_y);
cursor->cursor_client = event->seat_client->client;
}
+
+void roots_cursor_handle_focus_change(struct roots_cursor *cursor,
+ struct wlr_seat_pointer_focus_change_event *event) {
+ double sx = event->sx;
+ double sy = event->sy;
+
+ double lx = cursor->cursor->x;
+ double ly = cursor->cursor->y;
+
+ wlr_log(WLR_DEBUG, "entered surface %p, lx: %f, ly: %f, sx: %f, sy: %f",
+ event->new_surface, lx, ly, sx, sy);
+
+ roots_cursor_constrain(cursor,
+ wlr_pointer_constraints_v1_constraint_for_surface(
+ cursor->seat->input->server->desktop->pointer_constraints,
+ event->new_surface, cursor->seat->seat),
+ sx, sy);
+}
+
+void roots_cursor_handle_constraint_commit(struct roots_cursor *cursor) {
+ struct roots_desktop *desktop = cursor->seat->input->server->desktop;
+
+ struct roots_view *view;
+ double sx, sy;
+ struct wlr_surface *surface = desktop_surface_at(desktop,
+ cursor->cursor->x, cursor->cursor->y, &sx, &sy, &view);
+ // This should never happen but views move around right when they're
+ // created from (0, 0) to their actual coordinates.
+ if (surface != cursor->active_constraint->surface) {
+ roots_cursor_update_focus(cursor);
+ } else {
+ roots_cursor_constrain(cursor, cursor->active_constraint, sx, sy);
+ }
+}
+
+static void handle_constraint_commit(struct wl_listener *listener,
+ void *data) {
+ struct roots_cursor *cursor =
+ wl_container_of(listener, cursor, constraint_commit);
+ assert(cursor->active_constraint->surface == data);
+ roots_cursor_handle_constraint_commit(cursor);
+}
+
+void roots_cursor_constrain(struct roots_cursor *cursor,
+ struct wlr_pointer_constraint_v1 *constraint, double sx, double sy) {
+ if (cursor->active_constraint == constraint) {
+ return;
+ }
+
+ wlr_log(WLR_DEBUG, "roots_cursor_constrain(%p, %p)",
+ cursor, constraint);
+ wlr_log(WLR_DEBUG, "cursor->active_constraint: %p",
+ cursor->active_constraint);
+
+ wl_list_remove(&cursor->constraint_commit.link);
+ wl_list_init(&cursor->constraint_commit.link);
+ if (cursor->active_constraint) {
+ wlr_pointer_constraint_v1_send_deactivated(
+ cursor->active_constraint);
+ }
+
+ cursor->active_constraint = constraint;
+
+ if (constraint == NULL) {
+ return;
+ }
+
+ wlr_pointer_constraint_v1_send_activated(constraint);
+
+ wl_list_remove(&cursor->constraint_commit.link);
+ wl_signal_add(&constraint->surface->events.commit,
+ &cursor->constraint_commit);
+ cursor->constraint_commit.notify = handle_constraint_commit;
+
+ pixman_region32_clear(&cursor->confine);
+
+ pixman_region32_t *region = &constraint->region;
+
+ if (!pixman_region32_contains_point(region, floor(sx), floor(sy), NULL)) {
+ // Warp into region if possible
+ int nboxes;
+ pixman_box32_t *boxes = pixman_region32_rectangles(region, &nboxes);
+ if (nboxes > 0) {
+ struct roots_view *view = cursor->pointer_view->view;
+
+ double sx = (boxes[0].x1 + boxes[0].x2) / 2.;
+ double sy = (boxes[0].y1 + boxes[0].y2) / 2.;
+
+ rotate_child_position(&sx, &sy, 0, 0, view->width, view->height,
+ view->rotation);
+
+ double lx = view->x + sx;
+ double ly = view->y + sy;
+
+ wlr_cursor_warp_closest(cursor->cursor, NULL, lx, ly);
+ }
+ }
+
+ // A locked pointer will result in an empty region, thus disallowing all movement
+ if (constraint->type == WLR_POINTER_CONSTRAINT_V1_CONFINED) {
+ pixman_region32_copy(&cursor->confine, region);
+ }
+}
diff --git a/rootston/desktop.c b/rootston/desktop.c
index 3f6d977e..7da64ef8 100644
--- a/rootston/desktop.c
+++ b/rootston/desktop.c
@@ -1,4 +1,4 @@
-#define _POSIX_C_SOURCE 199309L
+#define _POSIX_C_SOURCE 200112L
#include <assert.h>
#include <math.h>
#include <stdlib.h>
@@ -15,6 +15,7 @@
#include <wlr/types/wlr_input_inhibitor.h>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_primary_selection.h>
#include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_wl_shell.h>
@@ -438,6 +439,11 @@ void view_destroy(struct roots_view *view) {
view_unmap(view);
}
+ // Can happen if fullscreened while unmapped, and hasn't been mapped
+ if (view->fullscreen_output != NULL) {
+ view->fullscreen_output->fullscreen_view = NULL;
+ }
+
if (view->destroy) {
view->destroy(view);
}
@@ -575,6 +581,9 @@ static bool view_at(struct roots_view *view, double lx, double ly,
view->wl_shell_surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP) {
return false;
}
+ if (view->wlr_surface == NULL) {
+ return false;
+ }
double view_sx = lx - view->x;
double view_sy = ly - view->y;
@@ -776,6 +785,62 @@ static void input_inhibit_deactivate(struct wl_listener *listener, void *data) {
}
}
+static void handle_constraint_destroy(struct wl_listener *listener,
+ void *data) {
+ struct roots_pointer_constraint *constraint =
+ wl_container_of(listener, constraint, destroy);
+ struct wlr_pointer_constraint_v1 *wlr_constraint = data;
+ struct roots_seat *seat = wlr_constraint->seat->data;
+
+ wl_list_remove(&constraint->destroy.link);
+
+ if (seat->cursor->active_constraint == wlr_constraint) {
+ wl_list_remove(&seat->cursor->constraint_commit.link);
+ wl_list_init(&seat->cursor->constraint_commit.link);
+ seat->cursor->active_constraint = NULL;
+
+ if (wlr_constraint->current.committed &
+ WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT &&
+ seat->cursor->pointer_view) {
+ double sx = wlr_constraint->current.cursor_hint.x;
+ double sy = wlr_constraint->current.cursor_hint.y;
+
+ struct roots_view *view = seat->cursor->pointer_view->view;
+ rotate_child_position(&sx, &sy, 0, 0, view->width, view->height,
+ view->rotation);
+ double lx = view->x + sx;
+ double ly = view->y + sy;
+
+ wlr_cursor_warp(seat->cursor->cursor, NULL, lx, ly);
+ }
+ }
+
+ free(constraint);
+}
+
+static void handle_pointer_constraint(struct wl_listener *listener,
+ void *data) {
+ struct wlr_pointer_constraint_v1 *wlr_constraint = data;
+ struct roots_seat *seat = wlr_constraint->seat->data;
+
+ struct roots_pointer_constraint *constraint =
+ calloc(1, sizeof(struct roots_pointer_constraint));
+ constraint->constraint = wlr_constraint;
+
+ constraint->destroy.notify = handle_constraint_destroy;
+ wl_signal_add(&wlr_constraint->events.destroy, &constraint->destroy);
+
+ double sx, sy;
+ struct wlr_surface *surface = desktop_surface_at(
+ seat->input->server->desktop,
+ seat->cursor->cursor->x, seat->cursor->cursor->y, &sx, &sy, NULL);
+
+ if (surface == wlr_constraint->surface) {
+ assert(!seat->cursor->active_constraint);
+ roots_cursor_constrain(seat->cursor, wlr_constraint, sx, sy);
+ }
+}
+
struct roots_desktop *desktop_create(struct roots_server *server,
struct roots_config *config) {
wlr_log(WLR_DEBUG, "Initializing roots desktop");
@@ -824,18 +889,30 @@ struct roots_desktop *desktop_create(struct roots_server *server,
desktop->tablet_v2 = wlr_tablet_v2_create(server->wl_display);
-#ifdef WLR_HAS_XWAYLAND
const char *cursor_theme = NULL;
+#ifdef WLR_HAS_XWAYLAND
const char *cursor_default = ROOTS_XCURSOR_DEFAULT;
+#endif
struct roots_cursor_config *cc =
roots_config_get_cursor(config, ROOTS_CONFIG_DEFAULT_SEAT_NAME);
if (cc != NULL) {
cursor_theme = cc->theme;
+#ifdef WLR_HAS_XWAYLAND
if (cc->default_image != NULL) {
cursor_default = cc->default_image;
}
+#endif
+ }
+
+ char cursor_size_fmt[16];
+ snprintf(cursor_size_fmt, sizeof(cursor_size_fmt),
+ "%d", ROOTS_XCURSOR_SIZE);
+ setenv("XCURSOR_SIZE", cursor_size_fmt, 1);
+ if (cursor_theme != NULL) {
+ setenv("XCURSOR_THEME", cursor_theme, 1);
}
+#ifdef WLR_HAS_XWAYLAND
desktop->xcursor_manager = wlr_xcursor_manager_create(cursor_theme,
ROOTS_XCURSOR_SIZE);
if (desktop->xcursor_manager == NULL) {
@@ -887,10 +964,15 @@ struct roots_desktop *desktop_create(struct roots_server *server,
wlr_input_inhibit_manager_create(server->wl_display);
desktop->input_inhibit_activate.notify = input_inhibit_activate;
wl_signal_add(&desktop->input_inhibit->events.activate,
- &desktop->input_inhibit_activate);
+ &desktop->input_inhibit_activate);
desktop->input_inhibit_deactivate.notify = input_inhibit_deactivate;
wl_signal_add(&desktop->input_inhibit->events.deactivate,
- &desktop->input_inhibit_deactivate);
+ &desktop->input_inhibit_deactivate);
+
+ desktop->input_method =
+ wlr_input_method_manager_v2_create(server->wl_display);
+
+ desktop->text_input = wlr_text_input_manager_v3_create(server->wl_display);
desktop->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create(
server->wl_display);
@@ -906,6 +988,15 @@ struct roots_desktop *desktop_create(struct roots_server *server,
&desktop->xdg_toplevel_decoration);
desktop->xdg_toplevel_decoration.notify = handle_xdg_toplevel_decoration;
+ desktop->pointer_constraints =
+ wlr_pointer_constraints_v1_create(server->wl_display);
+ desktop->pointer_constraint.notify = handle_pointer_constraint;
+ wl_signal_add(&desktop->pointer_constraints->events.new_constraint,
+ &desktop->pointer_constraint);
+
+ desktop->presentation =
+ wlr_presentation_create(server->wl_display, server->backend);
+
return desktop;
}
diff --git a/rootston/keyboard.c b/rootston/keyboard.c
index b5a8093b..66c373cf 100644
--- a/rootston/keyboard.c
+++ b/rootston/keyboard.c
@@ -5,9 +5,9 @@
#include <sys/wait.h>
#include <unistd.h>
#include <wayland-server.h>
-#include <wlr/backend/multi.h>
#include <wlr/backend/session.h>
#include <wlr/types/wlr_input_device.h>
+#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
@@ -176,6 +176,13 @@ static void keyboard_binding_execute(struct roots_keyboard *keyboard,
decoration->wlr_decoration, mode);
}
}
+ } else if (strcmp(command, "break_pointer_constraint") == 0) {
+ struct wl_list *list =
+ &keyboard->input->seats;
+ struct roots_seat *seat;
+ wl_list_for_each(seat, list, link) {
+ roots_cursor_constrain(seat->cursor, NULL, NAN, NAN);
+ }
} else {
wlr_log(WLR_ERROR, "unknown binding command: %s", command);
}
@@ -193,14 +200,13 @@ static bool keyboard_execute_compositor_binding(struct roots_keyboard *keyboard,
if (keysym >= XKB_KEY_XF86Switch_VT_1 &&
keysym <= XKB_KEY_XF86Switch_VT_12) {
struct roots_server *server = keyboard->input->server;
- if (wlr_backend_is_multi(server->backend)) {
- struct wlr_session *session =
- wlr_multi_get_session(server->backend);
- if (session) {
- unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1;
- wlr_session_change_vt(session, vt);
- }
+
+ struct wlr_session *session = wlr_backend_get_session(server->backend);
+ if (session) {
+ unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1;
+ wlr_session_change_vt(session, vt);
}
+
return true;
}
diff --git a/rootston/meson.build b/rootston/meson.build
index 9d1decce..d650dc51 100644
--- a/rootston/meson.build
+++ b/rootston/meson.build
@@ -9,6 +9,7 @@ sources = [
'main.c',
'output.c',
'seat.c',
+ 'text_input.c',
'virtual_keyboard.c',
'wl_shell.c',
'xdg_shell.c',
diff --git a/rootston/output.c b/rootston/output.c
index 674cda2d..9aa7de65 100644
--- a/rootston/output.c
+++ b/rootston/output.c
@@ -5,9 +5,10 @@
#include <time.h>
#include <wlr/backend/drm.h>
#include <wlr/config.h>
-#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_compositor.h>
+#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_wl_shell.h>
#include <wlr/types/wlr_xdg_shell_v6.h>
#include <wlr/types/wlr_xdg_shell.h>
@@ -22,7 +23,7 @@
* Rotate a child's position relative to a parent. The parent size is (pw, ph),
* the child position is (*sx, *sy) and its size is (sw, sh).
*/
-static void rotate_child_position(double *sx, double *sy, double sw, double sh,
+void rotate_child_position(double *sx, double *sy, double sw, double sh,
double pw, double ph, float rotation) {
if (rotation != 0.0) {
// Coordinates relative to the center of the subsurface
@@ -127,6 +128,65 @@ static void drag_icons_for_each_surface(struct roots_input *input,
}
}
+static void layer_for_each_surface(struct wl_list *layer,
+ const struct wlr_box *output_layout_box,
+ wlr_surface_iterator_func_t iterator, struct layout_data *layout_data,
+ void *user_data) {
+ struct roots_layer_surface *roots_surface;
+ wl_list_for_each(roots_surface, layer, link) {
+ struct wlr_layer_surface_v1 *layer = roots_surface->layer_surface;
+
+ layout_data->x = roots_surface->geo.x + output_layout_box->x;
+ layout_data->y = roots_surface->geo.y + output_layout_box->y;
+ layout_data->width = roots_surface->geo.width;
+ layout_data->height = roots_surface->geo.height;
+ layout_data->rotation = 0;
+ wlr_layer_surface_v1_for_each_surface(layer, iterator, user_data);
+ }
+}
+
+static void output_for_each_surface(struct roots_output *output,
+ wlr_surface_iterator_func_t iterator, struct layout_data *layout_data,
+ void *user_data) {
+ struct wlr_output *wlr_output = output->wlr_output;
+ struct roots_desktop *desktop = output->desktop;
+ struct roots_server *server = desktop->server;
+
+ const struct wlr_box *output_box =
+ wlr_output_layout_get_box(desktop->layout, wlr_output);
+
+ if (output->fullscreen_view != NULL) {
+ struct roots_view *view = output->fullscreen_view;
+ if (wlr_output->fullscreen_surface == view->wlr_surface) {
+ // The surface is managed by the wlr_output
+ return;
+ }
+
+ view_for_each_surface(view, layout_data, iterator, user_data);
+
+#ifdef WLR_HAS_XWAYLAND
+ if (view->type == ROOTS_XWAYLAND_VIEW) {
+ xwayland_children_for_each_surface(view->xwayland_surface,
+ iterator, layout_data, user_data);
+ }
+#endif
+ } else {
+ struct roots_view *view;
+ wl_list_for_each_reverse(view, &desktop->views, link) {
+ view_for_each_surface(view, layout_data, iterator, user_data);
+ }
+
+ drag_icons_for_each_surface(server->input, iterator,
+ layout_data, user_data);
+ }
+
+ size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
+ for (size_t i = 0; i < len; ++i) {
+ layer_for_each_surface(&output->layers[i], output_box,
+ iterator, layout_data, user_data);
+ }
+}
+
struct render_data {
struct layout_data layout;
@@ -320,6 +380,14 @@ static void render_view(struct roots_view *view, struct render_data *data) {
view_for_each_surface(view, &data->layout, render_surface, data);
}
+static void render_layer(struct roots_output *output,
+ const struct wlr_box *output_layout_box, struct render_data *data,
+ struct wl_list *layer) {
+ data->alpha = 1;
+ layer_for_each_surface(layer, output_layout_box, render_surface,
+ &data->layout, data);
+}
+
static bool has_standalone_surface(struct roots_view *view) {
if (!wl_list_empty(&view->wlr_surface->subsurfaces)) {
return false;
@@ -358,38 +426,6 @@ static void surface_send_frame_done(struct wlr_surface *surface, int sx, int sy,
wlr_surface_send_frame_done(surface, when);
}
-static void render_layer(struct roots_output *output,
- const struct wlr_box *output_layout_box, struct render_data *data,
- struct wl_list *layer) {
- struct roots_layer_surface *roots_surface;
- wl_list_for_each(roots_surface, layer, link) {
- struct wlr_layer_surface_v1 *layer = roots_surface->layer_surface;
-
- surface_for_each_surface(layer->surface,
- roots_surface->geo.x + output_layout_box->x,
- roots_surface->geo.y + output_layout_box->y,
- 0, &data->layout, render_surface, data);
-
- wlr_layer_surface_v1_for_each_surface(layer, render_surface, data);
- }
-}
-
-static void layers_send_done(
- struct roots_output *output, struct timespec *when) {
- size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
- for (size_t i = 0; i < len; ++i) {
- struct roots_layer_surface *roots_surface;
- wl_list_for_each(roots_surface, &output->layers[i], link) {
- struct wlr_layer_surface_v1 *layer = roots_surface->layer_surface;
- wlr_surface_send_frame_done(layer->surface, when);
- struct wlr_xdg_popup *popup;
- wl_list_for_each(popup, &roots_surface->layer_surface->popups, link) {
- wlr_surface_send_frame_done(popup->base->surface, when);
- }
- }
- }
-}
-
static void render_output(struct roots_output *output) {
struct wlr_output *wlr_output = output->wlr_output;
struct roots_desktop *desktop = output->desktop;
@@ -423,7 +459,8 @@ static void render_output(struct roots_output *output) {
output_box->y;
view_move(view, view_x, view_y);
- if (has_standalone_surface(view)) {
+ if (has_standalone_surface(view) &&
+ wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) {
wlr_output_set_fullscreen_surface(wlr_output, view->wlr_surface);
} else {
wlr_output_set_fullscreen_surface(wlr_output, NULL);
@@ -537,33 +574,8 @@ damage_finish:
pixman_region32_fini(&damage);
// Send frame done events to all surfaces
- if (output->fullscreen_view != NULL) {
- struct roots_view *view = output->fullscreen_view;
- if (wlr_output->fullscreen_surface == view->wlr_surface) {
- // The surface is managed by the wlr_output
- return;
- }
-
- view_for_each_surface(view, &data.layout, surface_send_frame_done,
- &data);
-
-#ifdef WLR_HAS_XWAYLAND
- if (view->type == ROOTS_XWAYLAND_VIEW) {
- xwayland_children_for_each_surface(view->xwayland_surface,
- surface_send_frame_done, &data.layout, &data);
- }
-#endif
- } else {
- struct roots_view *view;
- wl_list_for_each_reverse(view, &desktop->views, link) {
- view_for_each_surface(view, &data.layout, surface_send_frame_done,
- &data);
- }
-
- drag_icons_for_each_surface(server->input, surface_send_frame_done,
- &data.layout, &data);
- }
- layers_send_done(output, data.when);
+ output_for_each_surface(output, surface_send_frame_done,
+ &data.layout, &data);
}
void output_damage_whole(struct roots_output *output) {
@@ -769,6 +781,7 @@ static void output_destroy(struct roots_output *output) {
wl_list_remove(&output->destroy.link);
wl_list_remove(&output->mode.link);
wl_list_remove(&output->transform.link);
+ wl_list_remove(&output->present.link);
wl_list_remove(&output->damage_frame.link);
wl_list_remove(&output->damage_destroy.link);
free(output);
@@ -805,6 +818,52 @@ static void output_handle_transform(struct wl_listener *listener, void *data) {
arrange_layers(output);
}
+struct presentation_data {
+ struct layout_data layout;
+ struct roots_output *output;
+ struct wlr_presentation_event *event;
+};
+
+static void surface_send_presented(struct wlr_surface *surface, int sx, int sy,
+ void *_data) {
+ struct presentation_data *data = _data;
+ struct roots_output *output = data->output;
+ float rotation = data->layout.rotation;
+
+ double lx, ly;
+ get_layout_position(&data->layout, &lx, &ly, surface, sx, sy);
+
+ if (!surface_intersect_output(surface, output->desktop->layout,
+ output->wlr_output, lx, ly, rotation, NULL)) {
+ return;
+ }
+
+ wlr_presentation_send_surface_presented(output->desktop->presentation,
+ surface, data->event);
+}
+
+static void output_handle_present(struct wl_listener *listener, void *data) {
+ struct roots_output *output =
+ wl_container_of(listener, output, present);
+ struct wlr_output_event_present *output_event = data;
+
+ struct wlr_presentation_event event = {
+ .output = output->wlr_output,
+ .tv_sec = (uint64_t)output_event->when->tv_sec,
+ .tv_nsec = (uint32_t)output_event->when->tv_nsec,
+ .refresh = (uint32_t)output_event->refresh,
+ .seq = (uint64_t)output_event->seq,
+ .flags = output_event->flags,
+ };
+
+ struct presentation_data presentation_data = {
+ .output = output,
+ .event = &event,
+ };
+ output_for_each_surface(output, surface_send_presented,
+ &presentation_data.layout, &presentation_data);
+}
+
void handle_new_output(struct wl_listener *listener, void *data) {
struct roots_desktop *desktop = wl_container_of(listener, desktop,
new_output);
@@ -832,6 +891,8 @@ void handle_new_output(struct wl_listener *listener, void *data) {
wl_signal_add(&wlr_output->events.mode, &output->mode);
output->transform.notify = output_handle_transform;
wl_signal_add(&wlr_output->events.transform, &output->transform);
+ output->present.notify = output_handle_present;
+ wl_signal_add(&wlr_output->events.present, &output->present);
output->damage_frame.notify = output_damage_handle_frame;
wl_signal_add(&output->damage->events.frame, &output->damage_frame);
diff --git a/rootston/rootston.ini.example b/rootston/rootston.ini.example
index bb0efa44..4b75e9c6 100644
--- a/rootston/rootston.ini.example
+++ b/rootston/rootston.ini.example
@@ -53,9 +53,11 @@ meta-key = Logo
# - "close" to close the current view
# - "next_window" to cycle through windows
# - "alpha" to cycle a window's alpha channel
+# - "break_pointer_constraint" to decline and deactivate all pointer constraints
[bindings]
Logo+Shift+e = exit
Logo+q = close
Logo+m = maximize
+Logo+Escape = break_pointer_constraint
Alt+Tab = next_window
Ctrl+Shift+a = alpha
diff --git a/rootston/seat.c b/rootston/seat.c
index 507254d4..82444dcb 100644
--- a/rootston/seat.c
+++ b/rootston/seat.c
@@ -16,6 +16,7 @@
#include "rootston/input.h"
#include "rootston/keyboard.h"
#include "rootston/seat.h"
+#include "rootston/text_input.h"
#include "rootston/xcursor.h"
@@ -178,9 +179,18 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) {
roots_tool->tablet_v2_tool, event->distance);
}
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X) {
+ roots_tool->tilt_x = event->tilt_x;
+ }
+
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y) {
+ roots_tool->tilt_y = event->tilt_y;
+ }
+
if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y)) {
wlr_tablet_v2_tablet_tool_notify_tilt(
- roots_tool->tablet_v2_tool, event->tilt_x, event->tilt_y);
+ roots_tool->tablet_v2_tool,
+ roots_tool->tilt_x, roots_tool->tilt_y);
}
if (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION) {
@@ -285,6 +295,12 @@ static void handle_tool_proximity(struct wl_listener *listener, void *data) {
wl_list_init(&roots_tool->tool_link);
}
+ if (event->state == WLR_TABLET_TOOL_PROXIMITY_OUT) {
+ struct roots_tablet_tool *roots_tool = tool->data;
+ wlr_tablet_v2_tablet_tool_notify_proximity_out(roots_tool->tablet_v2_tool);
+ return;
+ }
+
handle_tablet_tool_position(cursor, event->device->data, event->tool,
true, true, event->x, event->y, 0, 0);
}
@@ -299,6 +315,14 @@ static void handle_request_set_cursor(struct wl_listener *listener,
roots_cursor_handle_request_set_cursor(cursor, event);
}
+static void handle_pointer_focus_change(struct wl_listener *listener,
+ void *data) {
+ struct roots_cursor *cursor =
+ wl_container_of(listener, cursor, focus_change);
+ struct wlr_seat_pointer_focus_change_event *event = data;
+ roots_cursor_handle_focus_change(cursor, event);
+}
+
static void seat_reset_device_mappings(struct roots_seat *seat,
struct wlr_input_device *device) {
struct wlr_cursor *cursor = seat->cursor->cursor;
@@ -434,6 +458,12 @@ static void roots_seat_init_cursor(struct roots_seat *seat) {
wl_signal_add(&seat->seat->events.request_set_cursor,
&seat->cursor->request_set_cursor);
seat->cursor->request_set_cursor.notify = handle_request_set_cursor;
+
+ wl_signal_add(&seat->seat->pointer_state.events.focus_change,
+ &seat->cursor->focus_change);
+ seat->cursor->focus_change.notify = handle_pointer_focus_change;
+
+ wl_list_init(&seat->cursor->constraint_commit.link);
}
static void roots_drag_icon_handle_surface_commit(struct wl_listener *listener,
@@ -567,6 +597,7 @@ struct roots_seat *roots_seat_create(struct roots_input *input, char *name) {
free(seat);
return NULL;
}
+ seat->seat->data = seat;
roots_seat_init_cursor(seat);
if (!seat->cursor) {
@@ -575,6 +606,8 @@ struct roots_seat *roots_seat_create(struct roots_input *input, char *name) {
return NULL;
}
+ roots_input_method_relay_init(seat, &seat->im_relay);
+
wl_list_insert(&input->seats, &seat->link);
seat->new_drag_icon.notify = roots_seat_handle_new_drag_icon;
@@ -1152,6 +1185,7 @@ void roots_seat_set_focus(struct roots_seat *seat, struct roots_view *view) {
if (view == NULL) {
seat->cursor->mode = ROOTS_CURSOR_PASSTHROUGH;
wlr_seat_keyboard_clear_focus(seat->seat);
+ roots_input_method_relay_set_focus(&seat->im_relay, NULL);
return;
}
@@ -1186,6 +1220,12 @@ void roots_seat_set_focus(struct roots_seat *seat, struct roots_view *view) {
wlr_seat_keyboard_notify_enter(seat->seat, view->wlr_surface,
NULL, 0, NULL);
}
+
+ if (seat->cursor) {
+ roots_cursor_update_focus(seat->cursor);
+ }
+
+ roots_input_method_relay_set_focus(&seat->im_relay, view->wlr_surface);
}
/**
@@ -1220,6 +1260,11 @@ void roots_seat_set_focus_layer(struct roots_seat *seat,
wlr_seat_keyboard_notify_enter(seat->seat, layer->surface,
NULL, 0, NULL);
}
+
+
+ if (seat->cursor) {
+ roots_cursor_update_focus(seat->cursor);
+ }
}
void roots_seat_set_exclusive_client(struct roots_seat *seat,
diff --git a/rootston/text_input.c b/rootston/text_input.c
new file mode 100644
index 00000000..70c92761
--- /dev/null
+++ b/rootston/text_input.c
@@ -0,0 +1,310 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <wlr/util/log.h>
+#include "rootston/seat.h"
+#include "rootston/text_input.h"
+
+static struct roots_text_input *relay_get_focusable_text_input(
+ struct roots_input_method_relay *relay) {
+ struct roots_text_input *text_input = NULL;
+ wl_list_for_each(text_input, &relay->text_inputs, link) {
+ if (text_input->pending_focused_surface) {
+ return text_input;
+ }
+ }
+ return NULL;
+}
+
+static struct roots_text_input *relay_get_focused_text_input(
+ struct roots_input_method_relay *relay) {
+ struct roots_text_input *text_input = NULL;
+ wl_list_for_each(text_input, &relay->text_inputs, link) {
+ if (text_input->input->focused_surface) {
+ return text_input;
+ }
+ }
+ return NULL;
+}
+
+static void handle_im_commit(struct wl_listener *listener, void *data) {
+ struct roots_input_method_relay *relay = wl_container_of(listener, relay,
+ input_method_commit);
+
+ struct roots_text_input *text_input = relay_get_focused_text_input(relay);
+ if (!text_input) {
+ return;
+ }
+ struct wlr_input_method_v2 *context = data;
+ assert(context == relay->input_method);
+ if (context->current.preedit.text) {
+ wlr_text_input_v3_send_preedit_string(text_input->input,
+ context->current.preedit.text,
+ context->current.preedit.cursor_begin,
+ context->current.preedit.cursor_end);
+ }
+ if (context->current.commit_text) {
+ wlr_text_input_v3_send_commit_string(text_input->input,
+ context->current.commit_text);
+ }
+ if (context->current.delete.before_length
+ || context->current.delete.after_length) {
+ wlr_text_input_v3_send_delete_surrounding_text(text_input->input,
+ context->current.delete.before_length,
+ context->current.delete.after_length);
+ }
+ wlr_text_input_v3_send_done(text_input->input);
+}
+
+static void text_input_set_pending_focused_surface(
+ struct roots_text_input *text_input, struct wlr_surface *surface) {
+ text_input->pending_focused_surface = surface;
+ wl_signal_add(&surface->events.destroy,
+ &text_input->pending_focused_surface_destroy);
+}
+
+static void text_input_clear_pending_focused_surface(
+ struct roots_text_input *text_input) {
+ wl_list_remove(&text_input->pending_focused_surface_destroy.link);
+ wl_list_init(&text_input->pending_focused_surface_destroy.link);
+ text_input->pending_focused_surface = NULL;
+}
+
+static void handle_im_destroy(struct wl_listener *listener, void *data) {
+ struct roots_input_method_relay *relay = wl_container_of(listener, relay,
+ input_method_destroy);
+ struct wlr_input_method_v2 *context = data;
+ assert(context == relay->input_method);
+ relay->input_method = NULL;
+ struct roots_text_input *text_input = relay_get_focused_text_input(relay);
+ if (text_input) {
+ // keyboard focus is still there, so keep the surface at hand in case
+ // the input method returns
+ text_input_set_pending_focused_surface(text_input,
+ text_input->input->focused_surface);
+ wlr_text_input_v3_send_leave(text_input->input);
+ }
+}
+
+static void relay_send_im_done(struct roots_input_method_relay *relay,
+ struct wlr_text_input_v3 *input) {
+ struct wlr_input_method_v2 *input_method = relay->input_method;
+ if (!input_method) {
+ wlr_log(WLR_INFO, "Sending IM_DONE but im is gone");
+ return;
+ }
+ // TODO: only send each of those if they were modified
+ wlr_input_method_v2_send_surrounding_text(input_method,
+ input->current.surrounding.text, input->current.surrounding.cursor,
+ input->current.surrounding.anchor);
+ wlr_input_method_v2_send_text_change_cause(input_method,
+ input->current.text_change_cause);
+ wlr_input_method_v2_send_content_type(input_method,
+ input->current.content_type.hint, input->current.content_type.purpose);
+ wlr_input_method_v2_send_done(input_method);
+ // TODO: pass intent, display popup size
+}
+
+static struct roots_text_input *text_input_to_roots(
+ struct roots_input_method_relay *relay,
+ struct wlr_text_input_v3 *text_input) {
+ struct roots_text_input *roots_text_input = NULL;
+ wl_list_for_each(roots_text_input, &relay->text_inputs, link) {
+ if (roots_text_input->input == text_input) {
+ return roots_text_input;
+ }
+ }
+ return NULL;
+}
+
+static void handle_text_input_enable(struct wl_listener *listener, void *data) {
+ struct roots_input_method_relay *relay = wl_container_of(listener, relay,
+ text_input_enable);
+ if (relay->input_method == NULL) {
+ wlr_log(WLR_INFO, "Enabling text input when input method is gone");
+ return;
+ }
+ struct roots_text_input *text_input = text_input_to_roots(relay,
+ (struct wlr_text_input_v3*)data);
+ wlr_input_method_v2_send_activate(relay->input_method);
+ relay_send_im_done(relay, text_input->input);
+}
+
+static void handle_text_input_commit(struct wl_listener *listener,
+ void *data) {
+ struct roots_input_method_relay *relay = wl_container_of(listener, relay,
+ text_input_commit);
+ struct roots_text_input *text_input = text_input_to_roots(relay,
+ (struct wlr_text_input_v3*)data);
+ if (!text_input->input->current_enabled) {
+ wlr_log(WLR_INFO, "Inactive text input tried to commit an update");
+ return;
+ }
+ wlr_log(WLR_DEBUG, "Text input committed update");
+ if (relay->input_method == NULL) {
+ wlr_log(WLR_INFO, "Text input committed, but input method is gone");
+ return;
+ }
+ relay_send_im_done(relay, text_input->input);
+}
+
+static void relay_disable_text_input(struct roots_input_method_relay *relay,
+ struct roots_text_input *text_input) {
+ if (relay->input_method == NULL) {
+ wlr_log(WLR_DEBUG, "Disabling text input, but input method is gone");
+ return;
+ }
+ wlr_input_method_v2_send_deactivate(relay->input_method);
+ relay_send_im_done(relay, text_input->input);
+}
+
+static void handle_text_input_disable(struct wl_listener *listener,
+ void *data) {
+ struct roots_input_method_relay *relay = wl_container_of(listener, relay,
+ text_input_disable);
+ struct roots_text_input *text_input = text_input_to_roots(relay,
+ (struct wlr_text_input_v3*)data);
+ relay_disable_text_input(relay, text_input);
+}
+
+static void handle_text_input_destroy(struct wl_listener *listener,
+ void *data) {
+ struct roots_input_method_relay *relay = wl_container_of(listener, relay,
+ text_input_destroy);
+ struct roots_text_input *text_input = text_input_to_roots(relay,
+ (struct wlr_text_input_v3*)data);
+
+ if (text_input->input->current_enabled) {
+ relay_disable_text_input(relay, text_input);
+ }
+ text_input_clear_pending_focused_surface(text_input);
+ wl_list_remove(&text_input->link);
+ text_input->input = NULL;
+ free(text_input);
+}
+
+static void handle_pending_focused_surface_destroy(struct wl_listener *listener,
+ void *data) {
+ struct roots_text_input *text_input = wl_container_of(listener, text_input,
+ pending_focused_surface_destroy);
+ struct wlr_surface *surface = data;
+ assert(text_input->pending_focused_surface == surface);
+ text_input->pending_focused_surface = NULL;
+}
+
+struct roots_text_input *roots_text_input_create(
+ struct roots_input_method_relay *relay,
+ struct wlr_text_input_v3 *text_input) {
+ struct roots_text_input *input = calloc(1, sizeof(struct roots_text_input));
+ if (!input) {
+ return NULL;
+ }
+ input->input = text_input;
+ input->relay = relay;
+
+ wl_signal_add(&text_input->events.enable, &relay->text_input_enable);
+ relay->text_input_enable.notify = handle_text_input_enable;
+
+ wl_signal_add(&text_input->events.commit, &relay->text_input_commit);
+ relay->text_input_commit.notify = handle_text_input_commit;
+
+ wl_signal_add(&text_input->events.disable, &relay->text_input_disable);
+ relay->text_input_disable.notify = handle_text_input_disable;
+
+ wl_signal_add(&text_input->events.destroy, &relay->text_input_destroy);
+ relay->text_input_destroy.notify = handle_text_input_destroy;
+
+ input->pending_focused_surface_destroy.notify =
+ handle_pending_focused_surface_destroy;
+ wl_list_init(&input->pending_focused_surface_destroy.link);
+ return input;
+}
+
+static void relay_handle_text_input(struct wl_listener *listener,
+ void *data) {
+ struct roots_input_method_relay *relay = wl_container_of(listener, relay,
+ text_input_new);
+ struct wlr_text_input_v3 *wlr_text_input = data;
+ if (relay->seat->seat != wlr_text_input->seat) {
+ return;
+ }
+
+ struct roots_text_input *text_input = roots_text_input_create(relay,
+ wlr_text_input);
+ if (!text_input) {
+ return;
+ }
+ wl_list_insert(&relay->text_inputs, &text_input->link);
+}
+
+static void relay_handle_input_method(struct wl_listener *listener,
+ void *data) {
+ struct roots_input_method_relay *relay = wl_container_of(listener, relay,
+ input_method_new);
+ struct wlr_input_method_v2 *input_method = data;
+ if (relay->seat->seat != input_method->seat) {
+ return;
+ }
+
+ if (relay->input_method != NULL) {
+ wlr_log(WLR_INFO, "Attempted to connect second input method to a seat");
+ wlr_input_method_v2_send_unavailable(input_method);
+ return;
+ }
+
+ relay->input_method = input_method;
+ wl_signal_add(&relay->input_method->events.commit,
+ &relay->input_method_commit);
+ relay->input_method_commit.notify = handle_im_commit;
+ wl_signal_add(&relay->input_method->events.destroy,
+ &relay->input_method_destroy);
+ relay->input_method_destroy.notify = handle_im_destroy;
+
+ struct roots_text_input *text_input = relay_get_focusable_text_input(relay);
+ if (text_input) {
+ wlr_text_input_v3_send_enter(text_input->input,
+ text_input->pending_focused_surface);
+ text_input_clear_pending_focused_surface(text_input);
+ }
+}
+
+void roots_input_method_relay_init(struct roots_seat *seat,
+ struct roots_input_method_relay *relay) {
+ relay->seat = seat;
+ wl_list_init(&relay->text_inputs);
+
+ relay->text_input_new.notify = relay_handle_text_input;
+ wl_signal_add(&seat->input->server->desktop->text_input->events.text_input,
+ &relay->text_input_new);
+
+ relay->input_method_new.notify = relay_handle_input_method;
+ wl_signal_add(
+ &seat->input->server->desktop->input_method->events.input_method,
+ &relay->input_method_new);
+}
+
+void roots_input_method_relay_set_focus(struct roots_input_method_relay *relay,
+ struct wlr_surface *surface) {
+ struct roots_text_input *text_input;
+ wl_list_for_each(text_input, &relay->text_inputs, link) {
+ if (text_input->pending_focused_surface) {
+ assert(text_input->input->focused_surface == NULL);
+ if (surface != text_input->pending_focused_surface) {
+ text_input_clear_pending_focused_surface(text_input);
+ }
+ } else if (text_input->input->focused_surface) {
+ assert(text_input->pending_focused_surface == NULL);
+ if (surface != text_input->input->focused_surface) {
+ relay_disable_text_input(relay, text_input);
+ wlr_text_input_v3_send_leave(text_input->input);
+ }
+ } else if (surface
+ && wl_resource_get_client(text_input->input->resource)
+ == wl_resource_get_client(surface->resource)) {
+ if (relay->input_method) {
+ wlr_text_input_v3_send_enter(text_input->input, surface);
+ } else {
+ text_input_set_pending_focused_surface(text_input, surface);
+ }
+ }
+ }
+}