diff options
Diffstat (limited to 'rootston/cursor.c')
| -rw-r--r-- | rootston/cursor.c | 219 | 
1 files changed, 199 insertions, 20 deletions
diff --git a/rootston/cursor.c b/rootston/cursor.c index 2b8d9a3e..9a163c63 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -1,9 +1,12 @@  #define _XOPEN_SOURCE 700 +#include <assert.h>  #include <math.h>  #include <stdlib.h> +#include <wlr/types/wlr_region.h>  #include <wlr/types/wlr_xcursor_manager.h>  #include <wlr/util/edges.h>  #include <wlr/util/log.h> +#include <wlr/util/region.h>  #ifdef __linux__  #include <linux/input-event-codes.h>  #elif __FreeBSD__ @@ -11,6 +14,7 @@  #endif  #include "rootston/cursor.h"  #include "rootston/desktop.h" +#include "rootston/view.h"  #include "rootston/xcursor.h"  struct roots_cursor *roots_cursor_create(struct roots_seat *seat) { @@ -100,7 +104,7 @@ static void seat_view_deco_button(struct roots_seat_view *view, double sx,  }  static void roots_passthrough_cursor(struct roots_cursor *cursor, -		uint32_t time) { +		int64_t time) {  	bool focus_changed;  	double sx, sy;  	struct roots_view *view = NULL; @@ -108,11 +112,13 @@ static void roots_passthrough_cursor(struct roots_cursor *cursor,  	struct roots_desktop *desktop = seat->input->server->desktop;  	struct wlr_surface *surface = desktop_surface_at(desktop,  			cursor->cursor->x, cursor->cursor->y, &sx, &sy, &view); +  	struct wl_client *client = NULL;  	if (surface) {  		client = wl_resource_get_client(surface->resource);  	} -	if (surface && !roots_seat_allow_input(cursor->seat, surface->resource)) { + +	if (surface && !roots_seat_allow_input(seat, surface->resource)) {  		return;  	} @@ -125,21 +131,27 @@ static void roots_passthrough_cursor(struct roots_cursor *cursor,  	if (view) {  		struct roots_seat_view *seat_view =  			roots_seat_view_from_view(seat, view); -		if (cursor->pointer_view && (surface || -					seat_view != cursor->pointer_view)) { + +		if (cursor->pointer_view && +				!cursor->wlr_surface && (surface || seat_view != cursor->pointer_view)) {  			seat_view_deco_leave(cursor->pointer_view); -			cursor->pointer_view = NULL;  		} + +		cursor->pointer_view = seat_view; +  		if (!surface) { -			cursor->pointer_view = seat_view;  			seat_view_deco_motion(seat_view, sx, sy);  		} +	} else { +		cursor->pointer_view = NULL;  	} +	cursor->wlr_surface = surface; +  	if (surface) {  		focus_changed = (seat->seat->pointer_state.focused_surface != surface);  		wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy); -		if (!focus_changed) { +		if (!focus_changed && time > 0) {  			wlr_seat_pointer_notify_motion(seat->seat, time, sx, sy);  		}  	} else { @@ -152,6 +164,10 @@ static void roots_passthrough_cursor(struct roots_cursor *cursor,  	}  } +void roots_cursor_update_focus(struct roots_cursor *cursor) { +	roots_passthrough_cursor(cursor, -1); +} +  void roots_cursor_update_position(struct roots_cursor *cursor,  		uint32_t time) {  	struct roots_seat *seat = cursor->seat; @@ -262,7 +278,7 @@ static void roots_cursor_press_button(struct roots_cursor *cursor,  	} else {  		if (view && !surface && cursor->pointer_view) {  			seat_view_deco_button(cursor->pointer_view, -					sx, sy, button, state); +				sx, sy, button, state);  		}  		if (state == WLR_BUTTON_RELEASED && @@ -291,15 +307,59 @@ static void roots_cursor_press_button(struct roots_cursor *cursor,  void roots_cursor_handle_motion(struct roots_cursor *cursor,  		struct wlr_event_pointer_motion *event) { -	wlr_cursor_move(cursor->cursor, event->device, -			event->delta_x, event->delta_y); +	double dx = event->delta_x; +	double dy = event->delta_y; + +	if (cursor->active_constraint) { +		struct roots_view *view = cursor->pointer_view->view; +		assert(view); + +		// TODO: handle rotated views +		if (view->rotation == 0.0) { +			double lx1 = cursor->cursor->x; +			double ly1 = cursor->cursor->y; + +			double lx2 = lx1 + dx; +			double ly2 = ly1 + dy; + +			double sx1 = lx1 - view->x; +			double sy1 = ly1 - view->y; + +			double sx2 = lx2 - view->x; +			double sy2 = ly2 - view->y; + +			double sx2_confined, sy2_confined; +			if (!wlr_region_confine(&cursor->confine, sx1, sy1, sx2, sy2, +					&sx2_confined, &sy2_confined)) { +				return; +			} + +			dx = sx2_confined - sx1; +			dy = sy2_confined - sy1; +		} +	} + +	wlr_cursor_move(cursor->cursor, event->device, dx, dy);  	roots_cursor_update_position(cursor, event->time_msec);  }  void roots_cursor_handle_motion_absolute(struct roots_cursor *cursor,  		struct wlr_event_pointer_motion_absolute *event) { -	wlr_cursor_warp_absolute(cursor->cursor, -			event->device, event->x, event->y); +	double lx, ly; +	wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, event->x, +		event->y, &lx, &ly); + +	if (cursor->pointer_view) { +		struct roots_view *view = cursor->pointer_view->view; + +		if (cursor->active_constraint && +				!pixman_region32_contains_point(&cursor->confine, +					floor(lx - view->x), floor(ly - view->y), NULL)) { +			return; +		} +	} + +	wlr_cursor_warp_closest(cursor->cursor, event->device, lx, ly);  	roots_cursor_update_position(cursor, event->time_msec);  } @@ -324,7 +384,7 @@ void roots_cursor_handle_touch_down(struct roots_cursor *cursor,  	double sx, sy;  	struct wlr_surface *surface = desktop_surface_at( -			desktop, lx, ly, &sx, &sy, NULL); +		desktop, lx, ly, &sx, &sy, NULL);  	uint32_t serial = 0;  	if (surface && roots_seat_allow_input(cursor->seat, surface->resource)) { @@ -393,18 +453,34 @@ void roots_cursor_handle_touch_motion(struct roots_cursor *cursor,  void roots_cursor_handle_tool_axis(struct roots_cursor *cursor,  		struct wlr_event_tablet_tool_axis *event) { +	double x = NAN, y = NAN;  	if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) &&  			(event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { -		wlr_cursor_warp_absolute(cursor->cursor, event->device, -			event->x, event->y); -		roots_cursor_update_position(cursor, event->time_msec); +		x = event->x; +		y = event->y;  	} else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) { -		wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, -1); -		roots_cursor_update_position(cursor, event->time_msec); +		x = event->x;  	} else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { -		wlr_cursor_warp_absolute(cursor->cursor, event->device, -1, event->y); -		roots_cursor_update_position(cursor, event->time_msec); +		y = event->y; +	} + +	double lx, ly; +	wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, +		x, y, &lx, &ly); + + +	if (cursor->pointer_view) { +		struct roots_view *view = cursor->pointer_view->view; + +		if (cursor->active_constraint && +				!pixman_region32_contains_point(&cursor->confine, +					floor(lx - view->x), floor(ly - view->y), NULL)) { +			return; +		}  	} + +	wlr_cursor_warp_closest(cursor->cursor, event->device, lx, ly); +	roots_cursor_update_position(cursor, event->time_msec);  }  void roots_cursor_handle_tool_tip(struct roots_cursor *cursor, @@ -434,3 +510,106 @@ void roots_cursor_handle_request_set_cursor(struct roots_cursor *cursor,  		event->hotspot_y);  	cursor->cursor_client = event->seat_client->client;  } + +void roots_cursor_handle_focus_change(struct roots_cursor *cursor, +		struct wlr_seat_pointer_focus_change_event *event) { +	double sx = event->sx; +	double sy = event->sy; + +	double lx = cursor->cursor->x; +	double ly = cursor->cursor->y; + +	wlr_log(WLR_DEBUG, "entered surface %p, lx: %f, ly: %f, sx: %f, sy: %f", +		event->new_surface, lx, ly, sx, sy); + +	roots_cursor_constrain(cursor, +		wlr_pointer_constraints_v1_constraint_for_surface( +			cursor->seat->input->server->desktop->pointer_constraints, +			event->new_surface, cursor->seat->seat), +		sx, sy); +} + +void roots_cursor_handle_constraint_commit(struct roots_cursor *cursor) { +	struct roots_desktop *desktop = cursor->seat->input->server->desktop; + +	struct roots_view *view; +	double sx, sy; +	struct wlr_surface *surface = desktop_surface_at(desktop, +			cursor->cursor->x, cursor->cursor->y, &sx, &sy, &view); +	// This should never happen but views move around right when they're +	// created from (0, 0) to their actual coordinates. +	if (surface != cursor->active_constraint->surface) { +		roots_cursor_update_focus(cursor); +	} else { +		roots_cursor_constrain(cursor, cursor->active_constraint, sx, sy); +	} +} + +static void handle_constraint_commit(struct wl_listener *listener, +		void *data) { +	struct roots_cursor *cursor = +		wl_container_of(listener, cursor, constraint_commit); +	assert(cursor->active_constraint->surface == data); +	roots_cursor_handle_constraint_commit(cursor); +} + +void roots_cursor_constrain(struct roots_cursor *cursor, +		struct wlr_pointer_constraint_v1 *constraint, double sx, double sy) { +	if (cursor->active_constraint == constraint) { +		return; +	} + +	wlr_log(WLR_DEBUG, "roots_cursor_constrain(%p, %p)", +		cursor, constraint); +	wlr_log(WLR_DEBUG, "cursor->active_constraint: %p", +		cursor->active_constraint); + +	wl_list_remove(&cursor->constraint_commit.link); +	wl_list_init(&cursor->constraint_commit.link); +	if (cursor->active_constraint) { +		wlr_pointer_constraint_v1_send_deactivated( +			cursor->active_constraint); +	} + +	cursor->active_constraint = constraint; + +	if (constraint == NULL) { +		return; +	} + +	wlr_pointer_constraint_v1_send_activated(constraint); + +	wl_list_remove(&cursor->constraint_commit.link); +	wl_signal_add(&constraint->surface->events.commit, +		&cursor->constraint_commit); +	cursor->constraint_commit.notify = handle_constraint_commit; + +	pixman_region32_clear(&cursor->confine); + +	pixman_region32_t *region = &constraint->region; + +	if (!pixman_region32_contains_point(region, floor(sx), floor(sy), NULL)) { +		// Warp into region if possible +		int nboxes; +		pixman_box32_t *boxes = pixman_region32_rectangles(region, &nboxes); +		if (nboxes > 0) { +			struct roots_view *view = cursor->pointer_view->view; + +			double sx = (boxes[0].x1 + boxes[0].x2) / 2.; +			double sy = (boxes[0].y1 + boxes[0].y2) / 2.; + +			rotate_child_position(&sx, &sy, 0, 0, view->width, view->height, +				view->rotation); + +			double lx = view->x + sx; +			double ly = view->y + sy; + +			wlr_cursor_warp_closest(cursor->cursor, NULL, lx, ly); +		} +	} + +	// A locked pointer will result in an empty region, thus disallowing all movement +	if (constraint->type == WLR_POINTER_CONSTRAINT_V1_CONFINED) { +		pixman_region32_copy(&cursor->confine, region); +	} +}  | 
