From 9114d3b84cf4e5ba0513a8f4d4a018a6de3d6223 Mon Sep 17 00:00:00 2001
From: Drew DeVault <sir@cmpwn.com>
Date: Sun, 8 Apr 2018 10:48:13 -0400
Subject: [PATCH] Implement tablet tool support

---
 include/sway/input/cursor.h |  4 ++-
 sway/input/cursor.c         | 67 ++++++++++++++++++++++++++++++++-----
 sway/input/seat.c           | 12 +++++--
 3 files changed, 72 insertions(+), 11 deletions(-)

diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h
index 64917ce5..8f907dcd 100644
--- a/include/sway/input/cursor.h
+++ b/include/sway/input/cursor.h
@@ -1,6 +1,6 @@
 #ifndef _SWAY_INPUT_CURSOR_H
 #define _SWAY_INPUT_CURSOR_H
-
+#include <stdint.h>
 #include "sway/input/seat.h"
 
 struct sway_cursor {
@@ -22,6 +22,8 @@ struct sway_cursor {
 
 	struct wl_listener tool_axis;
 	struct wl_listener tool_tip;
+	struct wl_listener tool_button;
+	uint32_t tool_buttons;
 
 	struct wl_listener request_set_cursor;
 };
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 6db615b1..cdded1c7 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -179,10 +179,8 @@ static void handle_cursor_motion_absolute(
 	cursor_send_pointer_motion(cursor, event->time_msec);
 }
 
-static void handle_cursor_button(struct wl_listener *listener, void *data) {
-	struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
-	struct wlr_event_pointer_button *event = data;
-
+static void dispatch_cursor_button(struct sway_cursor *cursor,
+		uint32_t time_msec, uint32_t button, enum wlr_button_state state) {
 	struct wlr_surface *surface = NULL;
 	double sx, sy;
 	struct sway_container *cont =
@@ -215,8 +213,15 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) {
 		seat_set_focus(cursor->seat, cont);
 	}
 
-	wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, event->time_msec,
-		event->button, event->state);
+	wlr_seat_pointer_notify_button(cursor->seat->wlr_seat,
+			time_msec, button, state);
+}
+
+static void handle_cursor_button(struct wl_listener *listener, void *data) {
+	struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
+	struct wlr_event_pointer_button *event = data;
+	dispatch_cursor_button(cursor,
+			event->time_msec, event->button, event->state);
 }
 
 static void handle_cursor_axis(struct wl_listener *listener, void *data) {
@@ -248,13 +253,53 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
 static void handle_tool_axis(struct wl_listener *listener, void *data) {
 	struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis);
 	struct wlr_event_tablet_tool_axis *event = data;
-	wlr_log(L_DEBUG, "TODO: handle tool axis event: %p", event);
+
+	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);
+		cursor_update_position(cursor);
+		cursor_send_pointer_motion(cursor, event->time_msec);
+	} else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) {
+		wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, -1);
+		cursor_update_position(cursor);
+		cursor_send_pointer_motion(cursor, event->time_msec);
+	} else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
+		wlr_cursor_warp_absolute(cursor->cursor, event->device, -1, event->y);
+		cursor_update_position(cursor);
+		cursor_send_pointer_motion(cursor, event->time_msec);
+	}
 }
 
 static void handle_tool_tip(struct wl_listener *listener, void *data) {
 	struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip);
 	struct wlr_event_tablet_tool_tip *event = data;
-	wlr_log(L_DEBUG, "TODO: handle tool tip event: %p", event);
+	dispatch_cursor_button(cursor, event->time_msec,
+			BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ?
+				WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED);
+}
+
+static void handle_tool_button(struct wl_listener *listener, void *data) {
+	struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button);
+	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->time_msec, BTN_RIGHT, event->state);
+		}
+		cursor->tool_buttons++;
+		break;
+	case WLR_BUTTON_RELEASED:
+		if (cursor->tool_buttons == 1) {
+			dispatch_cursor_button(cursor,
+					event->time_msec, BTN_RIGHT, event->state);
+		}
+		cursor->tool_buttons--;
+		break;
+	}
 }
 
 static void handle_request_set_cursor(struct wl_listener *listener,
@@ -332,6 +377,9 @@ 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;
@@ -339,6 +387,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_button, &cursor->tool_button);
+	cursor->tool_button.notify = handle_tool_button;
+
 	wl_signal_add(&seat->wlr_seat->events.request_set_cursor,
 			&cursor->request_set_cursor);
 	cursor->request_set_cursor.notify = handle_request_set_cursor;
diff --git a/sway/input/seat.c b/sway/input/seat.c
index b94e3291..c34da5e5 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -245,6 +245,12 @@ static void seat_configure_keyboard(struct sway_seat *seat,
 	}
 }
 
+static void seat_configure_tablet_tool(struct sway_seat *seat,
+		struct sway_seat_device *sway_device) {
+	wlr_cursor_attach_input_device(seat->cursor->cursor,
+		sway_device->input_device->wlr_device);
+}
+
 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;
@@ -272,9 +278,11 @@ void seat_configure_device(struct sway_seat *seat,
 		case WLR_INPUT_DEVICE_KEYBOARD:
 			seat_configure_keyboard(seat, seat_device);
 			break;
-		case WLR_INPUT_DEVICE_TOUCH:
-		case WLR_INPUT_DEVICE_TABLET_PAD:
 		case WLR_INPUT_DEVICE_TABLET_TOOL:
+			seat_configure_tablet_tool(seat, seat_device);
+			break;
+		case WLR_INPUT_DEVICE_TABLET_PAD:
+		case WLR_INPUT_DEVICE_TOUCH:
 			wlr_log(L_DEBUG, "TODO: configure other devices");
 			break;
 	}