From 6f6a9af60ec98a6f18f1163317dee74b88328dab Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 28 Dec 2018 13:48:09 -0500 Subject: [PATCH 1/2] Add helpers for improved mouse button parsing The following helper functions have been added to aid with parsing mouse buttons from a string: 1. `get_mouse_bindsym`: attempts to parse the string as an x11 button (button[1-9]) or as an event name (ex BTN_LEFT or BTN_SIDE) 2. `get_mouse_bindcode`: attempts to parse the string as an event code and validates that the event code is a button (starts with `BTN_`). 3. `get_mouse_button`: this is a conveniency function for callers that do not care whether a bindsym or bindcode are used and attempts to parse the string as a bindsym and then bindcode. None of these functions are used in this commit. The sole purpose of this commit is to make the larger set more granular and easier to review/manipulate. There will be a series of commits following this one that will modify any command which uses a mouse button to use these helpers. --- include/sway/input/cursor.h | 8 +++++ sway/input/cursor.c | 65 +++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 22e278b0c..4636bf6b2 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -86,4 +86,12 @@ void cursor_warp_to_container(struct sway_cursor *cursor, void cursor_warp_to_workspace(struct sway_cursor *cursor, struct sway_workspace *workspace); + +uint32_t get_mouse_bindsym(const char *name, char **error); + +uint32_t get_mouse_bindcode(const char *name, char **error); + +// Considers both bindsym and bindcode +uint32_t get_mouse_button(const char *name, char **error); + #endif diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 96feb47d0..0d5e93619 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -2,8 +2,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -1527,3 +1529,66 @@ void cursor_warp_to_workspace(struct sway_cursor *cursor, wlr_cursor_warp(cursor->cursor, NULL, x, y); } + +uint32_t get_mouse_bindsym(const char *name, char **error) { + if (strncasecmp(name, "button", strlen("button")) == 0) { + // Map to x11 mouse buttons + int number = name[strlen("button")] - '0'; + if (number < 1 || number > 9 || strlen(name) > strlen("button0")) { + *error = strdup("Only buttons 1-9 are supported. For other mouse " + "buttons, use the name of the event code."); + return 0; + } + static const uint32_t buttons[] = {BTN_LEFT, BTN_MIDDLE, BTN_RIGHT, + SWAY_SCROLL_UP, SWAY_SCROLL_DOWN, SWAY_SCROLL_LEFT, + SWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA}; + return buttons[number - 1]; + } else if (strncmp(name, "BTN_", strlen("BTN_")) == 0) { + // Get event code from name + int code = libevdev_event_code_from_name(EV_KEY, name); + if (code == -1) { + size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1; + *error = malloc(len); + if (*error) { + snprintf(*error, len, "Unknown event %s", name); + } + return 0; + } + return code; + } + return 0; +} + +uint32_t get_mouse_bindcode(const char *name, char **error) { + // Validate event code + errno = 0; + char *endptr; + int code = strtol(name, &endptr, 10); + if (endptr == name && code <= 0) { + *error = strdup("Button event code must be a positive integer."); + return 0; + } else if (errno == ERANGE) { + *error = strdup("Button event code out of range."); + return 0; + } + const char *event = libevdev_event_code_get_name(EV_KEY, code); + if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { + size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button", + code, event) + 1; + *error = malloc(len); + if (*error) { + snprintf(*error, len, "Event code %d (%s) is not a button", + code, event); + } + return 0; + } + return code; +} + +uint32_t get_mouse_button(const char *name, char **error) { + uint32_t button = get_mouse_bindsym(name, error); + if (!button && !error) { + button = get_mouse_bindcode(name, error); + } + return button; +} From eefa6b1ad3f7e6fcbcf52595a833ae28364c18ea Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 6 Jan 2019 13:16:54 -0500 Subject: [PATCH 2/2] bind{code,sym}: utilize mouse button helpers This modifies `bindcode` and `bindsym` to use `get_mouse_bindcode` and `get_mouse_bindsym`, respectively, to parse mouse buttons. Additionally, the `BINDING_MOUSE` type has been split into `BINDING_MOUSECODE` and `BINDING_MOUSESYM` to match keys and allow for mouse bindcodes to be used. Between the two commands, all button syms and codes should be supported, including x11 axis buttons. --- include/sway/config.h | 3 +- sway/commands/bind.c | 130 +++++++++++++++++++++++++----------------- sway/ipc-server.c | 8 ++- sway/sway.5.scd | 2 +- 4 files changed, 86 insertions(+), 57 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index ebf16e6a3..29c21afe5 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -27,7 +27,8 @@ struct sway_variable { enum binding_input_type { BINDING_KEYCODE, BINDING_KEYSYM, - BINDING_MOUSE, + BINDING_MOUSECODE, + BINDING_MOUSESYM, }; enum binding_flags { diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 4fceaf8ce..be47d4128 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -85,69 +85,91 @@ static int key_qsort_cmp(const void *keyp_a, const void *keyp_b) { */ static struct cmd_results *identify_key(const char* name, bool first_key, uint32_t* key_val, enum binding_input_type* type) { - if (*type == BINDING_KEYCODE) { - // check for keycode + if (*type == BINDING_MOUSECODE) { + // check for mouse bindcodes + char *message = NULL; + uint32_t button = get_mouse_bindcode(name, &message); + if (!button) { + if (message) { + struct cmd_results *error = + cmd_results_new(CMD_INVALID, "bindcode", message); + free(message); + return error; + } else { + return cmd_results_new(CMD_INVALID, "bindcode", + "Unknown button code %s", name); + } + } + *key_val = button; + } else if (*type == BINDING_MOUSESYM) { + // check for mouse bindsyms (x11 buttons or event names) + char *message = NULL; + uint32_t button = get_mouse_bindsym(name, &message); + if (!button) { + if (message) { + struct cmd_results *error = + cmd_results_new(CMD_INVALID, "bindsym", message); + free(message); + return error; + } else if (!button) { + return cmd_results_new(CMD_INVALID, "bindsym", + "Unknown button %s", name); + } + } + *key_val = button; + } else if (*type == BINDING_KEYCODE) { + // check for keycode. If it is the first key, allow mouse bindcodes + if (first_key) { + char *message = NULL; + uint32_t button = get_mouse_bindcode(name, &message); + free(message); + if (button) { + *type = BINDING_MOUSECODE; + *key_val = button; + return NULL; + } + } + xkb_keycode_t keycode = strtol(name, NULL, 10); if (!xkb_keycode_is_legal_ext(keycode)) { - return cmd_results_new(CMD_INVALID, "bindcode", - "Invalid keycode '%s'", name); + if (first_key) { + return cmd_results_new(CMD_INVALID, "bindcode", + "Invalid keycode or button code '%s'", name); + } else { + return cmd_results_new(CMD_INVALID, "bindcode", + "Invalid keycode '%s'", name); + } } *key_val = keycode; } else { - // check for keysym + // check for keysym. If it is the first key, allow mouse bindsyms + if (first_key) { + char *message = NULL; + uint32_t button = get_mouse_bindsym(name, &message); + if (message) { + struct cmd_results *error = + cmd_results_new(CMD_INVALID, "bindsym", message); + free(message); + return error; + } else if (button) { + *type = BINDING_MOUSESYM; + *key_val = button; + return NULL; + } + } + xkb_keysym_t keysym = xkb_keysym_from_name(name, XKB_KEYSYM_CASE_INSENSITIVE); - - // Check for mouse binding - uint32_t button = 0; - if (strncasecmp(name, "button", strlen("button")) == 0) { - // Map to x11 mouse buttons - button = name[strlen("button")] - '0'; - if (button < 1 || button > 9 || strlen(name) > strlen("button0")) { + if (!keysym) { + if (first_key) { return cmd_results_new(CMD_INVALID, "bindsym", - "Only buttons 1-9 are supported. For other mouse " - "buttons, use the name of the event code."); - } - uint32_t buttons[] = {BTN_LEFT, BTN_MIDDLE, BTN_RIGHT, - SWAY_SCROLL_UP, SWAY_SCROLL_DOWN, SWAY_SCROLL_LEFT, - SWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA}; - button = buttons[button - 1]; - } else if (strncmp(name, "BTN_", strlen("BTN_")) == 0) { - // Get event code - int code = libevdev_event_code_from_name(EV_KEY, name); - if (code == -1) { - return cmd_results_new(CMD_INVALID, "bindsym", - "Invalid event code name %s", name); - } - button = code; - } - - if (*type == BINDING_KEYSYM) { - if (button) { - if (first_key) { - *type = BINDING_MOUSE; - *key_val = button; - } else { - return cmd_results_new(CMD_INVALID, "bindsym", - "Mixed button '%s' into key sequence", name); - } - } else if (keysym) { - *key_val = keysym; + "Unknown key or button '%s'", name); } else { return cmd_results_new(CMD_INVALID, "bindsym", - "Unknown key '%s'", name); - } - } else { - if (button) { - *key_val = button; - } else if (keysym) { - return cmd_results_new(CMD_INVALID, "bindsym", - "Mixed keysym '%s' into button sequence", name); - } else { - return cmd_results_new(CMD_INVALID, "bindsym", - "Unknown button '%s'", name); + "Unknown key '%s'", name); } } + *key_val = keysym; } return NULL; } @@ -201,7 +223,8 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, } if (binding->flags & (BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR) || exclude_titlebar) { - binding->type = BINDING_MOUSE; + binding->type = binding->type == BINDING_KEYCODE ? + BINDING_MOUSECODE : BINDING_MOUSESYM; } if (argc < 2) { @@ -249,7 +272,8 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, // that this is one if (exclude_titlebar) { binding->flags &= ~BINDING_TITLEBAR; - } else if (binding->type == BINDING_MOUSE) { + } else if (binding->type == BINDING_MOUSECODE + || binding->type == BINDING_MOUSESYM) { binding->flags |= BINDING_TITLEBAR; } diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 456db866c..ff1bc89fb 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -445,8 +445,12 @@ void ipc_event_binding(struct sway_binding *binding) { json_object_object_add(json_binding, "input_code", json_object_new_int(input_code)); json_object_object_add(json_binding, "symbols", symbols); json_object_object_add(json_binding, "symbol", symbol); - json_object_object_add(json_binding, "input_type", binding->type == BINDING_MOUSE ? - json_object_new_string("mouse") : json_object_new_string("keyboard")); + + bool mouse = binding->type == BINDING_MOUSECODE || + binding->type == BINDING_MOUSESYM; + json_object_object_add(json_binding, "input_type", mouse + ? json_object_new_string("mouse") + : json_object_new_string("keyboard")); json_object *json = json_object_new_object(); json_object_object_add(json, "change", json_object_new_string("run")); diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 3757a097c..3398ad585 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -302,7 +302,7 @@ runtime. ``` *bindcode* [--release|--locked] [--input-device=] [--no-warn] - is also available for binding with key codes instead of key names. + is also available for binding with key/button codes instead of key/button names. *client.* Configures the color of window borders and title bars. All 5 colors are