From 2e637b7368de565a85f77fbd03408f33b763dd7b Mon Sep 17 00:00:00 2001
From: Brian Ashworth <bosrsf04@gmail.com>
Date: Thu, 18 Oct 2018 13:13:40 -0400
Subject: cmd_bind{sym,code}: Implement per-device bindings

bindsym --input-device=<identifier> ...
bindcode --input-device=<identifier> ...
---
 sway/input/cursor.c        | 40 +++++++++++++++++++++++++++-------------
 sway/input/input-manager.c |  6 +++---
 sway/input/keyboard.c      | 34 +++++++++++++++++++++++-----------
 3 files changed, 53 insertions(+), 27 deletions(-)

(limited to 'sway/input')

diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 688fc230..7ac5013d 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -727,19 +727,23 @@ static void state_add_button(struct sway_cursor *cursor, uint32_t button) {
  * Return the mouse binding which matches modifier, click location, release,
  * and pressed button state, otherwise return null.
  */
-static struct sway_binding* get_active_mouse_binding(const struct sway_cursor *cursor,
-		list_t *bindings, uint32_t modifiers, bool release, bool on_titlebar,
-				     bool on_border, bool on_content) {
+static struct sway_binding* get_active_mouse_binding(
+		const struct sway_cursor *cursor, list_t *bindings, uint32_t modifiers,
+		bool release, bool on_titlebar, bool on_border, bool on_content,
+		const char *identifier) {
 	uint32_t click_region = (on_titlebar ? BINDING_TITLEBAR : 0) |
 			(on_border ? BINDING_BORDER : 0) |
 			(on_content ? BINDING_CONTENTS : 0);
 
+	struct sway_binding *current = NULL;
 	for (int i = 0; i < bindings->length; ++i) {
 		struct sway_binding *binding = bindings->items[i];
 		if (modifiers ^ binding->modifiers ||
 				cursor->pressed_button_count != (size_t)binding->keys->length ||
 				release != (binding->flags & BINDING_RELEASE) ||
-				!(click_region & binding->flags)) {
+				!(click_region & binding->flags) ||
+				(strcmp(binding->input, identifier) != 0 &&
+				 strcmp(binding->input, "*") != 0)) {
 			continue;
 		}
 
@@ -755,13 +759,20 @@ static struct sway_binding* get_active_mouse_binding(const struct sway_cursor *c
 			continue;
 		}
 
-		return binding;
+		if (!current || strcmp(current->input, "*") == 0) {
+			current = binding;
+			if (strcmp(current->input, identifier) == 0) {
+				// If a binding is found for the exact input, quit searching
+				break;
+			}
+		}
 	}
-	return NULL;
+	return current;
 }
 
 void dispatch_cursor_button(struct sway_cursor *cursor,
-		uint32_t time_msec, uint32_t button, enum wlr_button_state state) {
+		struct wlr_input_device *device, uint32_t time_msec, uint32_t button,
+		enum wlr_button_state state) {
 	if (time_msec == 0) {
 		time_msec = get_current_time_msec();
 	}
@@ -797,18 +808,21 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
 	struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
 	uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
 
+	char *device_identifier = device ? input_device_get_identifier(device)
+		: strdup("*");
 	struct sway_binding *binding = NULL;
 	if (state == WLR_BUTTON_PRESSED) {
 		state_add_button(cursor, button);
 		binding = get_active_mouse_binding(cursor,
 			config->current_mode->mouse_bindings, modifiers, false,
-			on_titlebar, on_border, on_contents);
+			on_titlebar, on_border, on_contents, device_identifier);
 	} else {
 		binding = get_active_mouse_binding(cursor,
 			config->current_mode->mouse_bindings, modifiers, true,
-			on_titlebar, on_border, on_contents);
+			on_titlebar, on_border, on_contents, device_identifier);
 		state_erase_button(cursor, button);
 	}
+	free(device_identifier);
 	if (binding) {
 		seat_execute_command(seat, binding);
 		return;
@@ -942,7 +956,7 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) {
 	struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
 	wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
 	struct wlr_event_pointer_button *event = data;
-	dispatch_cursor_button(cursor,
+	dispatch_cursor_button(cursor, event->device,
 			event->time_msec, event->button, event->state);
 	transaction_commit_dirty();
 }
@@ -1128,7 +1142,7 @@ 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(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
 	struct wlr_event_tablet_tool_tip *event = data;
-	dispatch_cursor_button(cursor, event->time_msec,
+	dispatch_cursor_button(cursor, event->device, event->time_msec,
 			BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ?
 				WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED);
 	transaction_commit_dirty();
@@ -1143,14 +1157,14 @@ static void handle_tool_button(struct wl_listener *listener, void *data) {
 	switch (event->state) {
 	case WLR_BUTTON_PRESSED:
 		if (cursor->tool_buttons == 0) {
-			dispatch_cursor_button(cursor,
+			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,
+			dispatch_cursor_button(cursor, event->device,
 					event->time_msec, BTN_RIGHT, event->state);
 		}
 		cursor->tool_buttons--;
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 70c2abf7..671f9a47 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -45,7 +45,7 @@ struct sway_seat *input_manager_get_seat(
 	return seat_create(input, seat_name);
 }
 
-static char *get_device_identifier(struct wlr_input_device *device) {
+char *input_device_get_identifier(struct wlr_input_device *device) {
 	int vendor = device->vendor;
 	int product = device->product;
 	char *name = strdup(device->name);
@@ -278,7 +278,7 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
 	device->data = input_device;
 
 	input_device->wlr_device = device;
-	input_device->identifier = get_device_identifier(device);
+	input_device->identifier = input_device_get_identifier(device);
 	wl_list_insert(&input->devices, &input_device->link);
 
 	wlr_log(WLR_DEBUG, "adding device: '%s'",
@@ -375,7 +375,7 @@ void handle_virtual_keyboard(struct wl_listener *listener, void *data) {
 	device->data = input_device;
 
 	input_device->wlr_device = device;
-	input_device->identifier = get_device_identifier(device);
+	input_device->identifier = input_device_get_identifier(device);
 	wl_list_insert(&input_manager->devices, &input_device->link);
 
 	wlr_log(WLR_DEBUG, "adding virtual keyboard: '%s'",
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index 2c8b41cd..4427dabe 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -87,7 +87,7 @@ static void update_shortcut_state(struct sway_shortcut_state *state,
  */
 static void get_active_binding(const struct sway_shortcut_state *state,
 		list_t *bindings, struct sway_binding **current_binding,
-		uint32_t modifiers, bool release, bool locked) {
+		uint32_t modifiers, bool release, bool locked, const char *input) {
 	for (int i = 0; i < bindings->length; ++i) {
 		struct sway_binding *binding = bindings->items[i];
 		bool binding_locked = binding->flags & BINDING_LOCKED;
@@ -96,7 +96,9 @@ static void get_active_binding(const struct sway_shortcut_state *state,
 		if (modifiers ^ binding->modifiers ||
 				state->npressed != (size_t)binding->keys->length ||
 				release != binding_release ||
-				locked > binding_locked) {
+				locked > binding_locked ||
+				(strcmp(binding->input, input) != 0 &&
+				 strcmp(binding->input, "*") != 0)) {
 			continue;
 		}
 
@@ -112,13 +114,19 @@ static void get_active_binding(const struct sway_shortcut_state *state,
 			continue;
 		}
 
-		if (*current_binding && *current_binding != binding) {
+		if (*current_binding && *current_binding != binding &&
+				strcmp((*current_binding)->input, binding->input) == 0) {
 			wlr_log(WLR_DEBUG, "encountered duplicate bindings %d and %d",
 					(*current_binding)->order, binding->order);
-		} else {
+		} else if (!*current_binding ||
+				strcmp((*current_binding)->input, "*") == 0) {
 			*current_binding = binding;
+
+			if (strcmp((*current_binding)->input, input) == 0) {
+				// If a binding is found for the exact input, quit searching
+				return;
+			}
 		}
-		return;
 	}
 }
 
@@ -202,6 +210,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
 	struct wlr_seat *wlr_seat = seat->wlr_seat;
 	struct wlr_input_device *wlr_device =
 		keyboard->seat_device->input_device->wlr_device;
+	char *device_identifier = input_device_get_identifier(wlr_device);
 	wlr_idle_notify_activity(seat->input->server->idle, wlr_seat);
 	struct wlr_event_keyboard_key *event = data;
 	bool input_inhibited = seat->exclusive_client != NULL;
@@ -242,13 +251,13 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
 	struct sway_binding *binding_released = NULL;
 	get_active_binding(&keyboard->state_keycodes,
 			config->current_mode->keycode_bindings, &binding_released,
-			code_modifiers, true, input_inhibited);
+			code_modifiers, true, input_inhibited, device_identifier);
 	get_active_binding(&keyboard->state_keysyms_translated,
 			config->current_mode->keysym_bindings, &binding_released,
-			translated_modifiers, true, input_inhibited);
+			translated_modifiers, true, input_inhibited, device_identifier);
 	get_active_binding(&keyboard->state_keysyms_raw,
 			config->current_mode->keysym_bindings, &binding_released,
-			raw_modifiers, true, input_inhibited);
+			raw_modifiers, true, input_inhibited, device_identifier);
 
 	// Execute stored release binding once no longer active
 	if (keyboard->held_binding && binding_released != keyboard->held_binding &&
@@ -268,13 +277,14 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
 	if (event->state == WLR_KEY_PRESSED) {
 		get_active_binding(&keyboard->state_keycodes,
 				config->current_mode->keycode_bindings, &binding,
-				code_modifiers, false, input_inhibited);
+				code_modifiers, false, input_inhibited, device_identifier);
 		get_active_binding(&keyboard->state_keysyms_translated,
 				config->current_mode->keysym_bindings, &binding,
-				translated_modifiers, false, input_inhibited);
+				translated_modifiers, false, input_inhibited,
+				device_identifier);
 		get_active_binding(&keyboard->state_keysyms_raw,
 				config->current_mode->keysym_bindings, &binding,
-				raw_modifiers, false, input_inhibited);
+				raw_modifiers, false, input_inhibited, device_identifier);
 
 		if (binding) {
 			seat_execute_command(seat, binding);
@@ -315,6 +325,8 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
 	}
 
 	transaction_commit_dirty();
+
+	free(device_identifier);
 }
 
 static int handle_keyboard_repeat(void *data) {
-- 
cgit v1.2.3