From ba69f06695c24f98a05d138a53ba130108ebce6f Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Wed, 27 Dec 2017 10:08:18 -0500
Subject: binding config

---
 sway/commands/bind.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 173 insertions(+)
 create mode 100644 sway/commands/bind.c

(limited to 'sway/commands')

diff --git a/sway/commands/bind.c b/sway/commands/bind.c
new file mode 100644
index 00000000..62aa535a
--- /dev/null
+++ b/sway/commands/bind.c
@@ -0,0 +1,173 @@
+#ifdef __linux__
+#include <linux/input-event-codes.h>
+#elif __FreeBSD__
+#include <dev/evdev/input-event-codes.h>
+#endif
+#include <xkbcommon/xkbcommon.h>
+#include <xkbcommon/xkbcommon-names.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/config.h"
+#include "list.h"
+#include "log.h"
+#include "stringop.h"
+#include "util.h"
+
+int binding_order = 0;
+
+void free_sway_binding(struct sway_binding *binding) {
+	if (binding->keys) {
+		for (int i = 0; i < binding->keys->length; i++) {
+			free(binding->keys->items[i]);
+		}
+		list_free(binding->keys);
+	}
+	if (binding->command) {
+		free(binding->command);
+	}
+	free(binding);
+}
+
+struct cmd_results *cmd_bindsym(int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) {
+		return error;
+	}
+
+	struct sway_binding *binding = malloc(sizeof(struct sway_binding));
+	if (!binding) {
+		return cmd_results_new(CMD_FAILURE, "bindsym",
+				"Unable to allocate binding");
+	}
+	binding->keys = create_list();
+	binding->modifiers = 0;
+	binding->release = false;
+	binding->bindcode = false;
+
+	// Handle --release
+	if (strcmp("--release", argv[0]) == 0) {
+		if (argc >= 3) {
+			binding->release = true;
+			argv++;
+			argc--;
+		} else {
+			free_sway_binding(binding);
+			return cmd_results_new(CMD_FAILURE, "bindsym",
+				"Invalid bindsym command "
+				"(expected more than 2 arguments, got %d)", argc);
+		}
+	}
+
+	binding->command = join_args(argv + 1, argc - 1);
+
+	list_t *split = split_string(argv[0], "+");
+	for (int i = 0; i < split->length; ++i) {
+		// Check for a modifier key
+		uint32_t mod;
+		if ((mod = get_modifier_mask_by_name(split->items[i])) > 0) {
+			binding->modifiers |= mod;
+			continue;
+		}
+		// Check for xkb key
+		xkb_keysym_t sym = xkb_keysym_from_name(split->items[i],
+				XKB_KEYSYM_CASE_INSENSITIVE);
+
+		// Check for mouse binding
+		if (strncasecmp(split->items[i], "button", strlen("button")) == 0 &&
+				strlen(split->items[i]) == strlen("button0")) {
+			sym = ((char *)split->items[i])[strlen("button")] - '1' + BTN_LEFT;
+		}
+		if (!sym) {
+			struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym",
+					"Unknown key '%s'", (char *)split->items[i]);
+			free_sway_binding(binding);
+			free_flat_list(split);
+			return ret;
+		}
+		xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t));
+		if (!key) {
+			free_sway_binding(binding);
+			free_flat_list(split);
+			return cmd_results_new(CMD_FAILURE, "bindsym",
+					"Unable to allocate binding");
+		}
+		*key = sym;
+		list_add(binding->keys, key);
+	}
+	free_flat_list(split);
+
+	struct sway_mode *mode = config->current_mode;
+	// TODO overwrite the binding if it already exists
+	binding->order = binding_order++;
+	list_add(mode->keysym_bindings, binding);
+
+	sway_log(L_DEBUG, "bindsym - Bound %s to command %s",
+		argv[0], binding->command);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
+
+struct cmd_results *cmd_bindcode(int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "bindcode", EXPECTED_MORE_THAN, 1))) {
+		return error;
+	}
+
+	struct sway_binding *binding = malloc(sizeof(struct sway_binding));
+	if (!binding) {
+		return cmd_results_new(CMD_FAILURE, "bindsym",
+				"Unable to allocate binding");
+	}
+	binding->keys = create_list();
+	binding->modifiers = 0;
+	binding->release = false;
+	binding->bindcode = true;
+
+	// Handle --release
+	if (strcmp("--release", argv[0]) == 0) {
+		if (argc >= 3) {
+			binding->release = true;
+			argv++;
+			argc--;
+		} else {
+			free_sway_binding(binding);
+			return cmd_results_new(CMD_FAILURE, "bindcode",
+				"Invalid bindcode command "
+				"(expected more than 2 arguments, got %d)", argc);
+		}
+	}
+
+	binding->command = join_args(argv + 1, argc - 1);
+
+	list_t *split = split_string(argv[0], "+");
+	for (int i = 0; i < split->length; ++i) {
+		// Check for a modifier key
+		uint32_t mod;
+		if ((mod = get_modifier_mask_by_name(split->items[i])) > 0) {
+			binding->modifiers |= mod;
+			continue;
+		}
+		// parse keycode
+		xkb_keycode_t keycode = (int)strtol(split->items[i], NULL, 10);
+		if (!xkb_keycode_is_legal_ext(keycode)) {
+			error =
+				cmd_results_new(CMD_INVALID, "bindcode",
+					"Invalid keycode '%s'", (char *)split->items[i]);
+			free_sway_binding(binding);
+			list_free(split);
+			return error;
+		}
+		xkb_keycode_t *key = malloc(sizeof(xkb_keycode_t));
+		*key = keycode - 8;
+		list_add(binding->keys, key);
+	}
+	free_flat_list(split);
+
+	struct sway_mode *mode = config->current_mode;
+	// TODO overwrite binding if it already exists
+	binding->order = binding_order++;
+	list_add(mode->keycode_bindings, binding);
+
+	sway_log(L_DEBUG, "bindcode - Bound %s to command %s",
+		argv[0], binding->command);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
-- 
cgit v1.2.3


From 62b7ab3959468124086a1ba95361b3eed069b4a7 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Thu, 28 Dec 2017 18:50:22 -0500
Subject: overwrite old bindings

---
 sway/commands/bind.c  | 83 ++++++++++++++++++++++++++++++++++++++++++++++-----
 sway/input/keyboard.c | 14 +++++----
 2 files changed, 85 insertions(+), 12 deletions(-)

(limited to 'sway/commands')

diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index 62aa535a..99f54f46 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -28,6 +28,44 @@ void free_sway_binding(struct sway_binding *binding) {
 	free(binding);
 }
 
+/**
+ * Returns true if the bindings have the same key and modifier combinations.
+ * Note that keyboard layout is not considered, so the bindings might actually
+ * not be equivalent on some layouts.
+ */
+bool binding_key_compare(struct sway_binding *binding_a,
+		struct sway_binding *binding_b) {
+	if (binding_a->bindcode != binding_b->bindcode) {
+		return false;
+	}
+
+	if (binding_a->modifiers ^ binding_b->modifiers) {
+		return false;
+	}
+
+	if (binding_a->keys->length != binding_b->keys->length) {
+		return false;
+	}
+
+	int keys_len = binding_a->keys->length;
+	for (int i = 0; i < keys_len; ++i) {
+		uint32_t key_a = *(uint32_t*)binding_a->keys->items[i];
+		bool found = false;
+		for (int j = 0; j < keys_len; ++j) {
+			uint32_t key_b = *(uint32_t*)binding_b->keys->items[j];
+			if (key_b == key_a) {
+				found = true;
+				break;
+			}
+		}
+		if (!found) {
+			return false;
+		}
+	}
+
+	return true;
+}
+
 struct cmd_results *cmd_bindsym(int argc, char **argv) {
 	struct cmd_results *error = NULL;
 	if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) {
@@ -95,11 +133,26 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
 		list_add(binding->keys, key);
 	}
 	free_flat_list(split);
-
-	struct sway_mode *mode = config->current_mode;
-	// TODO overwrite the binding if it already exists
 	binding->order = binding_order++;
-	list_add(mode->keysym_bindings, binding);
+
+	list_t *mode_bindings = config->current_mode->keysym_bindings;
+
+	// overwrite the binding if it already exists
+	bool overwritten = false;
+	for (int i = 0; i < mode_bindings->length; ++i) {
+		struct sway_binding *config_binding = mode_bindings->items[i];
+		if (binding_key_compare(binding, config_binding)) {
+			sway_log(L_DEBUG, "overwriting old binding with command '%s'",
+				config_binding->command);
+			free_sway_binding(config_binding);
+			mode_bindings->items[i] = binding;
+			overwritten = true;
+		}
+	}
+
+	if (!overwritten) {
+		list_add(mode_bindings, binding);
+	}
 
 	sway_log(L_DEBUG, "bindsym - Bound %s to command %s",
 		argv[0], binding->command);
@@ -162,10 +215,26 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) {
 	}
 	free_flat_list(split);
 
-	struct sway_mode *mode = config->current_mode;
-	// TODO overwrite binding if it already exists
 	binding->order = binding_order++;
-	list_add(mode->keycode_bindings, binding);
+
+	list_t *mode_bindings = config->current_mode->keycode_bindings;
+
+	// overwrite the binding if it already exists
+	bool overwritten = false;
+	for (int i = 0; i < mode_bindings->length; ++i) {
+		struct sway_binding *config_binding = mode_bindings->items[i];
+		if (binding_key_compare(binding, config_binding)) {
+			sway_log(L_DEBUG, "overwriting old binding with command '%s'",
+				config_binding->command);
+			free_sway_binding(config_binding);
+			mode_bindings->items[i] = binding;
+			overwritten = true;
+		}
+	}
+
+	if (!overwritten) {
+		list_add(mode_bindings, binding);
+	}
 
 	sway_log(L_DEBUG, "bindcode - Bound %s to command %s",
 		argv[0], binding->command);
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index eacb6ceb..6272dcce 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -67,7 +67,9 @@ static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard,
 	list_t *keysym_bindings = config->current_mode->keysym_bindings;
 	for (int i = 0; i < keysym_bindings->length; ++i) {
 		struct sway_binding *binding = keysym_bindings->items[i];
+		sway_log(L_DEBUG, "@@ checking binding: %s", binding->command);
 		if (modifiers ^ binding->modifiers || n != binding->keys->length) {
+			sway_log(L_DEBUG, "@@ modifiers or key num dont match");
 			continue;
 		}
 
@@ -286,17 +288,18 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
 
 	xkb_keycode_t keycode = event->keycode + 8;
 	bool handled = false;
-	const xkb_keysym_t *keysyms;
 
 	// handle keycodes
 	handled = keyboard_execute_bindcode(keyboard);
+	sway_log(L_DEBUG, "@@ handled by bindcode? %d", handled);
 
 	// handle translated keysyms
+	const xkb_keysym_t *translated_keysyms;
 	uint32_t translated_modifiers;
 	size_t translated_keysyms_len =
-		keyboard_keysyms_translated(keyboard, keycode, &keysyms,
+		keyboard_keysyms_translated(keyboard, keycode, &translated_keysyms,
 			&translated_modifiers);
-	pressed_keysyms_update(keyboard->pressed_keysyms_translated, keysyms,
+	pressed_keysyms_update(keyboard->pressed_keysyms_translated, translated_keysyms,
 		translated_keysyms_len, event->state);
 	if (event->state == WLR_KEY_PRESSED && !handled) {
 		handled = keyboard_execute_bindsym(keyboard,
@@ -305,9 +308,10 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
 	}
 
 	// Handle raw keysyms
+	const xkb_keysym_t *raw_keysyms;
 	uint32_t raw_modifiers;
-	size_t raw_keysyms_len = keyboard_keysyms_raw(keyboard, keycode, &keysyms, &raw_modifiers);
-	pressed_keysyms_update(keyboard->pressed_keysyms_raw, keysyms, raw_keysyms_len,
+	size_t raw_keysyms_len = keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &raw_modifiers);
+	pressed_keysyms_update(keyboard->pressed_keysyms_raw, raw_keysyms, raw_keysyms_len,
 		event->state);
 	if (event->state == WLR_KEY_PRESSED && !handled) {
 		handled = keyboard_execute_bindsym(keyboard,
-- 
cgit v1.2.3


From bd3ca70e3d67476f4d66e9bc31c67b11aefb3519 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Fri, 29 Dec 2017 09:10:07 -0500
Subject: fix nitpicks

---
 sway/commands/bind.c  | 21 ++++++++++-----------
 sway/input/keyboard.c | 17 ++++++++---------
 2 files changed, 18 insertions(+), 20 deletions(-)

(limited to 'sway/commands')

diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index 99f54f46..ef9a4b7a 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -16,15 +16,14 @@
 int binding_order = 0;
 
 void free_sway_binding(struct sway_binding *binding) {
-	if (binding->keys) {
-		for (int i = 0; i < binding->keys->length; i++) {
-			free(binding->keys->items[i]);
-		}
-		list_free(binding->keys);
+	if (!binding) {
+		return;
 	}
-	if (binding->command) {
-		free(binding->command);
+
+	if (binding->keys) {
+		free_flat_list(binding->keys);
 	}
+	free(binding->command);
 	free(binding);
 }
 
@@ -72,7 +71,7 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
 		return error;
 	}
 
-	struct sway_binding *binding = malloc(sizeof(struct sway_binding));
+	struct sway_binding *binding = calloc(1, sizeof(struct sway_binding));
 	if (!binding) {
 		return cmd_results_new(CMD_FAILURE, "bindsym",
 				"Unable to allocate binding");
@@ -122,7 +121,7 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
 			free_flat_list(split);
 			return ret;
 		}
-		xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t));
+		xkb_keysym_t *key = calloc(1, sizeof(xkb_keysym_t));
 		if (!key) {
 			free_sway_binding(binding);
 			free_flat_list(split);
@@ -165,7 +164,7 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) {
 		return error;
 	}
 
-	struct sway_binding *binding = malloc(sizeof(struct sway_binding));
+	struct sway_binding *binding = calloc(1, sizeof(struct sway_binding));
 	if (!binding) {
 		return cmd_results_new(CMD_FAILURE, "bindsym",
 				"Unable to allocate binding");
@@ -209,7 +208,7 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) {
 			list_free(split);
 			return error;
 		}
-		xkb_keycode_t *key = malloc(sizeof(xkb_keycode_t));
+		xkb_keycode_t *key = calloc(1, sizeof(xkb_keycode_t));
 		*key = keycode - 8;
 		list_add(binding->keys, key);
 	}
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index 6272dcce..3090d32f 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -67,9 +67,7 @@ static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard,
 	list_t *keysym_bindings = config->current_mode->keysym_bindings;
 	for (int i = 0; i < keysym_bindings->length; ++i) {
 		struct sway_binding *binding = keysym_bindings->items[i];
-		sway_log(L_DEBUG, "@@ checking binding: %s", binding->command);
 		if (modifiers ^ binding->modifiers || n != binding->keys->length) {
-			sway_log(L_DEBUG, "@@ modifiers or key num dont match");
 			continue;
 		}
 
@@ -85,7 +83,8 @@ static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard,
 		}
 
 		if (match) {
-			sway_log(L_DEBUG, "running command for binding: %s", binding->command);
+			sway_log(L_DEBUG, "running command for binding: %s",
+				binding->command);
 			struct cmd_results *results = handle_command(binding->command);
 			if (results->status != CMD_SUCCESS) {
 				sway_log(L_DEBUG, "could not run command for binding: %s",
@@ -291,7 +290,6 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
 
 	// handle keycodes
 	handled = keyboard_execute_bindcode(keyboard);
-	sway_log(L_DEBUG, "@@ handled by bindcode? %d", handled);
 
 	// handle translated keysyms
 	const xkb_keysym_t *translated_keysyms;
@@ -299,8 +297,8 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
 	size_t translated_keysyms_len =
 		keyboard_keysyms_translated(keyboard, keycode, &translated_keysyms,
 			&translated_modifiers);
-	pressed_keysyms_update(keyboard->pressed_keysyms_translated, translated_keysyms,
-		translated_keysyms_len, event->state);
+	pressed_keysyms_update(keyboard->pressed_keysyms_translated,
+		translated_keysyms, translated_keysyms_len, event->state);
 	if (event->state == WLR_KEY_PRESSED && !handled) {
 		handled = keyboard_execute_bindsym(keyboard,
 			keyboard->pressed_keysyms_translated, translated_modifiers,
@@ -310,9 +308,10 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
 	// Handle raw keysyms
 	const xkb_keysym_t *raw_keysyms;
 	uint32_t raw_modifiers;
-	size_t raw_keysyms_len = keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &raw_modifiers);
-	pressed_keysyms_update(keyboard->pressed_keysyms_raw, raw_keysyms, raw_keysyms_len,
-		event->state);
+	size_t raw_keysyms_len =
+		keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &raw_modifiers);
+	pressed_keysyms_update(keyboard->pressed_keysyms_raw, raw_keysyms,
+		raw_keysyms_len, event->state);
 	if (event->state == WLR_KEY_PRESSED && !handled) {
 		handled = keyboard_execute_bindsym(keyboard,
 			keyboard->pressed_keysyms_raw, raw_modifiers,
-- 
cgit v1.2.3


From 50e791cadbfc51d531fff44cfbe3323a3b456adf Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Thu, 4 Jan 2018 07:25:52 -0500
Subject: binding release

---
 include/sway/input/keyboard.h |  3 ++
 sway/commands/bind.c          |  4 ++
 sway/input/keyboard.c         | 96 +++++++++++++++++++++++++++++++++----------
 3 files changed, 82 insertions(+), 21 deletions(-)

(limited to 'sway/commands')

diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h
index 0a5857f0..8ec3eb35 100644
--- a/include/sway/input/keyboard.h
+++ b/include/sway/input/keyboard.h
@@ -14,7 +14,10 @@ struct sway_keyboard {
 	struct wl_listener keyboard_modifiers;
 
 	xkb_keysym_t pressed_keysyms_translated[SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP];
+	uint32_t modifiers_translated;
+
 	xkb_keysym_t pressed_keysyms_raw[SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP];
+	uint32_t modifiers_raw;
 };
 
 struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index ef9a4b7a..79121404 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -34,6 +34,10 @@ void free_sway_binding(struct sway_binding *binding) {
  */
 bool binding_key_compare(struct sway_binding *binding_a,
 		struct sway_binding *binding_b) {
+	if (binding_a->release != binding_b->release) {
+		return false;
+	}
+
 	if (binding_a->bindcode != binding_b->bindcode) {
 		return false;
 	}
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index 3090d32f..7b90dacf 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -1,3 +1,4 @@
+#include <assert.h>
 #include <wlr/backend/multi.h>
 #include <wlr/backend/session.h>
 #include "sway/input/seat.h"
@@ -54,6 +55,18 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
 	return false;
 }
 
+static bool binding_matches_keystate(struct sway_binding *binding,
+		enum wlr_key_state key_state) {
+	if (key_state == WLR_KEY_PRESSED && !binding->release) {
+		return true;
+	}
+	if (key_state == WLR_KEY_RELEASED && binding->release) {
+		return true;
+	}
+
+	return false;
+}
+
 /**
  * Execute keyboard bindings bound with `bindysm`.
  *
@@ -61,13 +74,15 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
  * should be propagated to clients.
  */
 static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard,
-		xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) {
+		xkb_keysym_t *pressed_keysyms, uint32_t modifiers, enum wlr_key_state key_state) {
 	// configured bindings
 	int n = pressed_keysyms_length(pressed_keysyms);
 	list_t *keysym_bindings = config->current_mode->keysym_bindings;
 	for (int i = 0; i < keysym_bindings->length; ++i) {
 		struct sway_binding *binding = keysym_bindings->items[i];
-		if (modifiers ^ binding->modifiers || n != binding->keys->length) {
+		if (!binding_matches_keystate(binding, key_state) ||
+				modifiers ^ binding->modifiers ||
+				n != binding->keys->length) {
 			continue;
 		}
 
@@ -115,16 +130,43 @@ static bool keysym_is_modifier(xkb_keysym_t keysym) {
 }
 
 static bool binding_matches_keycodes(struct wlr_keyboard *keyboard,
-		struct sway_binding *binding) {
+		struct sway_binding *binding, struct wlr_event_keyboard_key *event) {
+	assert(binding->bindcode);
+
+	uint32_t keycode = event->keycode + 8;
+
+	if (!binding_matches_keystate(binding, event->state)) {
+		return false;
+	}
+
 	uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard);
 	if (modifiers ^ binding->modifiers) {
 		return false;
 	}
 
+	// on release, the released key must be in the binding
+	if (event->state == WLR_KEY_RELEASED) {
+		bool found = false;
+		for (int i = 0; i < binding->keys->length; ++i) {
+			uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8;
+			if (binding_keycode == keycode) {
+				found = true;
+				break;
+			}
+		}
+		if (!found) {
+			return false;
+		}
+	}
+
 	// every keycode in the binding must be present in the pressed keys on the
 	// keyboard
 	for (int i = 0; i < binding->keys->length; ++i) {
 		uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8;
+		if (event->state == WLR_KEY_RELEASED && keycode == binding_keycode) {
+			continue;
+		}
+
 		bool found = false;
 		for (size_t j = 0; j < keyboard->num_keycodes; ++j) {
 			xkb_keycode_t keycode = keyboard->keycodes[j] + 8;
@@ -178,13 +220,14 @@ static bool binding_matches_keycodes(struct wlr_keyboard *keyboard,
  * Returns true if the keysym was handled by a binding and false if the event
  * should be propagated to clients.
  */
-static bool keyboard_execute_bindcode(struct sway_keyboard *keyboard) {
+static bool keyboard_execute_bindcode(struct sway_keyboard *keyboard,
+		struct wlr_event_keyboard_key *event) {
 	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)) {
+		if (binding_matches_keycodes(wlr_keyboard, binding, event)) {
 			struct cmd_results *results = handle_command(binding->command);
 			if (results->status != CMD_SUCCESS) {
 				sway_log(L_DEBUG, "could not run command for binding: %s",
@@ -289,50 +332,61 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
 	bool handled = false;
 
 	// handle keycodes
-	handled = keyboard_execute_bindcode(keyboard);
+	handled = keyboard_execute_bindcode(keyboard, event);
 
 	// handle translated keysyms
+	if (!handled && event->state == WLR_KEY_RELEASED) {
+		handled = keyboard_execute_bindsym(keyboard,
+			keyboard->pressed_keysyms_translated,
+			keyboard->modifiers_translated,
+			event->state);
+	}
 	const xkb_keysym_t *translated_keysyms;
-	uint32_t translated_modifiers;
 	size_t translated_keysyms_len =
 		keyboard_keysyms_translated(keyboard, keycode, &translated_keysyms,
-			&translated_modifiers);
+			&keyboard->modifiers_translated);
 	pressed_keysyms_update(keyboard->pressed_keysyms_translated,
 		translated_keysyms, translated_keysyms_len, event->state);
-	if (event->state == WLR_KEY_PRESSED && !handled) {
+	if (!handled && event->state == WLR_KEY_PRESSED) {
 		handled = keyboard_execute_bindsym(keyboard,
-			keyboard->pressed_keysyms_translated, translated_modifiers,
-			translated_keysyms_len);
+			keyboard->pressed_keysyms_translated,
+			keyboard->modifiers_translated,
+			event->state);
 	}
 
 	// Handle raw keysyms
+	if (!handled && event->state == WLR_KEY_RELEASED) {
+		handled = keyboard_execute_bindsym(keyboard,
+			keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
+			event->state);
+	}
 	const xkb_keysym_t *raw_keysyms;
-	uint32_t raw_modifiers;
 	size_t raw_keysyms_len =
-		keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &raw_modifiers);
+		keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &keyboard->modifiers_raw);
 	pressed_keysyms_update(keyboard->pressed_keysyms_raw, raw_keysyms,
 		raw_keysyms_len, event->state);
-	if (event->state == WLR_KEY_PRESSED && !handled) {
+	if (!handled && event->state == WLR_KEY_PRESSED) {
 		handled = keyboard_execute_bindsym(keyboard,
-			keyboard->pressed_keysyms_raw, raw_modifiers,
-			raw_keysyms_len);
+			keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
+			event->state);
 	}
 
 	// Compositor bindings
-	if (event->state == WLR_KEY_PRESSED && !handled) {
+	if (!handled && event->state == WLR_KEY_PRESSED) {
 		handled =
 			keyboard_execute_compositor_binding(keyboard,
-				keyboard->pressed_keysyms_translated, translated_modifiers,
+				keyboard->pressed_keysyms_translated,
+				keyboard->modifiers_translated,
 				translated_keysyms_len);
 	}
-	if (event->state == WLR_KEY_PRESSED && !handled) {
+	if (!handled && event->state == WLR_KEY_PRESSED) {
 		handled =
 			keyboard_execute_compositor_binding(keyboard,
-				keyboard->pressed_keysyms_raw, raw_modifiers,
+				keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
 				raw_keysyms_len);
 	}
 
-	if (!handled) {
+	if (!handled || event->state == WLR_KEY_RELEASED) {
 		wlr_seat_set_keyboard(wlr_seat, wlr_device);
 		wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
 				event->keycode, event->state);
-- 
cgit v1.2.3