Merge pull request #852 from Laaas/master
Implement pointer-constraints-unstable-v1 protocol
This commit is contained in:
		
						commit
						5e9959daaa
					
				|  | @ -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': [ | ||||
|  |  | |||
|  | @ -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, ®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"); | ||||
| 	return 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; | ||||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -81,6 +81,11 @@ struct wlr_surface { | |||
| 	 * format, this is set to the whole 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 | ||||
|  |  | |||
|  | @ -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) { | ||||
|  | @ -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); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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); | ||||
| 	} | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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,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, | ||||
|  |  | |||
|  | @ -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', | ||||
|  |  | |||
|  | @ -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)); | ||||
|  |  | |||
|  | @ -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; | ||||
| 	} | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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); | ||||
| 	} | ||||
| } | ||||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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; | ||||
| 	} | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue