mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2024-11-25 08:32:30 +00:00
Implement pointer-constraints protocol in wlroots and rootston
This commit is contained in:
parent
437f538772
commit
fa2e6e7d9d
@ -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': [
|
||||
|
254
examples/pointer-constraints.c
Normal file
254
examples/pointer-constraints.c
Normal file
@ -0,0 +1,254 @@
|
||||
#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, ®istry_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");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
@ -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;
|
||||
@ -45,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);
|
||||
@ -81,9 +89,17 @@ void roots_cursor_handle_tool_tip(struct roots_cursor *cursor,
|
||||
void roots_cursor_handle_request_set_cursor(struct roots_cursor *cursor,
|
||||
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);
|
||||
|
||||
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
|
||||
|
@ -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,8 @@ struct roots_desktop {
|
||||
struct wl_listener input_inhibit_activate;
|
||||
struct wl_listener input_inhibit_deactivate;
|
||||
struct wl_listener virtual_keyboard_new;
|
||||
struct wl_listener constraint_create;
|
||||
struct wl_listener constraint_destroy;
|
||||
|
||||
#ifdef WLR_HAS_XWAYLAND
|
||||
struct wlr_xwayland *xwayland;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
88
include/wlr/types/wlr_pointer_constraints_v1.h
Normal file
88
include/wlr/types/wlr_pointer_constraints_v1.h
Normal file
@ -0,0 +1,88 @@
|
||||
#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,
|
||||
};
|
||||
|
||||
struct wlr_pointer_constraint_v1_state {
|
||||
pixman_region32_t *region;
|
||||
|
||||
// only valid for locked_pointer
|
||||
struct {
|
||||
double x, y;
|
||||
bool valid;
|
||||
} 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
|
||||
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct wlr_pointer_constraints_v1 {
|
||||
struct wl_list wl_resources; // wl_resource_get_link
|
||||
struct wl_global *wl_global;
|
||||
|
||||
struct {
|
||||
/**
|
||||
* Called when a new pointer constraint is created.
|
||||
*
|
||||
* data: wlr_pointer_constraint_v1*
|
||||
*/
|
||||
struct wl_signal constraint_create;
|
||||
/**
|
||||
* Called when a pointer constraint is destroyed.
|
||||
*
|
||||
* data: wlr_pointer_constraint_v1*
|
||||
*/
|
||||
struct wl_signal constraint_destroy;
|
||||
} 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 *wlr_pointer_constraints_v1);
|
||||
|
||||
struct wlr_pointer_constraint_v1 *wlr_pointer_constraints_v1_constraint_for_surface(
|
||||
struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1,
|
||||
struct wlr_surface *surface, struct wlr_seat *seat);
|
||||
|
||||
void wlr_pointer_constraint_v1_send_activated(
|
||||
struct wlr_pointer_constraint_v1 *constraint);
|
||||
void wlr_pointer_constraint_v1_send_deactivated(
|
||||
struct wlr_pointer_constraint_v1 *constraint);
|
||||
|
||||
|
||||
#endif
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
@ -303,15 +307,86 @@ 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);
|
||||
|
||||
double center_x = view->x + view->width / 2.;
|
||||
double center_y = view->y + view->height / 2.;
|
||||
|
||||
double lx1 = cursor->cursor->x;
|
||||
double ly1 = cursor->cursor->y;
|
||||
|
||||
double lx2 = lx1 + dx;
|
||||
double ly2 = ly1 + dy;
|
||||
|
||||
// Optimization for most common case.
|
||||
// This also makes sure that we don't encounter
|
||||
// precision bugs in the most common case.
|
||||
if (view->rotation == 0.0) {
|
||||
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;
|
||||
} else {
|
||||
assert(false);
|
||||
double c = cos(view->rotation);
|
||||
double s = sin(view->rotation);
|
||||
|
||||
double sx1 = c * (lx1 - center_x) - s * (ly1 - center_y) + view->width / 2.;
|
||||
double sy1 = s * (lx1 - center_x) + c * (ly1 - center_y) + view->height / 2.;
|
||||
|
||||
double sx2 = c * (lx2 - center_x) - s * (ly2 - center_y) + view->width / 2.;
|
||||
double sy2 = s * (lx2 - center_x) + c * (ly2 - center_y) + view->height / 2.;
|
||||
|
||||
double sx2_confined, sy2_confined;
|
||||
if (!wlr_region_confine(&cursor->confine, sx1, sy1, sx2, sy2, &sx2_confined, &sy2_confined)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// avoid NaNs
|
||||
double fraction = (sx2 - sx1) > (sy2 - sy1) ?
|
||||
(sx2_confined - sx1) / (sx2 - sx1) :
|
||||
(sy2_confined - sy1) / (sy2 - sy1);
|
||||
|
||||
dx *= fraction;
|
||||
dy *= fraction;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
lx - view->x, ly - view->y, NULL)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
wlr_cursor_warp_closest(cursor->cursor, event->device, lx, ly);
|
||||
roots_cursor_update_position(cursor, event->time_msec);
|
||||
}
|
||||
|
||||
@ -405,18 +480,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,
|
||||
lx - view->x, 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,
|
||||
@ -446,3 +537,102 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
void roots_cursor_constrain(struct roots_cursor *cursor,
|
||||
struct wlr_pointer_constraint_v1 *constraint, double sx, double sy) {
|
||||
if (cursor->active_constraint != constraint) {
|
||||
wlr_log(WLR_DEBUG, "roots_cursor_constrain(%p, %p)", cursor, constraint);
|
||||
wlr_log(WLR_DEBUG, "cursor->active_constraint: %p", cursor->active_constraint);
|
||||
|
||||
if (cursor->active_constraint) {
|
||||
wlr_pointer_constraint_v1_send_deactivated(cursor->active_constraint);
|
||||
if (cursor->constraint_commit.link.next) {
|
||||
wl_list_remove(&cursor->constraint_commit.link);
|
||||
}
|
||||
}
|
||||
|
||||
cursor->active_constraint = constraint;
|
||||
|
||||
if (!constraint) {
|
||||
return;
|
||||
}
|
||||
|
||||
wlr_pointer_constraint_v1_send_activated(constraint);
|
||||
wl_signal_add(&constraint->surface->events.commit,
|
||||
&cursor->constraint_commit);
|
||||
} else if (constraint == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
pixman_region32_clear(&cursor->confine);
|
||||
|
||||
pixman_region32_t *region = &constraint->region;
|
||||
|
||||
if (!pixman_region32_contains_point(region, sx, 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.;
|
||||
|
||||
double lx, ly;
|
||||
if (view->rotation == 0.0) {
|
||||
lx = sx + view->x;
|
||||
ly = sy + view->y;
|
||||
} else {
|
||||
double c = cos(view->rotation);
|
||||
double s = sin(view->rotation);
|
||||
|
||||
double center_x = view->width / 2.;
|
||||
double center_y = view->height / 2.;
|
||||
|
||||
lx = c * (sx - center_x) - s * (sy - center_y) + center_x + view->x;
|
||||
ly = s * (sx - center_x) + c * (sy - center_y) + center_y + view->y;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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,53 @@ static void input_inhibit_deactivate(struct wl_listener *listener, void *data) {
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_constraint_create(
|
||||
struct wl_listener *listener,
|
||||
struct wlr_pointer_constraint_v1 *constraint) {
|
||||
struct roots_seat* seat = constraint->seat->data;
|
||||
|
||||
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 == constraint->surface) {
|
||||
assert(!seat->cursor->active_constraint);
|
||||
roots_cursor_constrain(seat->cursor, constraint, sx, sy);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_constraint_destroy(
|
||||
struct wl_listener *listener,
|
||||
struct wlr_pointer_constraint_v1 *constraint) {
|
||||
struct roots_seat* seat = constraint->seat->data;
|
||||
if (seat->cursor->active_constraint == constraint) {
|
||||
roots_cursor_constrain(seat->cursor, NULL, NAN, NAN);
|
||||
if (constraint->current.cursor_hint.valid && seat->cursor->pointer_view) {
|
||||
double sx = constraint->current.cursor_hint.x;
|
||||
double sy = constraint->current.cursor_hint.y;
|
||||
|
||||
struct roots_view *view = seat->cursor->pointer_view->view;
|
||||
|
||||
double lx, ly;
|
||||
if (view->rotation == 0.0) {
|
||||
lx = sx + view->x;
|
||||
ly = sy + view->y;
|
||||
} else {
|
||||
double c = cos(view->rotation);
|
||||
double s = sin(view->rotation);
|
||||
|
||||
double center_x = view->width / 2.;
|
||||
double center_y = view->height / 2.;
|
||||
|
||||
lx = c * (sx - center_x) - s * (sy - center_y) + center_x + view->x;
|
||||
ly = s * (sx - center_x) + c * (sy - center_y) + center_y + view->y;
|
||||
}
|
||||
|
||||
wlr_cursor_warp(seat->cursor->cursor, NULL, lx, ly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct roots_desktop *desktop_create(struct roots_server *server,
|
||||
struct roots_config *config) {
|
||||
wlr_log(WLR_DEBUG, "Initializing roots desktop");
|
||||
@ -906,6 +954,14 @@ 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->constraint_destroy.notify = (wl_notify_func_t)handle_constraint_destroy;
|
||||
wl_signal_add(&desktop->pointer_constraints->events.constraint_destroy,
|
||||
&desktop->constraint_destroy);
|
||||
desktop->constraint_create.notify = (wl_notify_func_t)handle_constraint_create;
|
||||
wl_signal_add(&desktop->pointer_constraints->events.constraint_create,
|
||||
&desktop->constraint_create);
|
||||
|
||||
return desktop;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -299,6 +299,22 @@ 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 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);
|
||||
}
|
||||
|
||||
static void seat_reset_device_mappings(struct roots_seat *seat,
|
||||
struct wlr_input_device *device) {
|
||||
struct wlr_cursor *cursor = seat->cursor->cursor;
|
||||
@ -434,6 +450,13 @@ 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);
|
||||
seat->cursor->constraint_commit.notify = handle_constraint_commit;
|
||||
}
|
||||
|
||||
static void roots_drag_icon_handle_surface_commit(struct wl_listener *listener,
|
||||
@ -1188,8 +1211,10 @@ void roots_seat_set_focus(struct roots_seat *seat, struct roots_view *view) {
|
||||
NULL, 0, NULL);
|
||||
}
|
||||
|
||||
if (seat->cursor) {
|
||||
roots_cursor_update_focus(seat->cursor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Focus semantics of layer surfaces are somewhat detached from the normal focus
|
||||
@ -1224,8 +1249,11 @@ void roots_seat_set_focus_layer(struct roots_seat *seat,
|
||||
NULL, 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
if (seat->cursor) {
|
||||
roots_cursor_update_focus(seat->cursor);
|
||||
}
|
||||
}
|
||||
|
||||
void roots_seat_set_exclusive_client(struct roots_seat *seat,
|
||||
struct wl_client *client) {
|
||||
|
@ -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',
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
@ -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,
|
||||
|
362
types/wlr_pointer_constraints_v1.c
Normal file
362
types/wlr_pointer_constraints_v1.c
Normal file
@ -0,0 +1,362 @@
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <pixman.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);
|
||||
|
||||
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);
|
||||
|
||||
wlr_signal_emit_safe(
|
||||
&constraint->pointer_constraints->events.constraint_destroy,
|
||||
constraint);
|
||||
|
||||
if (constraint->current.region) {
|
||||
pixman_region32_fini(constraint->current.region);
|
||||
free(constraint->current.region);
|
||||
}
|
||||
if (constraint->pending.region &&
|
||||
constraint->current.region != constraint->pending.region) {
|
||||
pixman_region32_fini(constraint->pending.region);
|
||||
free(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) {
|
||||
if (constraint->pending.region &&
|
||||
constraint->current.region != constraint->pending.region) {
|
||||
pixman_region32_fini(constraint->pending.region);
|
||||
free(constraint->pending.region);
|
||||
}
|
||||
|
||||
if (region_resource) {
|
||||
constraint->pending.region = calloc(1, sizeof(pixman_region32_t));
|
||||
pixman_region32_init(constraint->pending.region);
|
||||
|
||||
pixman_region32_t *region = wlr_region_from_resource(region_resource);
|
||||
pixman_region32_copy(constraint->pending.region, region);
|
||||
} else {
|
||||
constraint->pending.region = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void pointer_constraint_set_region_wrapper(struct wl_client *client,
|
||||
struct wl_resource *resource, struct wl_resource *region_resource) {
|
||||
struct wlr_pointer_constraint_v1 *constraint =
|
||||
pointer_constraint_from_resource(resource);
|
||||
|
||||
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);
|
||||
|
||||
constraint->pending.cursor_hint.x = wl_fixed_to_double(x);
|
||||
constraint->pending.cursor_hint.y = wl_fixed_to_double(y);
|
||||
constraint->pending.cursor_hint.valid = true;
|
||||
}
|
||||
|
||||
static void pointer_constraint_handle_commit(
|
||||
struct wlr_pointer_constraint_v1 *constraint) {
|
||||
constraint->current.cursor_hint = constraint->pending.cursor_hint;
|
||||
|
||||
if (constraint->current.region != constraint->pending.region) {
|
||||
if (constraint->current.region) {
|
||||
pixman_region32_fini(constraint->current.region);
|
||||
free(constraint->current.region);
|
||||
}
|
||||
|
||||
constraint->current.region = constraint->pending.region;
|
||||
}
|
||||
|
||||
pixman_region32_clear(&constraint->region);
|
||||
|
||||
if (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_handle_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_set_region_wrapper,
|
||||
};
|
||||
|
||||
static const struct zwp_locked_pointer_v1_interface locked_pointer_impl = {
|
||||
.destroy = resource_destroy,
|
||||
.set_region = pointer_constraint_set_region_wrapper,
|
||||
.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_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;
|
||||
|
||||
pixman_region32_init(&constraint->region);
|
||||
|
||||
pointer_constraint_set_region(constraint, region_resource);
|
||||
pointer_constraint_handle_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);
|
||||
|
||||
wl_resource_set_implementation(constraint->resource,
|
||||
locked_pointer ? (void*)&locked_pointer_impl : (void*)&confined_pointer_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.constraint_create,
|
||||
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->wl_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->wl_global = wl_global;
|
||||
|
||||
wl_list_init(&pointer_constraints->wl_resources);
|
||||
wl_list_init(&pointer_constraints->constraints);
|
||||
wl_signal_init(&pointer_constraints->events.constraint_create);
|
||||
wl_signal_init(&pointer_constraints->events.constraint_destroy);
|
||||
|
||||
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->wl_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->wl_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;
|
||||
}
|
||||
|
||||
// Thankfully zwp_confined_pointer_v1_send_{un,}confined work
|
||||
// on both locked and confined pointer constraints.
|
||||
void wlr_pointer_constraint_v1_send_activated(
|
||||
struct wlr_pointer_constraint_v1 *constraint) {
|
||||
wlr_log(WLR_DEBUG, "constrained %p", constraint);
|
||||
zwp_confined_pointer_v1_send_confined(constraint->resource);
|
||||
}
|
||||
|
||||
void wlr_pointer_constraint_v1_send_deactivated(struct wlr_pointer_constraint_v1 *constraint) {
|
||||
if (wl_resource_get_user_data(constraint->resource)) {
|
||||
wlr_log(WLR_DEBUG, "unconstrained %p", constraint);
|
||||
zwp_confined_pointer_v1_send_unconfined(constraint->resource);
|
||||
if (constraint->lifetime == ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT) {
|
||||
pointer_constraint_destroy(constraint);
|
||||
}
|
||||
}
|
||||
}
|
@ -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, x1, y1, &box)) {
|
||||
region_confine(region, x1, y1, x2, y2, x2_out, y2_out, box);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user