aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/sway/input/cursor.h13
-rw-r--r--include/sway/input/seat.h6
-rw-r--r--include/sway/server.h4
-rw-r--r--protocols/meson.build1
-rw-r--r--sway/input/cursor.c235
-rw-r--r--sway/input/seat.c6
-rw-r--r--sway/input/seatop_down.c11
-rw-r--r--sway/server.c9
8 files changed, 261 insertions, 24 deletions
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h
index c87d8332..072a56ca 100644
--- a/include/sway/input/cursor.h
+++ b/include/sway/input/cursor.h
@@ -2,6 +2,7 @@
#define _SWAY_INPUT_CURSOR_H
#include <stdbool.h>
#include <stdint.h>
+#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_surface.h>
#include "sway/input/seat.h"
@@ -26,6 +27,9 @@ struct sway_cursor {
struct wlr_surface *image_surface;
int hotspot_x, hotspot_y;
+ struct wlr_pointer_constraint_v1 *active_constraint;
+ pixman_region32_t confine; // invalid if active_constraint == NULL
+
struct wl_listener motion;
struct wl_listener motion_absolute;
struct wl_listener button;
@@ -43,6 +47,8 @@ struct sway_cursor {
struct wl_listener request_set_cursor;
+ struct wl_listener constraint_commit;
+
struct wl_event_source *hide_source;
bool hidden;
@@ -75,7 +81,8 @@ int cursor_get_timeout(struct sway_cursor *cursor);
* Like cursor_rebase, but also allows focus to change when the cursor enters a
* new container.
*/
-void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec);
+void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
+ struct sway_node *node, struct wlr_surface *surface, double sx, double sy);
void dispatch_cursor_button(struct sway_cursor *cursor,
struct wlr_input_device *device, uint32_t time_msec, uint32_t button,
@@ -97,6 +104,10 @@ void cursor_warp_to_container(struct sway_cursor *cursor,
void cursor_warp_to_workspace(struct sway_cursor *cursor,
struct sway_workspace *workspace);
+
+void sway_cursor_constrain(struct sway_cursor *cursor,
+ struct wlr_pointer_constraint_v1 *constraint);
+
uint32_t get_mouse_bindsym(const char *name, char **error);
uint32_t get_mouse_bindcode(const char *name, char **error);
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index ef85b67f..1c9354df 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -84,6 +84,12 @@ struct sway_seat {
struct wl_list link; // input_manager::seats
};
+struct sway_pointer_constraint {
+ struct wlr_pointer_constraint_v1 *constraint;
+
+ struct wl_listener destroy;
+};
+
struct sway_seat *seat_create(const char *seat_name);
void seat_destroy(struct sway_seat *seat);
diff --git a/include/sway/server.h b/include/sway/server.h
index 9242ceb7..fa2c6557 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -61,6 +61,9 @@ struct sway_server {
struct wlr_presentation *presentation;
+ struct wlr_pointer_constraints_v1 *pointer_constraints;
+ struct wl_listener pointer_constraint;
+
size_t txn_timeout_ms;
list_t *transactions;
list_t *dirty_nodes;
@@ -86,5 +89,6 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data);
#endif
void handle_server_decoration(struct wl_listener *listener, void *data);
void handle_xdg_decoration(struct wl_listener *listener, void *data);
+void handle_pointer_constraint(struct wl_listener *listener, void *data);
#endif
diff --git a/protocols/meson.build b/protocols/meson.build
index a031245c..c438b078 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -39,6 +39,7 @@ server_protocols = [
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
[wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
+ [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
['wlr-layer-shell-unstable-v1.xml'],
['wlr-input-inhibitor-unstable-v1.xml'],
]
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index c84d6c40..c87efc2b 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -1,4 +1,5 @@
#define _POSIX_C_SOURCE 200809L
+#include <assert.h>
#include <math.h>
#include <libevdev/libevdev.h>
#include <linux/input-event-codes.h>
@@ -6,10 +7,11 @@
#include <float.h>
#include <limits.h>
#include <strings.h>
+#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_cursor.h>
-#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_idle.h>
-#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_xcursor_manager.h>
+#include <wlr/util/region.h>
#include "list.h"
#include "log.h"
#include "config.h"
@@ -327,8 +329,9 @@ void cursor_unhide(struct sway_cursor *cursor) {
cursor_rebase(cursor);
}
-void cursor_send_pointer_motion(struct sway_cursor *cursor,
- uint32_t time_msec) {
+void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
+ struct sway_node *node, struct wlr_surface *surface,
+ double sx, double sy) {
if (time_msec == 0) {
time_msec = get_current_time_msec();
}
@@ -343,12 +346,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor,
return;
}
- struct wlr_surface *surface = NULL;
- double sx, sy;
-
struct sway_node *prev_node = cursor->previous.node;
- struct sway_node *node = node_at_coords(seat,
- cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
// Update the stored previous position
cursor->previous.x = cursor->cursor->x;
@@ -401,9 +399,62 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
struct wlr_event_pointer_motion *event = data;
cursor_handle_activity(cursor);
- wlr_cursor_move(cursor->cursor, event->device,
- event->delta_x, event->delta_y);
- cursor_send_pointer_motion(cursor, event->time_msec);
+
+ double dx = event->delta_x;
+ double dy = event->delta_y;
+
+ struct wlr_surface *surface = NULL;
+ double sx, sy;
+ struct sway_node *node = node_at_coords(cursor->seat,
+ cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
+
+ if (cursor->active_constraint) {
+ if (cursor->active_constraint->surface != surface) {
+ return;
+ }
+
+ double sx_confined, sy_confined;
+ if (!wlr_region_confine(&cursor->confine, sx, sy, sx + dx, sy + dy,
+ &sx_confined, &sy_confined)) {
+ return;
+ }
+
+ dx = sx_confined - sx;
+ dy = sy_confined - sy;
+ }
+
+ wlr_cursor_move(cursor->cursor, event->device, dx, dy);
+ cursor_send_pointer_motion(cursor, event->time_msec, node, surface,
+ sx + dx, sy + dy);
+ transaction_commit_dirty();
+}
+
+static void cursor_motion_absolute(struct sway_cursor *cursor,
+ uint32_t time_msec, struct wlr_input_device *dev,
+ double x, double y) {
+ cursor_handle_activity(cursor);
+
+ double lx, ly;
+ wlr_cursor_absolute_to_layout_coords(cursor->cursor, dev,
+ x, y, &lx, &ly);
+
+ struct wlr_surface *surface = NULL;
+ double sx, sy;
+ struct sway_node *node = node_at_coords(cursor->seat,
+ lx, ly, &surface, &sx, &sy);
+
+ if (cursor->active_constraint) {
+ if (cursor->active_constraint->surface != surface) {
+ return;
+ }
+ if (!pixman_region32_contains_point(&cursor->confine,
+ floor(sx), floor(sy), NULL)) {
+ return;
+ }
+ }
+
+ wlr_cursor_warp_closest(cursor->cursor, dev, lx, ly);
+ cursor_send_pointer_motion(cursor, time_msec, node, surface, sx, sy);
transaction_commit_dirty();
}
@@ -412,10 +463,9 @@ static void handle_cursor_motion_absolute(
struct sway_cursor *cursor =
wl_container_of(listener, cursor, motion_absolute);
struct wlr_event_pointer_motion_absolute *event = data;
- cursor_handle_activity(cursor);
- wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y);
- cursor_send_pointer_motion(cursor, event->time_msec);
- transaction_commit_dirty();
+
+ cursor_motion_absolute(cursor, event->time_msec, event->device,
+ event->x, event->y);
}
/**
@@ -961,9 +1011,7 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) {
apply_mapping_from_region(event->device, ic->mapped_from_region, &x, &y);
}
- wlr_cursor_warp_absolute(cursor->cursor, event->device, x, y);
- cursor_send_pointer_motion(cursor, event->time_msec);
- transaction_commit_dirty();
+ cursor_motion_absolute(cursor, event->time_msec, event->device, x, y);
}
static void handle_tool_tip(struct wl_listener *listener, void *data) {
@@ -1001,6 +1049,49 @@ static void handle_tool_button(struct wl_listener *listener, void *data) {
transaction_commit_dirty();
}
+static void check_constraint_region(struct sway_cursor *cursor) {
+ struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint;
+ pixman_region32_t *region = &constraint->region;
+ struct sway_view *view = view_from_wlr_surface(constraint->surface);
+ if (view) {
+ struct sway_container *con = view->container;
+
+ double sx = cursor->cursor->x - con->content_x + view->geometry.x;
+ double sy = cursor->cursor->y - con->content_y + view->geometry.y;
+
+ if (!pixman_region32_contains_point(region,
+ floor(sx), floor(sy), NULL)) {
+ int nboxes;
+ pixman_box32_t *boxes = pixman_region32_rectangles(region, &nboxes);
+ if (nboxes > 0) {
+ double sx = (boxes[0].x1 + boxes[0].x2) / 2.;
+ double sy = (boxes[0].y1 + boxes[0].y2) / 2.;
+
+ wlr_cursor_warp_closest(cursor->cursor, NULL,
+ sx + con->content_x - view->geometry.x,
+ sy + con->content_y - view->geometry.y);
+ }
+ }
+ }
+
+ // 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);
+ } else {
+ pixman_region32_clear(&cursor->confine);
+ }
+}
+
+static void handle_constraint_commit(struct wl_listener *listener,
+ void *data) {
+ struct sway_cursor *cursor =
+ wl_container_of(listener, cursor, constraint_commit);
+ struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint;
+ assert(constraint->surface == data);
+
+ check_constraint_region(cursor);
+}
+
static void handle_request_set_cursor(struct wl_listener *listener,
void *data) {
struct sway_cursor *cursor =
@@ -1162,6 +1253,8 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
&cursor->request_set_cursor);
cursor->request_set_cursor.notify = handle_request_set_cursor;
+ wl_list_init(&cursor->constraint_commit.link);
+
cursor->cursor = wlr_cursor;
return cursor;
@@ -1284,3 +1377,107 @@ const char *get_mouse_button_name(uint32_t button) {
}
return name;
}
+
+static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) {
+ struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint;
+
+ if (constraint->current.committed &
+ WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) {
+ double sx = constraint->current.cursor_hint.x;
+ double sy = constraint->current.cursor_hint.y;
+
+ struct sway_view *view = view_from_wlr_surface(constraint->surface);
+ struct sway_container *con = view->container;
+
+ double lx = sx + con->content_x - view->geometry.x;
+ double ly = sy + con->content_y - view->geometry.y;
+
+ wlr_cursor_warp(cursor->cursor, NULL, lx, ly);
+ }
+}
+
+void handle_constraint_destroy(struct wl_listener *listener, void *data) {
+ struct sway_pointer_constraint *sway_constraint =
+ wl_container_of(listener, sway_constraint, destroy);
+ struct wlr_pointer_constraint_v1 *constraint = data;
+ struct sway_seat *seat = constraint->seat->data;
+ struct sway_cursor *cursor = seat->cursor;
+
+ wl_list_remove(&sway_constraint->destroy.link);
+
+ if (cursor->active_constraint == constraint) {
+ warp_to_constraint_cursor_hint(cursor);
+
+ if (cursor->constraint_commit.link.next != NULL) {
+ wl_list_remove(&cursor->constraint_commit.link);
+ }
+ wl_list_init(&cursor->constraint_commit.link);
+ cursor->active_constraint = NULL;
+ }
+
+ free(sway_constraint);
+}
+
+void handle_pointer_constraint(struct wl_listener *listener, void *data) {
+ struct wlr_pointer_constraint_v1 *constraint = data;
+ struct sway_seat *seat = constraint->seat->data;
+
+ struct sway_pointer_constraint *sway_constraint =
+ calloc(1, sizeof(struct sway_pointer_constraint));
+ sway_constraint->constraint = constraint;
+
+ sway_constraint->destroy.notify = handle_constraint_destroy;
+ wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy);
+
+ struct sway_node *focus = seat_get_focus(seat);
+ if (focus && focus->type == N_CONTAINER && focus->sway_container->view) {
+ struct wlr_surface *surface = focus->sway_container->view->surface;
+ if (surface == constraint->surface) {
+ sway_cursor_constrain(seat->cursor, constraint);
+ }
+ }
+}
+
+void sway_cursor_constrain(struct sway_cursor *cursor,
+ struct wlr_pointer_constraint_v1 *constraint) {
+ if (cursor->active_constraint == constraint) {
+ return;
+ }
+
+ wl_list_remove(&cursor->constraint_commit.link);
+ if (cursor->active_constraint) {
+ if (constraint == NULL) {
+ warp_to_constraint_cursor_hint(cursor);
+ }
+ wlr_pointer_constraint_v1_send_deactivated(
+ cursor->active_constraint);
+ }
+
+ cursor->active_constraint = constraint;
+
+ if (constraint == NULL) {
+ wl_list_init(&cursor->constraint_commit.link);
+ return;
+ }
+
+ // FIXME: Big hack, stolen from wlr_pointer_constraints_v1.c:121.
+ // This is necessary because the focus may be set before the surface
+ // has finished committing, which means that warping won't work properly,
+ // since this code will be run *after* the focus has been set.
+ // That is why we duplicate the code here.
+ if (pixman_region32_not_empty(&constraint->current.region)) {
+ pixman_region32_intersect(&constraint->region,
+ &constraint->surface->input_region, &constraint->current.region);
+ } else {
+ pixman_region32_copy(&constraint->region,
+ &constraint->surface->input_region);
+ }
+
+ check_constraint_region(cursor);
+
+ wlr_pointer_constraint_v1_send_activated(constraint);
+
+ cursor->constraint_commit.notify = handle_constraint_commit;
+ wl_signal_add(&constraint->surface->events.commit,
+ &cursor->constraint_commit);
+}
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 8cb1d8e9..d159da22 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -105,6 +105,11 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {
wlr_seat_keyboard_notify_enter(
seat->wlr_seat, view->surface, NULL, 0, NULL);
}
+
+ struct wlr_pointer_constraint_v1 *constraint =
+ wlr_pointer_constraints_v1_constraint_for_surface(
+ server.pointer_constraints, view->surface, seat->wlr_seat);
+ sway_cursor_constrain(seat->cursor, constraint);
}
}
@@ -684,6 +689,7 @@ static void send_unfocus(struct sway_container *con, void *data) {
// Unfocus the container and any children (eg. when leaving `focus parent`)
static void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) {
+ sway_cursor_constrain(seat->cursor, NULL);
wlr_seat_keyboard_clear_focus(seat->wlr_seat);
if (node->type == N_WORKSPACE) {
workspace_for_each_container(node->sway_workspace, send_unfocus, seat);
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c
index c491e9c2..c2256c9a 100644
--- a/sway/input/seatop_down.c
+++ b/sway/input/seatop_down.c
@@ -26,14 +26,19 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
static void handle_finish(struct sway_seat *seat) {
struct seatop_down_event *e = seat->seatop_data;
+ struct sway_cursor *cursor = seat->cursor;
// Set the cursor's previous coords to the x/y at the start of the
// operation, so the container change will be detected if using
// focus_follows_mouse and the cursor moved off the original container
// during the operation.
- seat->cursor->previous.x = e->ref_lx;
- seat->cursor->previous.y = e->ref_ly;
+ cursor->previous.x = e->ref_lx;
+ cursor->previous.y = e->ref_ly;
if (e->moved) {
- cursor_send_pointer_motion(seat->cursor, 0);
+ struct wlr_surface *surface = NULL;
+ double sx, sy;
+ struct sway_node *node = node_at_coords(seat,
+ cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
+ cursor_send_pointer_motion(cursor, 0, node, surface, sx, sy);
}
}
diff --git a/sway/server.c b/sway/server.c
index 43dc3900..82262585 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -10,11 +10,12 @@
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_data_control_v1.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
-#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_gamma_control.h>
+#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_gtk_primary_selection.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_layer_shell_v1.h>
+#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_screencopy_v1.h>
#include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_xcursor_manager.h>
@@ -105,6 +106,12 @@ bool server_init(struct sway_server *server) {
server->xdg_decoration.notify = handle_xdg_decoration;
wl_list_init(&server->xdg_decorations);
+ server->pointer_constraints =
+ wlr_pointer_constraints_v1_create(server->wl_display);
+ server->pointer_constraint.notify = handle_pointer_constraint;
+ wl_signal_add(&server->pointer_constraints->events.new_constraint,
+ &server->pointer_constraint);
+
server->presentation =
wlr_presentation_create(server->wl_display, server->backend);