aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2018-09-27 05:57:43 -0500
committerGitHub <noreply@github.com>2018-09-27 05:57:43 -0500
commit5e9959daaaa86394917144c9928c734fbf533f04 (patch)
tree7503c7ea870f40d68b0ee594d5129cd592ccbff4
parente47b8cd629f9378c74673cb577cc268667a71d1e (diff)
parent1b598f67ca28dce6b61829694ba1a55d05e35678 (diff)
Merge pull request #852 from Laaas/master
Implement pointer-constraints-unstable-v1 protocol
-rw-r--r--examples/meson.build4
-rw-r--r--examples/pointer-constraints.c260
-rw-r--r--include/rootston/cursor.h41
-rw-r--r--include/rootston/desktop.h2
-rw-r--r--include/rootston/input.h2
-rw-r--r--include/rootston/output.h3
-rw-r--r--include/rootston/seat.h8
-rw-r--r--include/wlr/types/wlr_box.h3
-rw-r--r--include/wlr/types/wlr_cursor.h12
-rw-r--r--include/wlr/types/wlr_pointer_constraints_v1.h102
-rw-r--r--include/wlr/types/wlr_region.h3
-rw-r--r--include/wlr/types/wlr_seat.h15
-rw-r--r--include/wlr/types/wlr_surface.h5
-rw-r--r--include/wlr/util/region.h5
-rw-r--r--protocol/meson.build2
-rw-r--r--rootston/config.c1
-rw-r--r--rootston/cursor.c219
-rw-r--r--rootston/desktop.c67
-rw-r--r--rootston/keyboard.c8
-rw-r--r--rootston/output.c2
-rw-r--r--rootston/rootston.ini.example2
-rw-r--r--rootston/seat.c24
-rw-r--r--types/meson.build1
-rw-r--r--types/seat/wlr_seat.c2
-rw-r--r--types/seat/wlr_seat_pointer.c25
-rw-r--r--types/wlr_box.c9
-rw-r--r--types/wlr_cursor.c6
-rw-r--r--types/wlr_pointer_constraints_v1.c372
-rw-r--r--types/wlr_surface.c20
-rw-r--r--util/region.c71
30 files changed, 1238 insertions, 58 deletions
diff --git a/examples/meson.build b/examples/meson.build
index b5ad6c98..86c0ddbb 100644
--- a/examples/meson.build
+++ b/examples/meson.build
@@ -70,6 +70,10 @@ examples = {
'src': 'gamma-control.c',
'dep': [wayland_client, wayland_cursor, wlr_protos, wlroots],
},
+ 'pointer-constraints': {
+ 'src': 'pointer-constraints.c',
+ 'dep': [wayland_client, wlr_protos, wlroots],
+ },
'dmabuf-capture': {
'src': 'dmabuf-capture.c',
'dep': [
diff --git a/examples/pointer-constraints.c b/examples/pointer-constraints.c
new file mode 100644
index 00000000..1df9f6ce
--- /dev/null
+++ b/examples/pointer-constraints.c
@@ -0,0 +1,260 @@
+#include <GLES2/gl2.h>
+#include <linux/input-event-codes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wayland-client.h>
+#include <wayland-egl.h>
+#include <wlr/render/egl.h>
+#include "xdg-shell-client-protocol.h"
+#include "pointer-constraints-unstable-v1-client-protocol.h"
+
+static int width = 512, height = 512;
+
+static struct wl_compositor *compositor = NULL;
+static struct wl_seat *seat = NULL;
+static struct xdg_wm_base *wm_base = NULL;
+static struct zwp_pointer_constraints_v1 *pointer_constraints = NULL;
+
+struct wlr_egl egl;
+struct wl_egl_window *egl_window;
+struct wlr_egl_surface *egl_surface;
+struct zwp_locked_pointer_v1* locked_pointer;
+struct zwp_confined_pointer_v1* confined_pointer;
+
+enum {
+ REGION_TYPE_NONE,
+ REGION_TYPE_DISJOINT,
+ REGION_TYPE_JOINT,
+ REGION_TYPE_MAX
+} region_type = REGION_TYPE_NONE;
+
+struct wl_region *regions[3];
+
+static void draw(void) {
+ eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
+
+ float color[] = {1.0, 1.0, 0.0, 1.0};
+
+ glViewport(0, 0, width, height);
+ glClearColor(color[0], color[1], color[2], 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ eglSwapBuffers(egl.display, egl_surface);
+}
+
+static void pointer_handle_button(void *data, struct wl_pointer *pointer,
+ uint32_t serial, uint32_t time, uint32_t button, uint32_t state_w) {
+ struct wl_surface *surface = data;
+
+ if (button == BTN_LEFT && state_w == WL_POINTER_BUTTON_STATE_PRESSED) {
+ region_type = (region_type + 1) % REGION_TYPE_MAX;
+
+ if (locked_pointer) {
+ zwp_locked_pointer_v1_set_region(locked_pointer,
+ regions[region_type]);
+ } else if (confined_pointer) {
+ zwp_confined_pointer_v1_set_region(confined_pointer,
+ regions[region_type]);
+ }
+
+ wl_surface_commit(surface);
+ }
+}
+
+static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, struct wl_surface *surface,
+ wl_fixed_t surface_x, wl_fixed_t surface_y) {
+ // This space intentionally left blank
+}
+
+static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, struct wl_surface *surface) {
+ // This space intentionally left blank
+}
+
+static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
+ // This space intentionally left blank
+}
+
+static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, uint32_t axis, wl_fixed_t value) {
+ // This space intentionally left blank
+}
+
+static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
+ // This space intentionally left blank
+}
+
+static void pointer_handle_axis_source(void *data,
+ struct wl_pointer *wl_pointer, uint32_t axis_source) {
+ // This space intentionally left blank
+}
+
+static void pointer_handle_axis_stop(void *data,
+ struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) {
+ // This space intentionally left blank
+}
+
+static void pointer_handle_axis_discrete(void *data,
+ struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
+ // This space intentionally left blank
+}
+
+static const struct wl_pointer_listener pointer_listener = {
+ .enter = pointer_handle_enter,
+ .leave = pointer_handle_leave,
+ .motion = pointer_handle_motion,
+ .button = pointer_handle_button,
+ .axis = pointer_handle_axis,
+ .frame = pointer_handle_frame,
+ .axis_source = pointer_handle_axis_source,
+ .axis_stop = pointer_handle_axis_stop,
+ .axis_discrete = pointer_handle_axis_discrete,
+};
+
+static void xdg_surface_handle_configure(void *data,
+ struct xdg_surface *xdg_surface, uint32_t serial) {
+ xdg_surface_ack_configure(xdg_surface, serial);
+ wl_egl_window_resize(egl_window, width, height, 0, 0);
+ draw();
+}
+
+static const struct xdg_surface_listener xdg_surface_listener = {
+ .configure = xdg_surface_handle_configure,
+};
+
+static void xdg_toplevel_handle_configure(void *data,
+ struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h,
+ struct wl_array *states) {
+ width = w;
+ height = h;
+}
+
+static void xdg_toplevel_handle_close(void *data,
+ struct xdg_toplevel *xdg_toplevel) {
+ exit(EXIT_SUCCESS);
+}
+
+static const struct xdg_toplevel_listener xdg_toplevel_listener = {
+ .configure = xdg_toplevel_handle_configure,
+ .close = xdg_toplevel_handle_close,
+};
+
+static void handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version) {
+ if (strcmp(interface, wl_compositor_interface.name) == 0) {
+ compositor = wl_registry_bind(registry, name,
+ &wl_compositor_interface, 1);
+ } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
+ wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
+ } else if (strcmp(interface, wl_seat_interface.name) == 0) {
+ seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
+ } else if (strcmp(interface,
+ zwp_pointer_constraints_v1_interface.name) == 0) {
+ pointer_constraints = wl_registry_bind(registry, name,
+ &zwp_pointer_constraints_v1_interface, version);
+ }
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = handle_global,
+ .global_remove = NULL,
+};
+
+int main(int argc, char **argv) {
+ if (argc != 4) {
+ goto invalid_args;
+ }
+
+ bool lock;
+ if (strcmp(argv[1], "lock") == 0) {
+ lock = true;
+ } else if (strcmp(argv[1], "confine") == 0) {
+ lock = false;
+ } else {
+ goto invalid_args;
+ }
+
+ enum zwp_pointer_constraints_v1_lifetime lifetime;
+ if (strcmp(argv[2], "oneshot") == 0) {
+ lifetime = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT;
+ } else if (strcmp(argv[2], "persistent") == 0) {
+ lifetime = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT;
+ } else {
+ goto invalid_args;
+ }
+
+ if (strcmp(argv[3], "no-region") == 0) {
+ region_type = REGION_TYPE_NONE;
+ } else if (strcmp(argv[3], "disjoint-region") == 0) {
+ region_type = REGION_TYPE_DISJOINT;
+ } else if (strcmp(argv[3], "joint-region") == 0) {
+ region_type = REGION_TYPE_JOINT;
+ }
+
+ struct wl_display *display = wl_display_connect(NULL);
+
+ struct wl_registry *registry = wl_display_get_registry(display);
+ wl_registry_add_listener(registry, &registry_listener, NULL);
+ wl_display_dispatch(display);
+ wl_display_roundtrip(display);
+
+ struct wl_region *disjoint_region = wl_compositor_create_region(compositor);
+ wl_region_add(disjoint_region, 0, 0, 255, 256);
+ wl_region_add(disjoint_region, 257, 0, 255, 256);
+ regions[REGION_TYPE_DISJOINT] = disjoint_region;
+
+ struct wl_region *joint_region = wl_compositor_create_region(compositor);
+ wl_region_add(joint_region, 0, 0, 256, 256);
+ wl_region_add(joint_region, 256, 0, 256, 256);
+ wl_region_add(joint_region, 256, 256, 256, 256);
+ regions[REGION_TYPE_JOINT] = joint_region;
+
+ wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
+ WL_SHM_FORMAT_ARGB8888);
+
+ struct wl_surface *surface = wl_compositor_create_surface(compositor);
+ struct xdg_surface *xdg_surface =
+ xdg_wm_base_get_xdg_surface(wm_base, surface);
+ struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
+
+ xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL);
+ xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
+
+ struct wl_pointer *pointer = wl_seat_get_pointer(seat);
+ wl_pointer_add_listener(pointer, &pointer_listener, surface);
+
+ if (lock) {
+ locked_pointer = zwp_pointer_constraints_v1_lock_pointer(
+ pointer_constraints, surface, pointer,
+ regions[region_type], lifetime);
+
+ zwp_locked_pointer_v1_set_cursor_position_hint(locked_pointer,
+ wl_fixed_from_int(128), wl_fixed_from_int(128));
+ } else {
+ confined_pointer = zwp_pointer_constraints_v1_confine_pointer(
+ pointer_constraints, surface, pointer,
+ regions[region_type], lifetime);
+ }
+
+ wl_surface_commit(surface);
+
+ egl_window = wl_egl_window_create(surface, width, height);
+ egl_surface = wlr_egl_create_surface(&egl, egl_window);
+
+ wl_display_roundtrip(display);
+
+ draw();
+
+ while (wl_display_dispatch(display) != -1) {}
+
+ return EXIT_SUCCESS;
+
+invalid_args:
+ fprintf(stderr, "pointer-constraints <lock | confine> "
+ "<oneshot | persistent> "
+ "<no-region | disjoint-rejoin | joint-region>\n");
+ return EXIT_FAILURE;
+}
diff --git a/include/rootston/cursor.h b/include/rootston/cursor.h
index 2c687a39..b5bb682f 100644
--- a/include/rootston/cursor.h
+++ b/include/rootston/cursor.h
@@ -1,6 +1,7 @@
#ifndef ROOTSTON_CURSOR_H
#define ROOTSTON_CURSOR_H
+#include <wlr/types/wlr_pointer_constraints_v1.h>
#include "rootston/seat.h"
enum roots_cursor_mode {
@@ -14,6 +15,9 @@ struct roots_cursor {
struct roots_seat *seat;
struct wlr_cursor *cursor;
+ struct wlr_pointer_constraint_v1 *active_constraint;
+ pixman_region32_t confine; // invalid if active_constraint == NULL
+
const char *default_xcursor;
enum roots_cursor_mode mode;
@@ -28,6 +32,7 @@ struct roots_cursor {
uint32_t resize_edges;
struct roots_seat_view *pointer_view;
+ struct wlr_surface *wlr_surface;
struct wl_listener motion;
struct wl_listener motion_absolute;
@@ -44,6 +49,10 @@ struct roots_cursor {
struct wl_listener tool_button;
struct wl_listener request_set_cursor;
+
+ struct wl_listener focus_change;
+
+ struct wl_listener constraint_commit;
};
struct roots_cursor *roots_cursor_create(struct roots_seat *seat);
@@ -51,36 +60,46 @@ struct roots_cursor *roots_cursor_create(struct roots_seat *seat);
void roots_cursor_destroy(struct roots_cursor *cursor);
void roots_cursor_handle_motion(struct roots_cursor *cursor,
- struct wlr_event_pointer_motion *event);
+ struct wlr_event_pointer_motion *event);
void roots_cursor_handle_motion_absolute(struct roots_cursor *cursor,
- struct wlr_event_pointer_motion_absolute *event);
+ struct wlr_event_pointer_motion_absolute *event);
void roots_cursor_handle_button(struct roots_cursor *cursor,
- struct wlr_event_pointer_button *event);
+ struct wlr_event_pointer_button *event);
void roots_cursor_handle_axis(struct roots_cursor *cursor,
- struct wlr_event_pointer_axis *event);
+ struct wlr_event_pointer_axis *event);
void roots_cursor_handle_touch_down(struct roots_cursor *cursor,
- struct wlr_event_touch_down *event);
+ struct wlr_event_touch_down *event);
void roots_cursor_handle_touch_up(struct roots_cursor *cursor,
- struct wlr_event_touch_up *event);
+ struct wlr_event_touch_up *event);
void roots_cursor_handle_touch_motion(struct roots_cursor *cursor,
- struct wlr_event_touch_motion *event);
+ struct wlr_event_touch_motion *event);
void roots_cursor_handle_tool_axis(struct roots_cursor *cursor,
- struct wlr_event_tablet_tool_axis *event);
+ struct wlr_event_tablet_tool_axis *event);
void roots_cursor_handle_tool_tip(struct roots_cursor *cursor,
- struct wlr_event_tablet_tool_tip *event);
+ struct wlr_event_tablet_tool_tip *event);
void roots_cursor_handle_request_set_cursor(struct roots_cursor *cursor,
- struct wlr_seat_pointer_request_set_cursor_event *event);
+ struct wlr_seat_pointer_request_set_cursor_event *event);
+
+void roots_cursor_handle_focus_change(struct roots_cursor *cursor,
+ struct wlr_seat_pointer_focus_change_event *event);
+
+void roots_cursor_handle_constraint_commit(struct roots_cursor *cursor);
void roots_cursor_update_position(struct roots_cursor *cursor,
- uint32_t time);
+ uint32_t time);
+
+void roots_cursor_update_focus(struct roots_cursor *cursor);
+
+void roots_cursor_constrain(struct roots_cursor *cursor,
+ struct wlr_pointer_constraint_v1 *constraint, double sx, double sy);
#endif
diff --git a/include/rootston/desktop.h b/include/rootston/desktop.h
index 89d8af4a..90851a17 100644
--- a/include/rootston/desktop.h
+++ b/include/rootston/desktop.h
@@ -56,6 +56,7 @@ struct roots_desktop {
struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard;
struct wlr_screencopy_manager_v1 *screencopy;
struct wlr_tablet_manager_v2 *tablet_v2;
+ struct wlr_pointer_constraints_v1 *pointer_constraints;
struct wl_listener new_output;
struct wl_listener layout_change;
@@ -67,6 +68,7 @@ struct roots_desktop {
struct wl_listener input_inhibit_activate;
struct wl_listener input_inhibit_deactivate;
struct wl_listener virtual_keyboard_new;
+ struct wl_listener pointer_constraint;
#ifdef WLR_HAS_XWAYLAND
struct wlr_xwayland *xwayland;
diff --git a/include/rootston/input.h b/include/rootston/input.h
index 2cdb13e6..31810b4d 100644
--- a/include/rootston/input.h
+++ b/include/rootston/input.h
@@ -16,7 +16,7 @@ struct roots_input {
struct wl_listener new_input;
- struct wl_list seats;
+ struct wl_list seats; // roots_seat::link
};
struct roots_input *input_create(struct roots_server *server,
diff --git a/include/rootston/output.h b/include/rootston/output.h
index f78ee81d..69bc5126 100644
--- a/include/rootston/output.h
+++ b/include/rootston/output.h
@@ -28,6 +28,9 @@ struct roots_output {
struct wl_listener damage_destroy;
};
+void rotate_child_position(double *sx, double *sy, double sw, double sh,
+ double pw, double ph, float rotation);
+
void handle_new_output(struct wl_listener *listener, void *data);
struct roots_view;
diff --git a/include/rootston/seat.h b/include/rootston/seat.h
index c5e584b6..1248918a 100644
--- a/include/rootston/seat.h
+++ b/include/rootston/seat.h
@@ -10,7 +10,7 @@ struct roots_seat {
struct roots_input *input;
struct wlr_seat *seat;
struct roots_cursor *cursor;
- struct wl_list link;
+ struct wl_list link; // roots_input::seats
// coordinates of the first touch point if it exists
int32_t touch_id;
@@ -122,6 +122,12 @@ struct roots_tablet_tool {
struct wl_listener tablet_destroy;
};
+struct roots_pointer_constraint {
+ struct wlr_pointer_constraint_v1 *constraint;
+
+ struct wl_listener destroy;
+};
+
struct roots_seat *roots_seat_create(struct roots_input *input, char *name);
void roots_seat_destroy(struct roots_seat *seat);
diff --git a/include/wlr/types/wlr_box.h b/include/wlr/types/wlr_box.h
index 11a53b56..8e25918b 100644
--- a/include/wlr/types/wlr_box.h
+++ b/include/wlr/types/wlr_box.h
@@ -9,6 +9,7 @@
#ifndef WLR_TYPES_WLR_BOX_H
#define WLR_TYPES_WLR_BOX_H
+#include <pixman.h>
#include <stdbool.h>
#include <wayland-server.h>
@@ -40,4 +41,6 @@ void wlr_box_transform(const struct wlr_box *box,
void wlr_box_rotated_bounds(const struct wlr_box *box, float rotation,
struct wlr_box *dest);
+void wlr_box_from_pixman_box32(const pixman_box32_t box, struct wlr_box *dest);
+
#endif
diff --git a/include/wlr/types/wlr_cursor.h b/include/wlr/types/wlr_cursor.h
index ba582be9..44ced1f0 100644
--- a/include/wlr/types/wlr_cursor.h
+++ b/include/wlr/types/wlr_cursor.h
@@ -91,6 +91,18 @@ bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev,
void wlr_cursor_absolute_to_layout_coords(struct wlr_cursor *cur,
struct wlr_input_device *dev, double x, double y, double *lx, double *ly);
+
+/**
+ * Warp the cursor to the given x and y coordinates. If the given point is out
+ * of the layout boundaries or constraints, the closest point will be used.
+ * If one coordinate is NAN, it will be ignored.
+ *
+ * `dev` may be passed to respect device mapping constraints. If `dev` is NULL,
+ * device mapping constraints will be ignored.
+ */
+void wlr_cursor_warp_closest(struct wlr_cursor *cur,
+ struct wlr_input_device *dev, double x, double y);
+
/**
* Warp the cursor to the given x and y in absolute 0..1 coordinates. If the
* given point is out of the layout boundaries or constraints, the closest point
diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h
new file mode 100644
index 00000000..fef8c2e9
--- /dev/null
+++ b/include/wlr/types/wlr_pointer_constraints_v1.h
@@ -0,0 +1,102 @@
+/*
+ * This an unstable interface of wlroots. No guarantees are made regarding the
+ * future consistency of this API.
+ */
+#ifndef WLR_USE_UNSTABLE
+#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
+#endif
+
+#ifndef WLR_TYPES_WLR_POINTER_CONSTRAINTS_V1_H
+#define WLR_TYPES_WLR_POINTER_CONSTRAINTS_V1_H
+
+#include <stdint.h>
+#include <wayland-server.h>
+#include <pixman.h>
+#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_seat.h>
+#include "pointer-constraints-unstable-v1-protocol.h"
+
+struct wlr_seat;
+
+enum wlr_pointer_constraint_v1_type {
+ WLR_POINTER_CONSTRAINT_V1_LOCKED,
+ WLR_POINTER_CONSTRAINT_V1_CONFINED,
+};
+
+enum wlr_pointer_constraint_v1_state_field {
+ WLR_POINTER_CONSTRAINT_V1_STATE_REGION = 1 << 0,
+ WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT = 1 << 1,
+};
+
+struct wlr_pointer_constraint_v1_state {
+ uint32_t committed; // enum wlr_pointer_constraint_v1_state_field
+ pixman_region32_t region;
+
+ // only valid for locked_pointer
+ struct {
+ double x, y;
+ } cursor_hint;
+};
+
+struct wlr_pointer_constraint_v1 {
+ struct wlr_pointer_constraints_v1 *pointer_constraints;
+
+ struct wl_resource *resource;
+ struct wlr_surface *surface;
+ struct wlr_seat *seat;
+ enum zwp_pointer_constraints_v1_lifetime lifetime;
+ enum wlr_pointer_constraint_v1_type type;
+ pixman_region32_t region;
+
+ struct wlr_pointer_constraint_v1_state current, pending;
+
+ struct wl_listener surface_commit;
+ struct wl_listener surface_destroy;
+ struct wl_listener seat_destroy;
+
+ struct wl_list link; // wlr_pointer_constraints_v1::constraints
+
+ struct {
+ struct wl_signal destroy;
+ } events;
+
+ void *data;
+};
+
+struct wlr_pointer_constraints_v1 {
+ struct wl_list resources; // wl_resource_get_link
+ struct wl_global *global;
+
+ struct {
+ /**
+ * Called when a new pointer constraint is created.
+ *
+ * data: struct wlr_pointer_constraint_v1 *
+ */
+ struct wl_signal new_constraint;
+ } events;
+
+ struct wl_list constraints; // wlr_pointer_constraint_v1::link
+
+ void *data;
+};
+
+struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1_create(
+ struct wl_display *display);
+void wlr_pointer_constraints_v1_destroy(
+ struct wlr_pointer_constraints_v1 *pointer_constraints);
+
+struct wlr_pointer_constraint_v1 *
+ wlr_pointer_constraints_v1_constraint_for_surface(
+ struct wlr_pointer_constraints_v1 *pointer_constraints,
+ struct wlr_surface *surface, struct wlr_seat *seat);
+
+void wlr_pointer_constraint_v1_send_activated(
+ struct wlr_pointer_constraint_v1 *constraint);
+/**
+ * Deactivate the constraint. May destroy the constraint.
+ */
+void wlr_pointer_constraint_v1_send_deactivated(
+ struct wlr_pointer_constraint_v1 *constraint);
+
+#endif
diff --git a/include/wlr/types/wlr_region.h b/include/wlr/types/wlr_region.h
index 3c4a0532..ec7f73aa 100644
--- a/include/wlr/types/wlr_region.h
+++ b/include/wlr/types/wlr_region.h
@@ -10,8 +10,7 @@
#define WLR_TYPES_WLR_REGION_H
#include <pixman.h>
-
-struct wl_resource;
+#include <wayland-server-protocol.h>
/*
* Creates a new region resource with the provided new ID. If `resource_list` is
diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h
index b3c02cf2..59de8207 100644
--- a/include/wlr/types/wlr_seat.h
+++ b/include/wlr/types/wlr_seat.h
@@ -146,6 +146,10 @@ struct wlr_seat_pointer_state {
uint32_t grab_time;
struct wl_listener surface_destroy;
+
+ struct {
+ struct wl_signal focus_change; // wlr_seat_pointer_focus_change_event
+ } events;
};
// TODO: May be useful to be able to simulate keyboard input events
@@ -238,6 +242,12 @@ struct wlr_seat_pointer_request_set_cursor_event {
int32_t hotspot_x, hotspot_y;
};
+struct wlr_seat_pointer_focus_change_event {
+ struct wlr_seat *seat;
+ struct wlr_surface *old_surface, *new_surface;
+ double sx, sy;
+};
+
/**
* Allocates a new wlr_seat and adds a wl_seat global to the display.
*/
@@ -546,6 +556,9 @@ bool wlr_seat_touch_has_grab(struct wlr_seat *seat);
bool wlr_seat_validate_grab_serial(struct wlr_seat *seat, uint32_t serial);
struct wlr_seat_client *wlr_seat_client_from_resource(
- struct wl_resource *resource);
+ struct wl_resource *resource);
+
+struct wlr_seat_client *wlr_seat_client_from_pointer_resource(
+ struct wl_resource *resource);
#endif
diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h
index 063f9e26..bfbfbcc8 100644
--- a/include/wlr/types/wlr_surface.h
+++ b/include/wlr/types/wlr_surface.h
@@ -82,6 +82,11 @@ struct wlr_surface {
*/
pixman_region32_t opaque_region;
/**
+ * The current input region, in surface-local coordinates. It is clipped to
+ * the surface bounds.
+ */
+ pixman_region32_t input_region;
+ /**
* `current` contains the current, committed surface state. `pending`
* accumulates state changes from the client between commits and shouldn't
* be accessed by the compositor directly. `previous` contains the state of
diff --git a/include/wlr/util/region.h b/include/wlr/util/region.h
index 32387bfb..4aca07e1 100644
--- a/include/wlr/util/region.h
+++ b/include/wlr/util/region.h
@@ -16,6 +16,8 @@
#ifndef WLR_UTIL_REGION_H
#define WLR_UTIL_REGION_H
+
+#include <stdbool.h>
#include <pixman.h>
#include <wayland-server.h>
@@ -48,4 +50,7 @@ void wlr_region_expand(pixman_region32_t *dst, pixman_region32_t *src,
void wlr_region_rotated_bounds(pixman_region32_t *dst, pixman_region32_t *src,
float rotation, int ox, int oy);
+bool wlr_region_confine(pixman_region32_t *region, double x1, double y1, double x2,
+ double y2, double *x2_out, double *y2_out);
+
#endif
diff --git a/protocol/meson.build b/protocol/meson.build
index c46ad131..e03b88e8 100644
--- a/protocol/meson.build
+++ b/protocol/meson.build
@@ -18,6 +18,7 @@ protocols = [
[wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
+ [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
'gamma-control.xml',
'gtk-primary-selection.xml',
'idle.xml',
@@ -36,6 +37,7 @@ client_protocols = [
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
+ [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
'idle.xml',
'screenshooter.xml',
'wlr-export-dmabuf-unstable-v1.xml',
diff --git a/rootston/config.c b/rootston/config.c
index 92d90de1..5c168679 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;
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..c180c839 100644
--- a/rootston/desktop.c
+++ b/rootston/desktop.c
@@ -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>
@@ -776,6 +777,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");
@@ -887,10 +944,10 @@ 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->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create(
server->wl_display);
@@ -906,6 +963,12 @@ 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);
+
return desktop;
}
diff --git a/rootston/keyboard.c b/rootston/keyboard.c
index b5a8093b..6ba0bd6d 100644
--- a/rootston/keyboard.c
+++ b/rootston/keyboard.c
@@ -8,6 +8,7 @@
#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 +177,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);
}
diff --git a/rootston/output.c b/rootston/output.c
index 4207f0d0..e85612fa 100644
--- a/rootston/output.c
+++ b/rootston/output.c
@@ -22,7 +22,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
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..e3336fde 100644
--- a/rootston/seat.c
+++ b/rootston/seat.c
@@ -299,6 +299,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 +442,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 +581,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) {
@@ -1186,6 +1201,10 @@ 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);
+ }
}
/**
@@ -1220,6 +1239,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/types/meson.build b/types/meson.build
index 1329060e..038f7de4 100644
--- a/types/meson.build
+++ b/types/meson.build
@@ -39,6 +39,7 @@ lib_wlr_types = static_library(
'wlr_output_layout.c',
'wlr_output.c',
'wlr_pointer.c',
+ 'wlr_pointer_constraints_v1.c',
'wlr_primary_selection.c',
'wlr_region.c',
'wlr_screenshooter.c',
diff --git a/types/seat/wlr_seat.c b/types/seat/wlr_seat.c
index 2e7da0a4..1940d7d6 100644
--- a/types/seat/wlr_seat.c
+++ b/types/seat/wlr_seat.c
@@ -225,6 +225,8 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) {
seat->pointer_state.default_grab = pointer_grab;
seat->pointer_state.grab = pointer_grab;
+ wl_signal_init(&seat->pointer_state.events.focus_change);
+
// keyboard state
struct wlr_seat_keyboard_grab *keyboard_grab =
calloc(1, sizeof(struct wlr_seat_keyboard_grab));
diff --git a/types/seat/wlr_seat_pointer.c b/types/seat/wlr_seat_pointer.c
index 899fb64f..594a5b81 100644
--- a/types/seat/wlr_seat_pointer.c
+++ b/types/seat/wlr_seat_pointer.c
@@ -53,7 +53,7 @@ static void pointer_send_frame(struct wl_resource *resource) {
static const struct wl_pointer_interface pointer_impl;
-static struct wlr_seat_client *seat_client_from_pointer_resource(
+struct wlr_seat_client *wlr_seat_client_from_pointer_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, &wl_pointer_interface,
&pointer_impl));
@@ -69,7 +69,7 @@ static void pointer_set_cursor(struct wl_client *client,
struct wl_resource *surface_resource,
int32_t hotspot_x, int32_t hotspot_y) {
struct wlr_seat_client *seat_client =
- seat_client_from_pointer_resource(pointer_resource);
+ wlr_seat_client_from_pointer_resource(pointer_resource);
if (seat_client == NULL) {
return;
}
@@ -146,7 +146,7 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat,
uint32_t serial = wl_display_next_serial(wlr_seat->display);
struct wl_resource *resource;
wl_resource_for_each(resource, &focused_client->pointers) {
- if (seat_client_from_pointer_resource(resource) == NULL) {
+ if (wlr_seat_client_from_pointer_resource(resource) == NULL) {
continue;
}
@@ -160,7 +160,7 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat,
uint32_t serial = wl_display_next_serial(wlr_seat->display);
struct wl_resource *resource;
wl_resource_for_each(resource, &client->pointers) {
- if (seat_client_from_pointer_resource(resource) == NULL) {
+ if (wlr_seat_client_from_pointer_resource(resource) == NULL) {
continue;
}
@@ -183,7 +183,14 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat,
wlr_seat->pointer_state.focused_client = client;
wlr_seat->pointer_state.focused_surface = surface;
- // TODO: send focus change event
+ struct wlr_seat_pointer_focus_change_event event = {
+ .seat = wlr_seat,
+ .new_surface = surface,
+ .old_surface = focused_surface,
+ .sx = sx,
+ .sy = sy,
+ };
+ wlr_signal_emit_safe(&wlr_seat->pointer_state.events.focus_change, &event);
}
void wlr_seat_pointer_clear_focus(struct wlr_seat *wlr_seat) {
@@ -199,7 +206,7 @@ void wlr_seat_pointer_send_motion(struct wlr_seat *wlr_seat, uint32_t time,
struct wl_resource *resource;
wl_resource_for_each(resource, &client->pointers) {
- if (seat_client_from_pointer_resource(resource) == NULL) {
+ if (wlr_seat_client_from_pointer_resource(resource) == NULL) {
continue;
}
@@ -219,7 +226,7 @@ uint32_t wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, uint32_t time,
uint32_t serial = wl_display_next_serial(wlr_seat->display);
struct wl_resource *resource;
wl_resource_for_each(resource, &client->pointers) {
- if (seat_client_from_pointer_resource(resource) == NULL) {
+ if (wlr_seat_client_from_pointer_resource(resource) == NULL) {
continue;
}
@@ -239,7 +246,7 @@ void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time,
struct wl_resource *resource;
wl_resource_for_each(resource, &client->pointers) {
- if (seat_client_from_pointer_resource(resource) == NULL) {
+ if (wlr_seat_client_from_pointer_resource(resource) == NULL) {
continue;
}
@@ -349,7 +356,7 @@ void seat_client_create_pointer(struct wlr_seat_client *seat_client,
void seat_client_destroy_pointer(struct wl_resource *resource) {
struct wlr_seat_client *seat_client =
- seat_client_from_pointer_resource(resource);
+ wlr_seat_client_from_pointer_resource(resource);
if (seat_client == NULL) {
return;
}
diff --git a/types/wlr_box.c b/types/wlr_box.c
index ada6a733..44a80dc2 100644
--- a/types/wlr_box.c
+++ b/types/wlr_box.c
@@ -143,3 +143,12 @@ void wlr_box_rotated_bounds(const struct wlr_box *box, float rotation,
dest->y = floor(fmin(y1, y2));
dest->height = ceil(fmax(y1, y2) - fmin(y1, y2));
}
+
+void wlr_box_from_pixman_box32(const pixman_box32_t box, struct wlr_box *dest) {
+ *dest = (struct wlr_box){
+ .x = box.x1,
+ .y = box.y1,
+ .width = box.x2 - box.x1,
+ .height = box.y2 - box.y1,
+ };
+}
diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c
index 49f11b20..e97dd840 100644
--- a/types/wlr_cursor.c
+++ b/types/wlr_cursor.c
@@ -252,7 +252,7 @@ bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev,
return result;
}
-static void cursor_warp_closest(struct wlr_cursor *cur,
+void wlr_cursor_warp_closest(struct wlr_cursor *cur,
struct wlr_input_device *dev, double lx, double ly) {
struct wlr_box *mapping = get_mapping(cur, dev);
if (mapping) {
@@ -286,7 +286,7 @@ void wlr_cursor_warp_absolute(struct wlr_cursor *cur,
double lx, ly;
wlr_cursor_absolute_to_layout_coords(cur, dev, x, y, &lx, &ly);
- cursor_warp_closest(cur, dev, lx, ly);
+ wlr_cursor_warp_closest(cur, dev, lx, ly);
}
void wlr_cursor_move(struct wlr_cursor *cur, struct wlr_input_device *dev,
@@ -296,7 +296,7 @@ void wlr_cursor_move(struct wlr_cursor *cur, struct wlr_input_device *dev,
double lx = !isnan(delta_x) ? cur->x + delta_x : cur->x;
double ly = !isnan(delta_y) ? cur->y + delta_y : cur->y;
- cursor_warp_closest(cur, dev, lx, ly);
+ wlr_cursor_warp_closest(cur, dev, lx, ly);
}
void wlr_cursor_set_image(struct wlr_cursor *cur, const uint8_t *pixels,
diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c
new file mode 100644
index 00000000..196af92e
--- /dev/null
+++ b/types/wlr_pointer_constraints_v1.c
@@ -0,0 +1,372 @@
+#include <assert.h>
+#include <limits.h>
+#include <pixman.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_pointer_constraints_v1.h>
+#include <wlr/types/wlr_region.h>
+#include <wlr/util/log.h>
+#include "util/signal.h"
+
+static const struct zwp_locked_pointer_v1_interface locked_pointer_impl;
+static const struct zwp_confined_pointer_v1_interface confined_pointer_impl;
+static const struct zwp_pointer_constraints_v1_interface pointer_constraints_impl;
+static void pointer_constraint_destroy(struct wlr_pointer_constraint_v1 *constraint);
+
+static struct wlr_pointer_constraint_v1 *pointer_constraint_from_resource(
+ struct wl_resource *resource) {
+ assert(
+ wl_resource_instance_of(
+ resource, &zwp_confined_pointer_v1_interface,
+ &confined_pointer_impl) ||
+ wl_resource_instance_of(
+ resource, &zwp_locked_pointer_v1_interface,
+ &locked_pointer_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+static struct wlr_pointer_constraints_v1 *pointer_constraints_from_resource(
+ struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource, &zwp_pointer_constraints_v1_interface,
+ &pointer_constraints_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+static void resource_destroy(struct wl_client *client,
+ struct wl_resource *resource) {
+ wl_resource_destroy(resource);
+}
+
+static void pointer_constraint_destroy(struct wlr_pointer_constraint_v1 *constraint) {
+ if (constraint == NULL) {
+ return;
+ }
+
+ wlr_log(WLR_DEBUG, "destroying constraint %p", constraint);
+
+ wlr_signal_emit_safe(&constraint->events.destroy, constraint);
+
+ wl_resource_set_user_data(constraint->resource, NULL);
+ wl_list_remove(&constraint->link);
+ wl_list_remove(&constraint->surface_commit.link);
+ wl_list_remove(&constraint->surface_destroy.link);
+ wl_list_remove(&constraint->seat_destroy.link);
+ pixman_region32_fini(&constraint->current.region);
+ pixman_region32_fini(&constraint->pending.region);
+ pixman_region32_fini(&constraint->region);
+ free(constraint);
+}
+
+static void pointer_constraint_destroy_resource(struct wl_resource *resource) {
+ struct wlr_pointer_constraint_v1 *constraint =
+ pointer_constraint_from_resource(resource);
+
+ pointer_constraint_destroy(constraint);
+}
+
+static void pointer_constraint_set_region(
+ struct wlr_pointer_constraint_v1 *constraint,
+ struct wl_resource *region_resource) {
+ pixman_region32_clear(&constraint->pending.region);
+
+ if (region_resource) {
+ pixman_region32_t *region = wlr_region_from_resource(region_resource);
+ pixman_region32_copy(&constraint->pending.region, region);
+ }
+
+ constraint->pending.committed |= WLR_POINTER_CONSTRAINT_V1_STATE_REGION;
+}
+
+static void pointer_constraint_handle_set_region(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *region_resource) {
+ struct wlr_pointer_constraint_v1 *constraint =
+ pointer_constraint_from_resource(resource);
+ if (constraint == NULL) {
+ return;
+ }
+
+ pointer_constraint_set_region(constraint, region_resource);
+}
+
+static void pointer_constraint_set_cursor_position_hint(struct wl_client *client,
+ struct wl_resource *resource, wl_fixed_t x, wl_fixed_t y) {
+ struct wlr_pointer_constraint_v1 *constraint =
+ pointer_constraint_from_resource(resource);
+ if (constraint == NULL) {
+ return;
+ }
+
+ constraint->pending.cursor_hint.x = wl_fixed_to_double(x);
+ constraint->pending.cursor_hint.y = wl_fixed_to_double(y);
+ constraint->pending.committed |= WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT;
+}
+
+static void pointer_constraint_commit(
+ struct wlr_pointer_constraint_v1 *constraint) {
+ if (constraint->pending.committed &
+ WLR_POINTER_CONSTRAINT_V1_STATE_REGION) {
+ pixman_region32_copy(&constraint->current.region,
+ &constraint->pending.region);
+ }
+ if (constraint->pending.committed &
+ WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) {
+ constraint->current.cursor_hint = constraint->pending.cursor_hint;
+ }
+ constraint->current.committed |= constraint->pending.committed;
+
+ constraint->pending.committed = 0;
+
+ pixman_region32_clear(&constraint->region);
+ 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);
+ }
+}
+
+static void handle_surface_commit(struct wl_listener *listener, void *data) {
+ struct wlr_pointer_constraint_v1 *constraint =
+ wl_container_of(listener, constraint, surface_commit);
+
+ pointer_constraint_commit(constraint);
+}
+
+static void handle_surface_destroy(struct wl_listener *listener, void *data) {
+ struct wlr_pointer_constraint_v1 *constraint =
+ wl_container_of(listener, constraint, surface_destroy);
+
+ pointer_constraint_destroy(constraint);
+}
+
+static void handle_seat_destroy(struct wl_listener *listener, void *data) {
+ struct wlr_pointer_constraint_v1 *constraint =
+ wl_container_of(listener, constraint, seat_destroy);
+
+ pointer_constraint_destroy(constraint);
+}
+
+static const struct zwp_confined_pointer_v1_interface confined_pointer_impl = {
+ .destroy = resource_destroy,
+ .set_region = pointer_constraint_handle_set_region,
+};
+
+static const struct zwp_locked_pointer_v1_interface locked_pointer_impl = {
+ .destroy = resource_destroy,
+ .set_region = pointer_constraint_handle_set_region,
+ .set_cursor_position_hint = pointer_constraint_set_cursor_position_hint,
+};
+
+static void pointer_constraint_create(struct wl_client *client,
+ struct wl_resource *pointer_constraints_resource, uint32_t id,
+ struct wl_resource *surface_resource,
+ struct wl_resource *pointer_resource,
+ struct wl_resource *region_resource,
+ enum zwp_pointer_constraints_v1_lifetime lifetime,
+ enum wlr_pointer_constraint_v1_type type) {
+ struct wlr_pointer_constraints_v1 *pointer_constraints =
+ pointer_constraints_from_resource(pointer_constraints_resource);
+
+ struct wlr_surface *surface = wlr_surface_from_resource(surface_resource);
+ struct wlr_seat *seat =
+ wlr_seat_client_from_pointer_resource(pointer_resource)->seat;
+
+ if (wlr_pointer_constraints_v1_constraint_for_surface(pointer_constraints,
+ surface, seat)) {
+ wl_resource_post_error(pointer_constraints_resource,
+ ZWP_POINTER_CONSTRAINTS_V1_ERROR_ALREADY_CONSTRAINED,
+ "a pointer constraint with a wl_pointer of the same wl_seat"
+ " is already on this surface");
+ return;
+ }
+
+ uint32_t version = wl_resource_get_version(pointer_constraints_resource);
+
+ bool locked_pointer = type == WLR_POINTER_CONSTRAINT_V1_LOCKED;
+
+ struct wl_resource *resource = locked_pointer ?
+ wl_resource_create(client, &zwp_locked_pointer_v1_interface, version, id) :
+ wl_resource_create(client, &zwp_confined_pointer_v1_interface, version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ struct wlr_pointer_constraint_v1 *constraint = calloc(1, sizeof(*constraint));
+ if (constraint == NULL) {
+ wl_resource_destroy(resource);
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ constraint->resource = resource;
+ constraint->surface = surface;
+ constraint->seat = seat;
+ constraint->lifetime = lifetime;
+ constraint->type = type;
+ constraint->pointer_constraints = pointer_constraints;
+
+ wl_signal_init(&constraint->events.destroy);
+
+ pixman_region32_init(&constraint->region);
+
+ pixman_region32_init(&constraint->pending.region);
+ pixman_region32_init(&constraint->current.region);
+
+ pointer_constraint_set_region(constraint, region_resource);
+ pointer_constraint_commit(constraint);
+
+ constraint->surface_commit.notify = handle_surface_commit;
+ wl_signal_add(&surface->events.commit, &constraint->surface_commit);
+
+ constraint->surface_destroy.notify = handle_surface_destroy;
+ wl_signal_add(&surface->events.destroy, &constraint->surface_destroy);
+
+ constraint->seat_destroy.notify = handle_seat_destroy;
+ wl_signal_add(&seat->events.destroy, &constraint->seat_destroy);
+
+ void *impl = locked_pointer ?
+ (void *)&locked_pointer_impl : (void *)&confined_pointer_impl;
+ wl_resource_set_implementation(constraint->resource, impl, constraint,
+ pointer_constraint_destroy_resource);
+
+ wlr_log(WLR_DEBUG, "new %s_pointer %p (res %p)",
+ locked_pointer ? "locked" : "confined",
+ constraint, constraint->resource);
+
+ wl_list_insert(&pointer_constraints->constraints, &constraint->link);
+
+ wlr_signal_emit_safe(&pointer_constraints->events.new_constraint,
+ constraint);
+}
+
+static void pointer_constraints_lock_pointer(struct wl_client *client,
+ struct wl_resource *cons_resource, uint32_t id,
+ struct wl_resource *surface, struct wl_resource *pointer,
+ struct wl_resource *region, enum zwp_pointer_constraints_v1_lifetime lifetime) {
+ pointer_constraint_create(client, cons_resource, id, surface, pointer,
+ region, lifetime, WLR_POINTER_CONSTRAINT_V1_LOCKED);
+}
+
+static void pointer_constraints_confine_pointer(struct wl_client *client,
+ struct wl_resource *cons_resource, uint32_t id,
+ struct wl_resource *surface, struct wl_resource *pointer,
+ struct wl_resource *region,
+ enum zwp_pointer_constraints_v1_lifetime lifetime) {
+ pointer_constraint_create(client, cons_resource, id, surface, pointer,
+ region, lifetime, WLR_POINTER_CONSTRAINT_V1_CONFINED);
+}
+
+static const struct zwp_pointer_constraints_v1_interface
+ pointer_constraints_impl = {
+ .destroy = resource_destroy,
+ .lock_pointer = pointer_constraints_lock_pointer,
+ .confine_pointer = pointer_constraints_confine_pointer,
+};
+
+static void pointer_constraints_destroy(struct wl_resource *resource) {
+ wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void pointer_constraints_bind(struct wl_client *client, void *data,
+ uint32_t version, uint32_t id) {
+ struct wlr_pointer_constraints_v1 *pointer_constraints = data;
+ assert(client && pointer_constraints);
+
+ struct wl_resource *resource = wl_resource_create(client,
+ &zwp_pointer_constraints_v1_interface, version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_list_insert(&pointer_constraints->resources,
+ wl_resource_get_link(resource));
+ wl_resource_set_implementation(resource, &pointer_constraints_impl,
+ pointer_constraints, pointer_constraints_destroy);
+}
+
+struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1_create(
+ struct wl_display *display) {
+ struct wlr_pointer_constraints_v1 *pointer_constraints =
+ calloc(1, sizeof(*pointer_constraints));
+
+ if (!pointer_constraints) {
+ return NULL;
+ }
+
+ struct wl_global *wl_global = wl_global_create(display,
+ &zwp_pointer_constraints_v1_interface, 1, pointer_constraints,
+ pointer_constraints_bind);
+ if (!wl_global) {
+ free(pointer_constraints);
+ return NULL;
+ }
+ pointer_constraints->global = wl_global;
+
+ wl_list_init(&pointer_constraints->resources);
+ wl_list_init(&pointer_constraints->constraints);
+ wl_signal_init(&pointer_constraints->events.new_constraint);
+
+ return pointer_constraints;
+}
+
+void wlr_pointer_constraints_v1_destroy(
+ struct wlr_pointer_constraints_v1 *pointer_constraints) {
+ struct wl_resource *resource, *_tmp_res;
+ wl_resource_for_each_safe(resource, _tmp_res,
+ &pointer_constraints->resources) {
+ wl_resource_destroy(resource);
+ }
+
+ struct wlr_pointer_constraint_v1 *constraint, *_tmp_cons;
+ wl_list_for_each_safe(constraint, _tmp_cons,
+ &pointer_constraints->constraints, link) {
+ wl_resource_destroy(constraint->resource);
+ }
+
+ wl_global_destroy(pointer_constraints->global);
+ free(pointer_constraints);
+}
+
+struct wlr_pointer_constraint_v1 *
+ wlr_pointer_constraints_v1_constraint_for_surface(
+ struct wlr_pointer_constraints_v1 *pointer_constraints,
+ struct wlr_surface *surface, struct wlr_seat *seat) {
+ struct wlr_pointer_constraint_v1 *constraint;
+ wl_list_for_each(constraint, &pointer_constraints->constraints, link) {
+ if (constraint->surface == surface && constraint->seat == seat) {
+ return constraint;
+ }
+ }
+
+ return NULL;
+}
+
+void wlr_pointer_constraint_v1_send_activated(
+ struct wlr_pointer_constraint_v1 *constraint) {
+ wlr_log(WLR_DEBUG, "constrained %p", constraint);
+ if (constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) {
+ zwp_locked_pointer_v1_send_locked(constraint->resource);
+ } else {
+ zwp_confined_pointer_v1_send_confined(constraint->resource);
+ }
+}
+
+void wlr_pointer_constraint_v1_send_deactivated(
+ struct wlr_pointer_constraint_v1 *constraint) {
+ wlr_log(WLR_DEBUG, "unconstrained %p", constraint);
+ if (constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) {
+ zwp_locked_pointer_v1_send_unlocked(constraint->resource);
+ } else {
+ zwp_confined_pointer_v1_send_unconfined(constraint->resource);
+ }
+
+ if (constraint->lifetime ==
+ ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT) {
+ pointer_constraint_destroy(constraint);
+ }
+}
diff --git a/types/wlr_surface.c b/types/wlr_surface.c
index ab1dfc2d..408f38d1 100644
--- a/types/wlr_surface.c
+++ b/types/wlr_surface.c
@@ -113,9 +113,6 @@ static void surface_set_opaque_region(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region_resource) {
struct wlr_surface *surface = wlr_surface_from_resource(resource);
- if ((surface->pending.committed & WLR_SURFACE_STATE_OPAQUE_REGION)) {
- pixman_region32_clear(&surface->pending.opaque);
- }
surface->pending.committed |= WLR_SURFACE_STATE_OPAQUE_REGION;
if (region_resource) {
pixman_region32_t *region = wlr_region_from_resource(region_resource);
@@ -126,7 +123,8 @@ static void surface_set_opaque_region(struct wl_client *client,
}
static void surface_set_input_region(struct wl_client *client,
- struct wl_resource *resource, struct wl_resource *region_resource) {
+ struct wl_resource *resource,
+ struct wl_resource *region_resource) {
struct wlr_surface *surface = wlr_surface_from_resource(resource);
surface->pending.committed |= WLR_SURFACE_STATE_INPUT_REGION;
if (region_resource) {
@@ -353,9 +351,14 @@ static void surface_update_opaque_region(struct wlr_surface *surface) {
return;
}
- pixman_region32_copy(&surface->opaque_region, &surface->current.opaque);
pixman_region32_intersect_rect(&surface->opaque_region,
- &surface->opaque_region,
+ &surface->current.opaque,
+ 0, 0, surface->current.width, surface->current.height);
+}
+
+static void surface_update_input_region(struct wlr_surface *surface) {
+ pixman_region32_intersect_rect(&surface->input_region,
+ &surface->current.input,
0, 0, surface->current.width, surface->current.height);
}
@@ -380,6 +383,7 @@ static void surface_commit_pending(struct wlr_surface *surface) {
surface_apply_damage(surface);
}
surface_update_opaque_region(surface);
+ surface_update_input_region(surface);
// commit subsurface order
struct wlr_subsurface *subsurface;
@@ -587,6 +591,7 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) {
surface_state_finish(&surface->previous);
pixman_region32_fini(&surface->buffer_damage);
pixman_region32_fini(&surface->opaque_region);
+ pixman_region32_fini(&surface->input_region);
wlr_buffer_unref(surface->buffer);
free(surface);
}
@@ -633,6 +638,7 @@ struct wlr_surface *wlr_surface_create(struct wl_client *client,
wl_list_init(&surface->subsurface_pending_list);
pixman_region32_init(&surface->buffer_damage);
pixman_region32_init(&surface->opaque_region);
+ pixman_region32_init(&surface->input_region);
wl_signal_add(&renderer->events.destroy, &surface->renderer_destroy);
surface->renderer_destroy.notify = surface_handle_renderer_destroy;
@@ -937,7 +943,7 @@ bool wlr_surface_point_accepts_input(struct wlr_surface *surface,
double sx, double sy) {
return sx >= 0 && sx < surface->current.width &&
sy >= 0 && sy < surface->current.height &&
- pixman_region32_contains_point(&surface->current.input, sx, sy, NULL);
+ pixman_region32_contains_point(&surface->current.input, floor(sx), floor(sy), NULL);
}
struct wlr_surface *wlr_surface_surface_at(struct wlr_surface *surface,
diff --git a/util/region.c b/util/region.c
index 38f84c5e..61f9c7c7 100644
--- a/util/region.c
+++ b/util/region.c
@@ -1,5 +1,8 @@
+#include <assert.h>
#include <math.h>
+#include <limits.h>
#include <stdlib.h>
+#include <wlr/types/wlr_box.h>
#include <wlr/util/region.h>
void wlr_region_scale(pixman_region32_t *dst, pixman_region32_t *src,
@@ -177,3 +180,71 @@ void wlr_region_rotated_bounds(pixman_region32_t *dst, pixman_region32_t *src,
pixman_region32_init_rects(dst, dst_rects, nrects);
free(dst_rects);
}
+
+static void region_confine(pixman_region32_t *region, double x1, double y1, double x2,
+ double y2, double *x2_out, double *y2_out, pixman_box32_t box) {
+ double x_clamped = fmax(fmin(x2, box.x2 - 1), box.x1);
+ double y_clamped = fmax(fmin(y2, box.y2 - 1), box.y1);
+
+ // If the target coordinates are above box.{x,y}2 - 1, but less than
+ // box.{x,y}2, then they are still within the box.
+ if (floor(x_clamped) == floor(x2) && floor(y_clamped) == floor(y2)) {
+ *x2_out = x2;
+ *y2_out = y2;
+ return;
+ }
+
+ double dx = x2 - x1;
+ double dy = y2 - y1;
+
+ // We use fabs to avoid negative zeroes and thus avoid a bug
+ // with negative infinity.
+ double delta = fmin(fabs(x_clamped - x1) / fabs(dx), fabs(y_clamped - y1) / fabs(dy));
+
+ // We clamp it again due to precision errors.
+ double x = fmax(fmin(delta * dx + x1, box.x2 - 1), box.x1);
+ double y = fmax(fmin(delta * dy + y1, box.y2 - 1), box.y1);
+
+ // Go one unit past the boundary to find an adjacent box.
+ int x_ext = floor(x) + (dx == 0 ? 0 : dx > 0 ? 1 : -1);
+ int y_ext = floor(y) + (dy == 0 ? 0 : dy > 0 ? 1 : -1);
+
+ if (pixman_region32_contains_point(region, x_ext, y_ext, &box)) {
+ return region_confine(region, x1, y1, x2, y2, x2_out, y2_out, box);
+ } else if (dx == 0 || dy == 0) {
+ *x2_out = x;
+ *y2_out = y;
+ } else {
+ bool bordering_x = x == box.x1 || x == box.x2 - 1;
+ bool bordering_y = y == box.y1 || y == box.y2 - 1;
+
+ if ((bordering_x && bordering_y) || (!bordering_x && !bordering_y)) {
+ double x2_potential, y2_potential;
+ double tmp1, tmp2;
+ region_confine(region, x, y, x, y2, &tmp1, &y2_potential, box);
+ region_confine(region, x, y, x2, y, &x2_potential, &tmp2, box);
+ if (fabs(x2_potential - x) > fabs(y2_potential - y)) {
+ *x2_out = x2_potential;
+ *y2_out = y;
+ } else {
+ *x2_out = x;
+ *y2_out = y2_potential;
+ }
+ } else if (bordering_x) {
+ return region_confine(region, x, y, x, y2, x2_out, y2_out, box);
+ } else if (bordering_y) {
+ return region_confine(region, x, y, x2, y, x2_out, y2_out, box);
+ }
+ }
+}
+
+bool wlr_region_confine(pixman_region32_t *region, double x1, double y1, double x2,
+ double y2, double *x2_out, double *y2_out) {
+ pixman_box32_t box;
+ if (pixman_region32_contains_point(region, floor(x1), floor(y1), &box)) {
+ region_confine(region, x1, y1, x2, y2, x2_out, y2_out, box);
+ return true;
+ } else {
+ return false;
+ }
+}