diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 527d03500..9cf029f42 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -147,4 +147,5 @@ const char *get_mouse_button_name(uint32_t button); void handle_request_set_cursor_shape(struct wl_listener *listener, void *data); +void handle_pointer_warp(struct wl_listener *listener, void *data); #endif diff --git a/include/sway/server.h b/include/sway/server.h index 8c8114882..c94f67c3b 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -81,6 +81,9 @@ struct sway_server { struct wlr_pointer_constraints_v1 *pointer_constraints; struct wl_listener pointer_constraint; + struct wlr_pointer_warp_v1 *pointer_warp_v1; + struct wl_listener pointer_warp; + struct wlr_xdg_output_manager_v1 *xdg_output_manager_v1; struct wlr_output_manager_v1 *output_manager_v1; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index fc0f11fd8..9cc74dfed 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -1415,3 +1416,43 @@ void handle_request_set_cursor_shape(struct wl_listener *listener, void *data) { cursor_set_image(seat->cursor, wlr_cursor_shape_v1_name(event->shape), focused_client); } + +void handle_pointer_warp(struct wl_listener *listener, void *data) { + const struct wlr_pointer_warp_v1_event_warp *event = data; + struct sway_seat *seat = event->seat_client->seat->data; + struct sway_cursor *cursor = seat->cursor; + + struct wl_client *focused_client = NULL; + struct wlr_surface *focused_surface = + cursor->seat->wlr_seat->pointer_state.focused_surface; + if (focused_surface != NULL) { + focused_client = wl_resource_get_client(focused_surface->resource); + } + + if (focused_client == NULL || event->seat_client->client != focused_client) { + sway_log(SWAY_DEBUG, "denying request to warp cursor from unfocused client"); + return; + } + + struct wlr_box surface_box = { + .width = event->surface->current.width, + .height = event->surface->current.height, + }; + + if (!wlr_box_contains_point(&surface_box, event->x, event->y)) { + sway_log(SWAY_DEBUG, "denying request to warp cursor out of surface"); + return; + } + + struct sway_view *view = view_from_wlr_surface(event->surface); + if (view == NULL) { + return; + } + + struct sway_container *con = view->container; + double lx = event->x + con->pending.content_x - view->geometry.x; + double ly = event->y + con->pending.content_y - view->geometry.y; + wlr_cursor_warp(cursor->cursor, NULL, lx, ly); + wlr_seat_pointer_warp(event->seat_client->seat, event->x, event->y); + cursor_rebase(cursor); +} diff --git a/sway/server.c b/sway/server.c index ed3468b53..dc66a7a1e 100644 --- a/sway/server.c +++ b/sway/server.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -426,6 +427,14 @@ bool server_init(struct sway_server *server) { wl_signal_add(&server->pointer_constraints->events.new_constraint, &server->pointer_constraint); + server->pointer_warp_v1 = wlr_pointer_warp_v1_create(server->wl_display, 1); + if (!server->pointer_warp_v1) { + sway_log(SWAY_ERROR, "Failed to create pointer warp"); + return false; + } + server->pointer_warp.notify = handle_pointer_warp; + wl_signal_add(&server->pointer_warp_v1->events.warp, &server->pointer_warp); + if (!wlr_presentation_create(server->wl_display, server->backend, SWAY_PRESENTATION_VERSION)) { sway_log(SWAY_ERROR, "Failed to create presentation"); return false; @@ -724,6 +733,7 @@ void server_fini(struct sway_server *server) { wl_list_remove(&server->server_decoration.link); wl_list_remove(&server->xdg_decoration.link); wl_list_remove(&server->pointer_constraint.link); + wl_list_remove(&server->pointer_warp.link); wl_list_remove(&server->output_manager_apply.link); wl_list_remove(&server->output_manager_test.link); wl_list_remove(&server->output_power_manager_set_mode.link);