From a78a5684ea8a19e54d797ab6cddd2f81f88360a5 Mon Sep 17 00:00:00 2001
From: frsfnrrg <frsfnrrg@users.noreply.github.com>
Date: Sun, 27 May 2018 12:37:18 -0400
Subject: [PATCH] Implement bindsym/bindcode --locked

Adds the --locked flag to bindsym and bindcode commands.

When a keyboard's associated seat has an exclusive client
(i.e, a screenlocker), then bindings are only executed if
they have the locked flag. When there is no such client,
this restriction is lifted.
---
 include/sway/config.h |  1 +
 sway/commands/bind.c  | 48 +++++++++++++++++++++++++++----------------
 sway/input/keyboard.c | 25 +++++++++++++---------
 sway/sway.5.scd       |  9 +++++---
 4 files changed, 52 insertions(+), 31 deletions(-)

diff --git a/include/sway/config.h b/include/sway/config.h
index 33f52156..118981e3 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -28,6 +28,7 @@ struct sway_variable {
 struct sway_binding {
 	int order;
 	bool release;
+	bool locked;
 	bool bindcode;
 	list_t *keys;
 	uint32_t modifiers;
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index cbabb07b..c6b3368a 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -83,20 +83,26 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
 	binding->keys = create_list();
 	binding->modifiers = 0;
 	binding->release = false;
+	binding->locked = false;
 	binding->bindcode = false;
 
-	// Handle --release
-	if (strcmp("--release", argv[0]) == 0) {
-		if (argc >= 3) {
+	// Handle --release and --locked
+	while (argc > 0) {
+		if (strcmp("--release", argv[0]) == 0) {
 			binding->release = true;
-			argv++;
-			argc--;
+		} else if (strcmp("--locked", argv[0]) == 0) {
+			binding->locked = true;
 		} else {
-			free_sway_binding(binding);
-			return cmd_results_new(CMD_FAILURE, "bindsym",
-				"Invalid bindsym command "
-				"(expected more than 2 arguments, got %d)", argc);
+			break;
 		}
+		argv++;
+		argc--;
+	}
+	if (argc < 2) {
+		free_sway_binding(binding);
+		return cmd_results_new(CMD_FAILURE, "bindsym",
+			"Invalid bindsym command "
+			"(expected at least 2 non-option arguments, got %d)", argc);
 	}
 
 	binding->command = join_args(argv + 1, argc - 1);
@@ -176,20 +182,26 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) {
 	binding->keys = create_list();
 	binding->modifiers = 0;
 	binding->release = false;
+	binding->locked = false;
 	binding->bindcode = true;
 
-	// Handle --release
-	if (strcmp("--release", argv[0]) == 0) {
-		if (argc >= 3) {
+	// Handle --release and --locked
+	while (argc > 0) {
+		if (strcmp("--release", argv[0]) == 0) {
 			binding->release = true;
-			argv++;
-			argc--;
+		} else if (strcmp("--locked", argv[0]) == 0) {
+			binding->locked = true;
 		} else {
-			free_sway_binding(binding);
-			return cmd_results_new(CMD_FAILURE, "bindcode",
-				"Invalid bindcode command "
-				"(expected more than 2 arguments, got %d)", argc);
+			break;
 		}
+		argv++;
+		argc--;
+	}
+	if (argc < 2) {
+		free_sway_binding(binding);
+		return cmd_results_new(CMD_FAILURE, "bindcode",
+			"Invalid bindcode command "
+			"(expected at least 2 non-option arguments, got %d)", argc);
 	}
 
 	binding->command = join_args(argv + 1, argc - 1);
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index c07557db..e873eea3 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -141,7 +141,7 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
  */
 static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard,
 		xkb_keysym_t *pressed_keysyms, uint32_t modifiers,
-		enum wlr_key_state key_state) {
+		enum wlr_key_state key_state, bool locked) {
 	// configured bindings
 	int n = pressed_keysyms_length(pressed_keysyms);
 	list_t *keysym_bindings = config->current_mode->keysym_bindings;
@@ -149,7 +149,7 @@ static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard,
 		struct sway_binding *binding = keysym_bindings->items[i];
 		if (!binding_matches_key_state(binding, key_state) ||
 				modifiers ^ binding->modifiers ||
-				n != binding->keys->length) {
+				n != binding->keys->length || locked > binding->locked) {
 			continue;
 		}
 
@@ -174,7 +174,7 @@ static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard,
 }
 
 static bool binding_matches_keycodes(struct wlr_keyboard *keyboard,
-		struct sway_binding *binding, struct wlr_event_keyboard_key *event) {
+		struct sway_binding *binding, struct wlr_event_keyboard_key *event, bool locked) {
 	assert(binding->bindcode);
 
 	uint32_t keycode = event->keycode + 8;
@@ -183,6 +183,10 @@ static bool binding_matches_keycodes(struct wlr_keyboard *keyboard,
 		return false;
 	}
 
+	if (locked > binding->locked) {
+		return false;
+	}
+
 	uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard);
 	if (modifiers ^ binding->modifiers) {
 		return false;
@@ -265,13 +269,13 @@ static bool binding_matches_keycodes(struct wlr_keyboard *keyboard,
  * should be propagated to clients.
  */
 static bool keyboard_execute_bindcode(struct sway_keyboard *keyboard,
-		struct wlr_event_keyboard_key *event) {
+		struct wlr_event_keyboard_key *event, bool locked) {
 	struct wlr_keyboard *wlr_keyboard =
 		keyboard->seat_device->input_device->wlr_device->keyboard;
 	list_t *keycode_bindings = config->current_mode->keycode_bindings;
 	for (int i = 0; i < keycode_bindings->length; ++i) {
 		struct sway_binding *binding = keycode_bindings->items[i];
-		if (binding_matches_keycodes(wlr_keyboard, binding, event)) {
+		if (binding_matches_keycodes(wlr_keyboard, binding, event, locked)) {
 			keyboard_execute_command(keyboard, binding);
 			return true;
 		}
@@ -333,19 +337,20 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
 		keyboard->seat_device->input_device->wlr_device;
 	wlr_idle_notify_activity(keyboard->seat_device->sway_seat->input->server->idle, wlr_seat);
 	struct wlr_event_keyboard_key *event = data;
+	bool input_inhibited = keyboard->seat_device->sway_seat->exclusive_client != NULL;
 
 	xkb_keycode_t keycode = event->keycode + 8;
 	bool handled = false;
 
 	// handle keycodes
-	handled = keyboard_execute_bindcode(keyboard, event);
+	handled = keyboard_execute_bindcode(keyboard, event, input_inhibited);
 
 	// handle translated keysyms
 	if (!handled && event->state == WLR_KEY_RELEASED) {
 		handled = keyboard_execute_bindsym(keyboard,
 			keyboard->pressed_keysyms_translated,
 			keyboard->modifiers_translated,
-			event->state);
+			event->state, input_inhibited);
 	}
 	const xkb_keysym_t *translated_keysyms;
 	size_t translated_keysyms_len =
@@ -357,14 +362,14 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
 		handled = keyboard_execute_bindsym(keyboard,
 			keyboard->pressed_keysyms_translated,
 			keyboard->modifiers_translated,
-			event->state);
+			event->state, input_inhibited);
 	}
 
 	// Handle raw keysyms
 	if (!handled && event->state == WLR_KEY_RELEASED) {
 		handled = keyboard_execute_bindsym(keyboard,
 			keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
-			event->state);
+			event->state, input_inhibited);
 	}
 	const xkb_keysym_t *raw_keysyms;
 	size_t raw_keysyms_len =
@@ -374,7 +379,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
 	if (!handled && event->state == WLR_KEY_PRESSED) {
 		handled = keyboard_execute_bindsym(keyboard,
 			keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
-			event->state);
+			event->state, input_inhibited);
 	}
 
 	// Compositor bindings
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index e6bc5a1e..5d99c9d6 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -186,17 +186,20 @@ runtime.
 
 		for\_window <criteria> move container to workspace <workspace>
 
-*bindsym* <key combo> <command>
+*bindsym* [--release|--locked] <key combo> <command>
 	Binds _key combo_ to execute the sway command _command_ when pressed. You
 	may use XKB key names here (*xev*(1) is a good tool for discovering these).
+	With the flag _--release_, the command is executed when the key combo is
+	released. Unless the flag _--locked_ is set, the command will not be run
+	when a screen locking program is active.
 
 	Example:
 
 		# Execute firefox when alt, shift, and f are pressed together
 		bindsym Mod1+Shift+f exec firefox
 
-	*bindcode* <code> <command> is also available for binding with key codes
-	instead of key names.
+	*bindcode* [--release|--locked] <code> <command> is also available for
+	binding with key codes instead of key names.
 
 *client.<class>* <border> <background> <text> <indicator> <child\_border>
 	Configures the color of window borders and title bars. All 5 colors are