diff options
Diffstat (limited to 'sway/input')
| -rw-r--r-- | sway/input/cursor.c | 242 | ||||
| -rw-r--r-- | sway/input/seat.c | 29 | ||||
| -rw-r--r-- | sway/input/tablet.c | 345 | 
3 files changed, 570 insertions, 46 deletions
| diff --git a/sway/input/cursor.c b/sway/input/cursor.c index c6a332b8..574186d7 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -10,6 +10,7 @@  #include <wlr/types/wlr_box.h>  #include <wlr/types/wlr_cursor.h>  #include <wlr/types/wlr_idle.h> +#include <wlr/types/wlr_tablet_v2.h>  #include <wlr/types/wlr_xcursor_manager.h>  #include <wlr/util/region.h>  #include "list.h" @@ -20,6 +21,7 @@  #include "sway/desktop/transaction.h"  #include "sway/input/cursor.h"  #include "sway/input/keyboard.h" +#include "sway/input/tablet.h"  #include "sway/layers.h"  #include "sway/output.h"  #include "sway/tree/arrange.h" @@ -443,72 +445,224 @@ static void apply_mapping_from_region(struct wlr_input_device *device,  	*y = apply_mapping_from_coord(y1, y2, *y);  } +static void handle_tablet_tool_position(struct sway_cursor *cursor, +		struct sway_tablet *tablet, +		struct wlr_tablet_tool *tool, +		bool change_x, bool change_y, +		double x, double y, double dx, double dy, +		int32_t time_msec) { +	if (!change_x && !change_y) { +		return; +	} + +	struct sway_input_device *input_device = tablet->seat_device->input_device; +	struct input_config *ic = input_device_get_config(input_device); +	if (ic != NULL && ic->mapped_from_region != NULL) { +		apply_mapping_from_region(input_device->wlr_device, +			ic->mapped_from_region, &x, &y); +	} + +	switch (tool->type) { +	case WLR_TABLET_TOOL_TYPE_MOUSE: +		wlr_cursor_move(cursor->cursor, input_device->wlr_device, dx, dy); +		break; +	default: +		wlr_cursor_warp_absolute(cursor->cursor, input_device->wlr_device, +			change_x ? x : NAN, change_y ? y : NAN); +	} + +	double sx, sy; +	struct wlr_surface *surface = NULL; +	struct sway_seat *seat = cursor->seat; +	node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, +		&surface, &sx, &sy); +	struct sway_tablet_tool *sway_tool = tool->data; + +	if (!surface || !wlr_surface_accepts_tablet_v2(tablet->tablet_v2, surface)) { +		wlr_tablet_v2_tablet_tool_notify_proximity_out(sway_tool->tablet_v2_tool); +		cursor_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy); +		return; +	} + +	wlr_tablet_v2_tablet_tool_notify_proximity_in(sway_tool->tablet_v2_tool, +		tablet->tablet_v2, surface); + +	wlr_tablet_v2_tablet_tool_notify_motion(sway_tool->tablet_v2_tool, sx, sy); +} +  static void handle_tool_axis(struct wl_listener *listener, void *data) {  	struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis);  	wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);  	struct wlr_event_tablet_tool_axis *event = data; -	struct sway_input_device *input_device = event->device->data; +	struct sway_tablet_tool *sway_tool = event->tool->data; -	double x = NAN, y = NAN; -	if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) { -		x = event->x; +	if (!sway_tool) { +		sway_log(SWAY_DEBUG, "tool axis before proximity"); +		return;  	} -	if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { -		y = event->y; + +	handle_tablet_tool_position(cursor, sway_tool->tablet, event->tool, +		event->updated_axes & WLR_TABLET_TOOL_AXIS_X, +		event->updated_axes & WLR_TABLET_TOOL_AXIS_Y, +		event->x, event->y, event->dx, event->dy, event->time_msec); + +	if (event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE) { +		wlr_tablet_v2_tablet_tool_notify_pressure( +			sway_tool->tablet_v2_tool, event->pressure);  	} -	struct input_config *ic = input_device_get_config(input_device); -	if (ic != NULL && ic->mapped_from_region != NULL) { -		apply_mapping_from_region(event->device, ic->mapped_from_region, &x, &y); +	if (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE) { +		wlr_tablet_v2_tablet_tool_notify_distance( +			sway_tool->tablet_v2_tool, event->distance);  	} -	double lx, ly; -	wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, -			x, y, &lx, &ly); +	if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X) { +		sway_tool->tilt_x = event->tilt_x; +	} -	double dx = lx - cursor->cursor->x; -	double dy = ly - cursor->cursor->y; +	if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y) { +		sway_tool->tilt_y = event->tilt_y; +	} -	cursor_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); -	wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); -	transaction_commit_dirty(); +	if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y)) { +		wlr_tablet_v2_tablet_tool_notify_tilt( +			sway_tool->tablet_v2_tool, +			sway_tool->tilt_x, sway_tool->tilt_y); +	} + +	if (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION) { +		wlr_tablet_v2_tablet_tool_notify_rotation( +			sway_tool->tablet_v2_tool, event->rotation); +	} + +	if (event->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER) { +		wlr_tablet_v2_tablet_tool_notify_slider( +			sway_tool->tablet_v2_tool, event->slider); +	} + +	if (event->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL) { +		wlr_tablet_v2_tablet_tool_notify_wheel( +			sway_tool->tablet_v2_tool, event->wheel_delta, 0); +	}  }  static void handle_tool_tip(struct wl_listener *listener, void *data) {  	struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip);  	wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);  	struct wlr_event_tablet_tool_tip *event = data; -	dispatch_cursor_button(cursor, event->device, event->time_msec, -			BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ? -				WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED); -	wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); -	transaction_commit_dirty(); +	struct sway_tablet_tool *sway_tool = event->tool->data; +	struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; +	struct sway_seat *seat = cursor->seat; + +	double sx, sy; +	struct wlr_surface *surface = NULL; +	node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, +		&surface, &sx, &sy); + +	if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { +		dispatch_cursor_button(cursor, event->device, event->time_msec, +				BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ? +					WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED); +		wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); +		transaction_commit_dirty(); +		return; +	} + +	if (event->state == WLR_TABLET_TOOL_TIP_DOWN) { +		wlr_tablet_v2_tablet_tool_notify_down(sway_tool->tablet_v2_tool); +		wlr_tablet_tool_v2_start_implicit_grab(sway_tool->tablet_v2_tool); +	} else { +		wlr_tablet_v2_tablet_tool_notify_up(sway_tool->tablet_v2_tool); +	} +} + +static struct sway_tablet *get_tablet_for_device(struct sway_cursor *cursor, +		struct wlr_input_device *device) { +	struct sway_tablet *tablet; +	wl_list_for_each(tablet, &cursor->tablets, link) { +		if (tablet->seat_device->input_device->wlr_device == device) { +			return tablet; +		} +	} +	return NULL; +} + +static void handle_tool_proximity(struct wl_listener *listener, void *data) { +	struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_proximity); +	wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); +	struct wlr_event_tablet_tool_proximity *event = data; + +	struct wlr_tablet_tool *tool = event->tool; +	if (!tool->data) { +		struct sway_tablet *tablet = get_tablet_for_device(cursor, event->device); +		if (!tablet) { +			sway_log(SWAY_ERROR, "no tablet for tablet tool"); +			return; +		} +		sway_tablet_tool_configure(tablet, tool); +	} + +	struct sway_tablet_tool *sway_tool = tool->data; +	if (!sway_tool) { +		sway_log(SWAY_ERROR, "tablet tool not initialized"); +		return; +	} + +	if (event->state == WLR_TABLET_TOOL_PROXIMITY_OUT) { +		wlr_tablet_v2_tablet_tool_notify_proximity_out(sway_tool->tablet_v2_tool); +		return; +	} + +	handle_tablet_tool_position(cursor, sway_tool->tablet, event->tool, +		true, true, event->x, event->y, 0, 0, event->time_msec);  }  static void handle_tool_button(struct wl_listener *listener, void *data) {  	struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button);  	wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);  	struct wlr_event_tablet_tool_button *event = data; -	// TODO: the user may want to configure which tool buttons are mapped to -	// which simulated pointer buttons -	switch (event->state) { -	case WLR_BUTTON_PRESSED: -		if (cursor->tool_buttons == 0) { -			dispatch_cursor_button(cursor, event->device, -					event->time_msec, BTN_RIGHT, event->state); -		} -		cursor->tool_buttons++; -		break; -	case WLR_BUTTON_RELEASED: -		if (cursor->tool_buttons == 1) { -			dispatch_cursor_button(cursor, event->device, -					event->time_msec, BTN_RIGHT, event->state); +	struct sway_tablet_tool *sway_tool = event->tool->data; +	struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; +	struct sway_seat *seat = cursor->seat; + +	if (!sway_tool) { +		sway_log(SWAY_DEBUG, "tool button before proximity"); +		return; +	} + +	double sx, sy; +	struct wlr_surface *surface = NULL; + +	node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, +		&surface, &sx, &sy); + +	if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { +		// TODO: the user may want to configure which tool buttons are mapped to +		// which simulated pointer buttons +		switch (event->state) { +		case WLR_BUTTON_PRESSED: +			if (cursor->tool_buttons == 0) { +				dispatch_cursor_button(cursor, event->device, +						event->time_msec, BTN_RIGHT, event->state); +			} +			cursor->tool_buttons++; +			break; +		case WLR_BUTTON_RELEASED: +			if (cursor->tool_buttons == 1) { +				dispatch_cursor_button(cursor, event->device, +						event->time_msec, BTN_RIGHT, event->state); +			} +			cursor->tool_buttons--; +			break;  		} -		cursor->tool_buttons--; -		break; +		wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); +		transaction_commit_dirty(); +		return;  	} -	wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); -	transaction_commit_dirty(); + +	wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, +		(enum zwp_tablet_pad_v2_button_state)event->button, +		(enum zwp_tablet_pad_v2_button_state)event->state);  }  static void check_constraint_region(struct sway_cursor *cursor) { @@ -698,9 +852,6 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {  		&cursor->touch_motion);  	cursor->touch_motion.notify = handle_touch_motion; -	// TODO: tablet protocol support -	// Note: We should emulate pointer events for clients that don't support the -	// tablet protocol when the time comes  	wl_signal_add(&wlr_cursor->events.tablet_tool_axis,  		&cursor->tool_axis);  	cursor->tool_axis.notify = handle_tool_axis; @@ -708,6 +859,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {  	wl_signal_add(&wlr_cursor->events.tablet_tool_tip, &cursor->tool_tip);  	cursor->tool_tip.notify = handle_tool_tip; +	wl_signal_add(&wlr_cursor->events.tablet_tool_proximity, &cursor->tool_proximity); +	cursor->tool_proximity.notify = handle_tool_proximity; +  	wl_signal_add(&wlr_cursor->events.tablet_tool_button, &cursor->tool_button);  	cursor->tool_button.notify = handle_tool_button; @@ -716,6 +870,8 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {  	cursor->request_set_cursor.notify = handle_request_set_cursor;  	wl_list_init(&cursor->constraint_commit.link); +	wl_list_init(&cursor->tablets); +	wl_list_init(&cursor->tablet_pads);  	cursor->cursor = wlr_cursor; diff --git a/sway/input/seat.c b/sway/input/seat.c index b2243fe3..ebd40343 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -8,6 +8,7 @@  #include <wlr/types/wlr_data_device.h>  #include <wlr/types/wlr_output_layout.h>  #include <wlr/types/wlr_primary_selection.h> +#include <wlr/types/wlr_tablet_v2.h>  #include <wlr/types/wlr_xcursor_manager.h>  #include "config.h"  #include "list.h" @@ -18,6 +19,7 @@  #include "sway/input/keyboard.h"  #include "sway/input/seat.h"  #include "sway/input/switch.h" +#include "sway/input/tablet.h"  #include "sway/ipc-server.h"  #include "sway/layers.h"  #include "sway/output.h" @@ -34,6 +36,8 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) {  	}  	sway_keyboard_destroy(seat_device->keyboard); +	sway_tablet_destroy(seat_device->tablet); +	sway_tablet_pad_destroy(seat_device->tablet_pad);  	wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor,  		seat_device->input_device->wlr_device);  	wl_list_remove(&seat_device->link); @@ -118,6 +122,14 @@ static void seat_keyboard_notify_enter(struct sway_seat *seat,  			state->pressed_keycodes, state->npressed, &keyboard->modifiers);  } +static void seat_tablet_pads_notify_enter(struct sway_seat *seat, +		struct wlr_surface *surface) { +	struct sway_seat_device *seat_device; +	wl_list_for_each(seat_device, &seat->devices, link) { +		sway_tablet_pad_notify_enter(seat_device->tablet_pad, surface); +	} +} +  /**   * If con is a view, set it as active and enable keyboard input.   * If con is a container, set all child views as active and don't enable @@ -138,6 +150,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {  #endif  		seat_keyboard_notify_enter(seat, view->surface); +		seat_tablet_pads_notify_enter(seat, view->surface);  		struct wlr_pointer_constraint_v1 *constraint =  			wlr_pointer_constraints_v1_constraint_for_surface( @@ -638,14 +651,23 @@ static void seat_configure_touch(struct sway_seat *seat,  static void seat_configure_tablet_tool(struct sway_seat *seat,  		struct sway_seat_device *sway_device) { -	if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { -		seat_configure_xcursor(seat); +	if (!sway_device->tablet) { +		sway_device->tablet = sway_tablet_create(seat, sway_device);  	} +	sway_configure_tablet(sway_device->tablet);  	wlr_cursor_attach_input_device(seat->cursor->cursor,  		sway_device->input_device->wlr_device);  	seat_apply_input_config(seat, sway_device);  } +static void seat_configure_tablet_pad(struct sway_seat *seat, +		struct sway_seat_device *sway_device) { +	if (!sway_device->tablet) { +		sway_device->tablet_pad = sway_tablet_pad_create(seat, sway_device); +	} +	sway_configure_tablet_pad(sway_device->tablet_pad); +} +  static struct sway_seat_device *seat_get_device(struct sway_seat *seat,  		struct sway_input_device *input_device) {  	struct sway_seat_device *seat_device = NULL; @@ -682,7 +704,7 @@ void seat_configure_device(struct sway_seat *seat,  			seat_configure_tablet_tool(seat, seat_device);  			break;  		case WLR_INPUT_DEVICE_TABLET_PAD: -			sway_log(SWAY_DEBUG, "TODO: configure tablet pad"); +			seat_configure_tablet_pad(seat, seat_device);  			break;  	}  } @@ -1079,6 +1101,7 @@ void seat_set_focus_surface(struct sway_seat *seat,  		seat->has_focus = false;  	}  	seat_keyboard_notify_enter(seat, surface); +	seat_tablet_pads_notify_enter(seat, surface);  }  void seat_set_focus_layer(struct sway_seat *seat, diff --git a/sway/input/tablet.c b/sway/input/tablet.c new file mode 100644 index 00000000..b0d4d0c6 --- /dev/null +++ b/sway/input/tablet.c @@ -0,0 +1,345 @@ +#define _POSIX_C_SOURCE 200809L +#include <stdlib.h> +#include <wlr/backend/libinput.h> +#include <wlr/types/wlr_tablet_v2.h> +#include "log.h" +#include "sway/input/cursor.h" +#include "sway/input/seat.h" +#include "sway/input/tablet.h" + +static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) { +	struct sway_tablet_pad *pad = +		wl_container_of(listener, pad, tablet_destroy); + +	pad->tablet = NULL; + +	wl_list_remove(&pad->tablet_destroy.link); +	wl_list_init(&pad->tablet_destroy.link); +} + +static void attach_tablet_pad(struct sway_tablet_pad *tablet_pad, +		struct sway_tablet *tablet) { +	sway_log(SWAY_DEBUG, "Attaching tablet pad \"%s\" to tablet tool \"%s\"", +		tablet_pad->seat_device->input_device->wlr_device->name, +		tablet->seat_device->input_device->wlr_device->name); + +	tablet_pad->tablet = tablet; + +	wl_list_remove(&tablet_pad->tablet_destroy.link); +	tablet_pad->tablet_destroy.notify = handle_pad_tablet_destroy; +	wl_signal_add(&tablet->seat_device->input_device->wlr_device->events.destroy, +		&tablet_pad->tablet_destroy); +} + +struct sway_tablet *sway_tablet_create(struct sway_seat *seat, +		struct sway_seat_device *device) { +	struct sway_tablet *tablet = +		calloc(1, sizeof(struct sway_tablet)); +	if (!sway_assert(tablet, "could not allocate sway tablet for seat")) { +		return NULL; +	} + +	wl_list_insert(&seat->cursor->tablets, &tablet->link); + +	device->tablet = tablet; +	tablet->seat_device = device; + +	return tablet; +} + +void sway_configure_tablet(struct sway_tablet *tablet) { +	struct wlr_input_device *device = +		tablet->seat_device->input_device->wlr_device; +	struct sway_seat *seat = tablet->seat_device->sway_seat; + +	if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { +		seat_configure_xcursor(seat); +	} + +	tablet->tablet_v2 = +		wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device); + +	/* Search for a sibling tablet pad */ +	if (!wlr_input_device_is_libinput(device)) { +		/* We can only do this on libinput devices */ +		return; +	} + +	struct libinput_device_group *group = +		libinput_device_get_device_group(wlr_libinput_get_device_handle(device)); +	struct sway_tablet_pad *tablet_pad; +	wl_list_for_each(tablet_pad, &seat->cursor->tablet_pads, link) { +		struct wlr_input_device *pad_device = +			tablet_pad->seat_device->input_device->wlr_device; +		if (!wlr_input_device_is_libinput(pad_device)) { +			continue; +		} + +		struct libinput_device_group *pad_group = +			libinput_device_get_device_group(wlr_libinput_get_device_handle(pad_device)); + +		if (pad_group == group) { +			attach_tablet_pad(tablet_pad, tablet); +			break; +		} +	} +} + +void sway_tablet_destroy(struct sway_tablet *tablet) { +	if (!tablet) { +		return; +	} +	wl_list_remove(&tablet->link); +	free(tablet); +} + +static void handle_tablet_tool_set_cursor(struct wl_listener *listener, void *data) { +	struct sway_tablet_tool *tool = +		wl_container_of(listener, tool, set_cursor); +	struct wlr_tablet_v2_event_cursor *event = data; + +	struct sway_cursor *cursor = tool->seat->cursor; +	if (!seatop_allows_set_cursor(cursor->seat)) { +		return; +	} + +	struct wl_client *focused_client = NULL; +	struct wlr_surface *focused_surface = +		cursor->seat->wlr_seat->pointer_state.focused_surface; +	if (focused_surface != NULL) { +		focused_client = wl_resource_get_client(focused_surface->resource); +	} + +	// TODO: check cursor mode +	if (focused_client == NULL || +			event->seat_client->client != focused_client) { +		sway_log(SWAY_DEBUG, "denying request to set cursor from unfocused client"); +		return; +	} + +	cursor_set_image_surface(cursor, event->surface, event->hotspot_x, +			event->hotspot_y, focused_client); +} + +static void handle_tablet_tool_destroy(struct wl_listener *listener, void *data) { +	struct sway_tablet_tool *tool = +		wl_container_of(listener, tool, tool_destroy); + +	wl_list_remove(&tool->tool_destroy.link); +	wl_list_remove(&tool->set_cursor.link); + +	free(tool); +} + +void sway_tablet_tool_configure(struct sway_tablet *tablet, +		struct wlr_tablet_tool *wlr_tool) { +	struct sway_tablet_tool *tool = +		calloc(1, sizeof(struct sway_tablet_tool)); +	if (!sway_assert(tool, "could not allocate sway tablet tool for tablet")) { +		return; +	} + +	tool->seat = tablet->seat_device->sway_seat; +	tool->tablet = tablet; +	tool->tablet_v2_tool = +		wlr_tablet_tool_create(server.tablet_v2, +			tablet->seat_device->sway_seat->wlr_seat, wlr_tool); + +	tool->tool_destroy.notify = handle_tablet_tool_destroy; +	wl_signal_add(&wlr_tool->events.destroy, &tool->tool_destroy); + +	tool->set_cursor.notify = handle_tablet_tool_set_cursor; +	wl_signal_add(&tool->tablet_v2_tool->events.set_cursor, +		&tool->set_cursor); + +	wlr_tool->data = tool; +} + +static void handle_tablet_pad_attach(struct wl_listener *listener, +		void *data) { +	struct sway_tablet_pad *pad = wl_container_of(listener, pad, attach); +	struct wlr_tablet_tool *wlr_tool = data; +	struct sway_tablet_tool *tool = wlr_tool->data; + +	if (!tool) { +		return; +	} + +	attach_tablet_pad(pad, tool->tablet); +} + +static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { +	struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring); +	struct wlr_event_tablet_pad_ring *event = data; + +	if (!pad->current_surface) { +		return; +	} + +	wlr_tablet_v2_tablet_pad_notify_ring(pad->tablet_v2_pad, +		event->ring, event->position, +		event->source == WLR_TABLET_PAD_RING_SOURCE_FINGER, +		event->time_msec); +} + +static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { +	struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip); +	struct wlr_event_tablet_pad_strip *event = data; + +	if (!pad->current_surface) { +		return; +	} + +	wlr_tablet_v2_tablet_pad_notify_strip(pad->tablet_v2_pad, +		event->strip, event->position, +		event->source == WLR_TABLET_PAD_STRIP_SOURCE_FINGER, +		event->time_msec); +} + +static void handle_tablet_pad_button(struct wl_listener *listener, void *data) { +	struct sway_tablet_pad *pad = wl_container_of(listener, pad, button); +	struct wlr_event_tablet_pad_button *event = data; + +	if (!pad->current_surface) { +		return; +	} + +	wlr_tablet_v2_tablet_pad_notify_mode(pad->tablet_v2_pad, +		event->group, event->mode, event->time_msec); + +	wlr_tablet_v2_tablet_pad_notify_button(pad->tablet_v2_pad, +		event->button, event->time_msec, +		(enum zwp_tablet_pad_v2_button_state)event->state); +} + +struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat, +		struct sway_seat_device *device) { +	struct sway_tablet_pad *tablet_pad = +		calloc(1, sizeof(struct sway_tablet_pad)); +	if (!sway_assert(tablet_pad, "could not allocate sway tablet")) { +		return NULL; +	} + +	tablet_pad->seat_device = device; +	wl_list_init(&tablet_pad->attach.link); +	wl_list_init(&tablet_pad->button.link); +	wl_list_init(&tablet_pad->strip.link); +	wl_list_init(&tablet_pad->ring.link); +	wl_list_init(&tablet_pad->surface_destroy.link); +	wl_list_init(&tablet_pad->tablet_destroy.link); + +	wl_list_insert(&seat->cursor->tablet_pads, &tablet_pad->link); + +	return tablet_pad; +} + +void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { +	struct wlr_input_device *device = +		tablet_pad->seat_device->input_device->wlr_device; +	struct sway_seat *seat = tablet_pad->seat_device->sway_seat; + +	tablet_pad->tablet_v2_pad = +		wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, device); + +	wl_list_remove(&tablet_pad->attach.link); +	tablet_pad->attach.notify = handle_tablet_pad_attach; +	wl_signal_add(&device->tablet_pad->events.attach_tablet, +		&tablet_pad->attach); + +	wl_list_remove(&tablet_pad->button.link); +	tablet_pad->button.notify = handle_tablet_pad_button; +	wl_signal_add(&device->tablet_pad->events.button, &tablet_pad->button); + +	wl_list_remove(&tablet_pad->strip.link); +	tablet_pad->strip.notify = handle_tablet_pad_strip; +	wl_signal_add(&device->tablet_pad->events.strip, &tablet_pad->strip); + +	wl_list_remove(&tablet_pad->ring.link); +	tablet_pad->ring.notify = handle_tablet_pad_ring; +	wl_signal_add(&device->tablet_pad->events.ring, &tablet_pad->ring); + +	/* Search for a sibling tablet */ +	if (!wlr_input_device_is_libinput(device)) { +		/* We can only do this on libinput devices */ +		return; +	} + +	struct libinput_device_group *group = +		libinput_device_get_device_group(wlr_libinput_get_device_handle(device)); +	struct sway_tablet *tool; +	wl_list_for_each(tool, &seat->cursor->tablets, link) { +		struct wlr_input_device *tablet = +			tool->seat_device->input_device->wlr_device; +		if (!wlr_input_device_is_libinput(tablet)) { +			continue; +		} + +		struct libinput_device_group *tablet_group = +			libinput_device_get_device_group(wlr_libinput_get_device_handle(tablet)); + +		if (tablet_group == group) { +			attach_tablet_pad(tablet_pad, tool); +			break; +		} +	} +} + +void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { +	if (!tablet_pad) { +		return; +	} + +	wl_list_remove(&tablet_pad->link); +	wl_list_remove(&tablet_pad->attach.link); +	wl_list_remove(&tablet_pad->button.link); +	wl_list_remove(&tablet_pad->strip.link); +	wl_list_remove(&tablet_pad->ring.link); +	wl_list_remove(&tablet_pad->surface_destroy.link); +	wl_list_remove(&tablet_pad->tablet_destroy.link); + +	free(tablet_pad); +} + +static void handle_pad_tablet_surface_destroy(struct wl_listener *listener, +		void *data) { +	struct sway_tablet_pad *tablet_pad = +		wl_container_of(listener, tablet_pad, surface_destroy); + +	wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad, +		tablet_pad->current_surface); +	wl_list_remove(&tablet_pad->surface_destroy.link); +	wl_list_init(&tablet_pad->surface_destroy.link); +	tablet_pad->current_surface = NULL; +} + +void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, +		struct wlr_surface *surface) { +	if (!tablet_pad || !tablet_pad->tablet) { +		return; +	} + +	if (surface == tablet_pad->current_surface) { +		return; +	} + +	/* Leave current surface */ +	if (tablet_pad->current_surface) { +		wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad, +			tablet_pad->current_surface); +		wl_list_remove(&tablet_pad->surface_destroy.link); +		wl_list_init(&tablet_pad->surface_destroy.link); +		tablet_pad->current_surface = NULL; +	} + +	if (!wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) { +		return; +	} + +	wlr_tablet_v2_tablet_pad_notify_enter(tablet_pad->tablet_v2_pad, +		tablet_pad->tablet->tablet_v2, surface); + +	tablet_pad->current_surface = surface; +	wl_list_remove(&tablet_pad->surface_destroy.link); +	tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy; +	wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy); +} | 
