diff --git a/include/sway/commands.h b/include/sway/commands.h index 68487879..7672a3fd 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -207,6 +207,7 @@ sway_cmd bar_cmd_pango_markup; sway_cmd bar_cmd_strip_workspace_numbers; sway_cmd bar_cmd_strip_workspace_name; sway_cmd bar_cmd_swaybar_command; +sway_cmd bar_cmd_tray_bindcode; sway_cmd bar_cmd_tray_bindsym; sway_cmd bar_cmd_tray_output; sway_cmd bar_cmd_tray_padding; diff --git a/include/sway/config.h b/include/sway/config.h index 96fe899b..978606a6 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -260,7 +260,7 @@ struct bar_config { #if HAVE_TRAY char *icon_theme; - const char *tray_bindings[10]; // mouse buttons 0-9 + struct wl_list tray_bindings; // struct tray_binding::link list_t *tray_outputs; // char * int tray_padding; #endif @@ -272,6 +272,14 @@ struct bar_binding { char *command; }; +#if HAVE_TRAY +struct tray_binding { + uint32_t button; + const char *command; + struct wl_list link; // struct tray_binding::link +}; +#endif + struct border_colors { float border[4]; float background[4]; diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 77aa0ea1..abd72783 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -103,4 +103,6 @@ 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); +const char *get_mouse_button_name(uint32_t button); + #endif diff --git a/include/swaybar/config.h b/include/swaybar/config.h index add0a1cf..ec042e51 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h @@ -70,13 +70,21 @@ struct swaybar_config { #if HAVE_TRAY char *icon_theme; - char *tray_bindings[10]; // mouse buttons 0-9 + struct wl_list tray_bindings; // struct tray_binding::link bool tray_hidden; list_t *tray_outputs; // char * int tray_padding; #endif }; +#if HAVE_TRAY +struct tray_binding { + uint32_t button; + char *command; + struct wl_list link; // struct tray_binding::link +}; +#endif + struct swaybar_config *init_config(void); void free_config(struct swaybar_config *config); uint32_t parse_position(const char *position); diff --git a/include/swaybar/input.h b/include/swaybar/input.h index 4b46b0de..d76cd551 100644 --- a/include/swaybar/input.h +++ b/include/swaybar/input.h @@ -22,19 +22,6 @@ struct swaybar_pointer { uint32_t serial; }; -enum x11_button { - NONE, - LEFT, - MIDDLE, - RIGHT, - SCROLL_UP, - SCROLL_DOWN, - SCROLL_LEFT, - SCROLL_RIGHT, - BACK, - FORWARD, -}; - enum hotspot_event_handling { HOTSPOT_IGNORE, HOTSPOT_PROCESS, @@ -54,6 +41,8 @@ extern const struct wl_seat_listener seat_listener; void update_cursor(struct swaybar *bar); +uint32_t event_to_x11_button(uint32_t event); + void free_hotspots(struct wl_list *list); #endif diff --git a/sway/commands/bar.c b/sway/commands/bar.c index b19d9574..2cfc538f 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c @@ -28,6 +28,7 @@ static struct cmd_handler bar_handlers[] = { { "status_padding", bar_cmd_status_padding }, { "strip_workspace_name", bar_cmd_strip_workspace_name }, { "strip_workspace_numbers", bar_cmd_strip_workspace_numbers }, + { "tray_bindcode", bar_cmd_tray_bindcode }, { "tray_bindsym", bar_cmd_tray_bindsym }, { "tray_output", bar_cmd_tray_output }, { "tray_padding", bar_cmd_tray_padding }, diff --git a/sway/commands/bar/bind.c b/sway/commands/bar/bind.c index 71adced8..4b0be804 100644 --- a/sway/commands/bar/bind.c +++ b/sway/commands/bar/bind.c @@ -46,27 +46,7 @@ static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code) { free_bar_binding(binding); return cmd_results_new(CMD_INVALID, "Unknown button %s", argv[0]); } - - const char *name = libevdev_event_code_get_name(EV_KEY, binding->button); - if (!name) { - switch (binding->button) { - case SWAY_SCROLL_UP: - name = "SWAY_SCROLL_UP"; - break; - case SWAY_SCROLL_DOWN: - name = "SWAY_SCROLL_DOWN"; - break; - case SWAY_SCROLL_LEFT: - name = "SWAY_SCROLL_LEFT"; - break; - case SWAY_SCROLL_RIGHT: - name = "SWAY_SCROLL_RIGHT"; - break; - default: - // Unreachable - break; - } - } + const char *name = get_mouse_button_name(binding->button); binding->command = join_args(argv + 1, argc - 1); diff --git a/sway/commands/bar/tray_bind.c b/sway/commands/bar/tray_bind.c new file mode 100644 index 00000000..48a15462 --- /dev/null +++ b/sway/commands/bar/tray_bind.c @@ -0,0 +1,97 @@ +#include +#include "config.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/input/cursor.h" +#include "log.h" + +static struct cmd_results *tray_bind(int argc, char **argv, bool code) { +#if HAVE_TRAY + const char *command = code ? "bar tray_bindcode" : "bar tray_bindsym"; + struct cmd_results *error = NULL; + if ((error = checkarg(argc, command, EXPECTED_EQUAL_TO, 2))) { + return error; + } + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "No bar defined."); + } + + struct tray_binding *binding = calloc(1, sizeof(struct tray_binding)); + if (!binding) { + return cmd_results_new(CMD_FAILURE, "Unable to allocate tray binding"); + } + + char *message = NULL; + if (code) { + binding->button = get_mouse_bindcode(argv[0], &message); + } else { + binding->button = get_mouse_bindsym(argv[0], &message); + } + if (message) { + free(binding); + error = cmd_results_new(CMD_INVALID, message); + free(message); + return error; + } else if (!binding->button) { + free(binding); + return cmd_results_new(CMD_INVALID, "Unknown button %s", argv[0]); + } + const char *name = get_mouse_button_name(binding->button); + + static const char *commands[] = { + "ContextMenu", + "Activate", + "SecondaryActivate", + "ScrollDown", + "ScrollLeft", + "ScrollRight", + "ScrollUp", + "nop" + }; + + for (size_t i = 0; i < sizeof(commands) / sizeof(commands[0]); ++i) { + if (strcasecmp(argv[1], commands[i]) == 0) { + binding->command = commands[i]; + } + } + if (!binding->command) { + return cmd_results_new(CMD_INVALID, "[Bar %s] Invalid tray command %s", + config->current_bar->id, argv[1]); + } + + bool overwritten = false; + struct tray_binding *other = NULL; + wl_list_for_each(other, &config->current_bar->tray_bindings, link) { + if (other->button == binding->button) { + overwritten = true; + other->command = binding->command; + free(binding); + binding = other; + wlr_log(WLR_DEBUG, + "[bar %s] Updated tray binding for %u (%s) to %s", + config->current_bar->id, binding->button, name, + binding->command); + break; + } + } + if (!overwritten) { + wl_list_insert(&config->current_bar->tray_bindings, &binding->link); + wlr_log(WLR_DEBUG, "[bar %s] Added tray binding for %u (%s) to %s", + config->current_bar->id, binding->button, name, + binding->command); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +#else + return cmd_results_new(CMD_INVALID, + "Sway has been compiled without tray support"); +#endif +} + +struct cmd_results *bar_cmd_tray_bindcode(int argc, char **argv) { + return tray_bind(argc, argv, true); +} + +struct cmd_results *bar_cmd_tray_bindsym(int argc, char **argv) { + return tray_bind(argc, argv, false); +} diff --git a/sway/commands/bar/tray_bindsym.c b/sway/commands/bar/tray_bindsym.c deleted file mode 100644 index 4e57e35e..00000000 --- a/sway/commands/bar/tray_bindsym.c +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include "config.h" -#include "sway/commands.h" -#include "sway/config.h" -#include "log.h" - -struct cmd_results *bar_cmd_tray_bindsym(int argc, char **argv) { -#if HAVE_TRAY - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "tray_bindsym", EXPECTED_EQUAL_TO, 2))) { - return error; - } - - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "No bar defined."); - } - - int button = 0; - if (strncasecmp(argv[0], "button", strlen("button")) == 0 && - strlen(argv[0]) == strlen("button0")) { - button = argv[0][strlen("button")] - '0'; - } - if (button < 1 || button > 9) { - return cmd_results_new(CMD_FAILURE, - "[Bar %s] Only buttons 1 to 9 are supported", - config->current_bar->id); - } - - static const char *commands[] = { - "ContextMenu", - "Activate", - "SecondaryActivate", - "ScrollDown", - "ScrollLeft", - "ScrollRight", - "ScrollUp", - "nop" - }; - - for (size_t i = 0; i < sizeof(commands) / sizeof(commands[0]); ++i) { - if (strcasecmp(argv[1], commands[i]) == 0) { - wlr_log(WLR_DEBUG, "[Bar %s] Binding button %d to %s", - config->current_bar->id, button, commands[i]); - config->current_bar->tray_bindings[button] = commands[i]; - return cmd_results_new(CMD_SUCCESS, NULL); - } - } - - return cmd_results_new(CMD_INVALID, - "[Bar %s] Invalid command %s", config->current_bar->id, argv[1]); -#else - return cmd_results_new(CMD_INVALID, - "Sway has been compiled without tray support"); -#endif -} diff --git a/sway/config/bar.c b/sway/config/bar.c index 701bf051..b1aa2313 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -81,6 +81,12 @@ void free_bar_config(struct bar_config *bar) { #if HAVE_TRAY list_free_items_and_destroy(bar->tray_outputs); free(bar->icon_theme); + + struct tray_binding *tray_bind = NULL, *tmp_tray_bind = NULL; + wl_list_for_each_safe(tray_bind, tmp_tray_bind, &bar->tray_bindings, link) { + wl_list_remove(&tray_bind->link); + free(tray_bind); + } #endif free(bar); } @@ -174,6 +180,7 @@ struct bar_config *default_bar_config(void) { #if HAVE_TRAY bar->tray_padding = 2; + wl_list_init(&bar->tray_bindings); #endif list_add(config->bars, bar); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 731e82ad..5eb44412 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -1237,3 +1237,19 @@ uint32_t get_mouse_button(const char *name, char **error) { } return button; } + +const char *get_mouse_button_name(uint32_t button) { + const char *name = libevdev_event_code_get_name(EV_KEY, button); + if (!name) { + if (button == SWAY_SCROLL_UP) { + name = "SWAY_SCROLL_UP"; + } else if (button == SWAY_SCROLL_DOWN) { + name = "SWAY_SCROLL_DOWN"; + } else if (button == SWAY_SCROLL_LEFT) { + name = "SWAY_SCROLL_LEFT"; + } else if (button == SWAY_SCROLL_RIGHT) { + name = "SWAY_SCROLL_RIGHT"; + } + } + return name; +} diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 6e5ba4fd..d72fc5db 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -852,15 +852,16 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) { } json_object *tray_bindings = json_object_new_array(); - for (int i = 0; i < 10; ++i) { - if (bar->tray_bindings[i]) { - json_object *bind = json_object_new_object(); - json_object_object_add(bind, "input_code", - json_object_new_int(i)); - json_object_object_add(bind, "command", - json_object_new_string(bar->tray_bindings[i])); - json_object_array_add(tray_bindings, bind); - } + struct tray_binding *tray_bind = NULL; + wl_list_for_each(tray_bind, &bar->tray_bindings, link) { + json_object *bind = json_object_new_object(); + json_object_object_add(bind, "input_code", + json_object_new_int(event_to_x11_button(tray_bind->button))); + json_object_object_add(bind, "event_code", + json_object_new_int(tray_bind->button)); + json_object_object_add(bind, "command", + json_object_new_string(tray_bind->command)); + json_object_array_add(tray_bindings, bind); } if (json_object_array_length(tray_bindings) > 0) { json_object_object_add(json, "tray_bindings", tray_bindings); diff --git a/sway/meson.build b/sway/meson.build index c2ed6298..0a08ee74 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -127,7 +127,7 @@ sway_sources = files( 'commands/bar/strip_workspace_numbers.c', 'commands/bar/strip_workspace_name.c', 'commands/bar/swaybar_command.c', - 'commands/bar/tray_bindsym.c', + 'commands/bar/tray_bind.c', 'commands/bar/tray_output.c', 'commands/bar/tray_padding.c', 'commands/bar/workspace_buttons.c', diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index 3f6b4298..9a6397e3 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd @@ -115,13 +115,19 @@ Sway allows configuring swaybar in the sway configuration file. Swaybar provides a system tray where third-party applications may place icons. The following commands configure the tray. -The _button_ argument in all cases is a platform-specific button code. On Linux -you can find a list of these at linux/input-event-codes.h. +*tray\_bindcode* +ContextMenu|Activate|SecondaryActivate|ScrollDown|ScrollLeft|ScrollRight|ScrollUp|nop + Executes the action when the mouse button has been pressed. The buttons can + be given as an event code, which can be obtained from `libinput debug-events`. + To disable the default behavior for a button, use the command _nop_. -*tray\_bindsym* button ContextMenu|Activate|SecondaryActivate|ScrollDown|ScrollLeft|ScrollRight|ScrollUp|nop - Binds mouse button _n_ (1 to 9) to the specified action. Use the command - _nop_ to disable the default action (Activate for button 1, ContextMenu for - button 2 and SecondaryActivate for button 3). +*tray\_bindsym* button[1-9]| +ContextMenu|Activate|SecondaryActivate|ScrollDown|ScrollLeft|ScrollRight|ScrollUp|nop + Executes the action when the mouse button has been pressed. The buttons can + be given as a x11 button number or an event name, which can be obtained + from `libinput debug-events`. Use the command _nop_ to disable the default + action (Activate for button1, ContextMenu for button2 and SecondaryActivate + for button3). *tray\_padding* [px] Sets the pixel padding of the system tray. This padding will surround the diff --git a/swaybar/config.c b/swaybar/config.c index d4cc9b1a..0071c7f9 100644 --- a/swaybar/config.c +++ b/swaybar/config.c @@ -78,6 +78,7 @@ struct swaybar_config *init_config(void) { #if HAVE_TRAY config->tray_padding = 2; + wl_list_init(&config->tray_bindings); #endif return config; @@ -91,6 +92,16 @@ static void free_binding(struct swaybar_binding *binding) { free(binding); } +#if HAVE_TRAY +static void free_tray_binding(struct tray_binding *binding) { + if (!binding) { + return; + } + free(binding->command); + free(binding); +} +#endif + void free_config(struct swaybar_config *config) { free(config->status_command); free(config->font); @@ -111,9 +122,14 @@ void free_config(struct swaybar_config *config) { } #if HAVE_TRAY list_free_items_and_destroy(config->tray_outputs); - for (int i = 0; i < 10; ++i) { - free(config->tray_bindings[i]); + + struct tray_binding *tray_bind = NULL, *tmp_tray_bind = NULL; + wl_list_for_each_safe(tray_bind, tmp_tray_bind, &config->tray_bindings, + link) { + wl_list_remove(&tray_bind->link); + free_tray_binding(tray_bind); } + free(config->icon_theme); #endif free(config); diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index 116c8f6e..8bca1bf9 100644 --- a/swaybar/i3bar.c +++ b/swaybar/i3bar.c @@ -259,31 +259,6 @@ bool i3bar_handle_readable(struct status_line *status) { } } -static uint32_t event_to_x11_button(uint32_t event) { - switch (event) { - case BTN_LEFT: - return 1; - case BTN_MIDDLE: - return 2; - case BTN_RIGHT: - return 3; - case SWAY_SCROLL_UP: - return 4; - case SWAY_SCROLL_DOWN: - return 5; - case SWAY_SCROLL_LEFT: - return 6; - case SWAY_SCROLL_RIGHT: - return 7; - case BTN_SIDE: - return 8; - case BTN_EXTRA: - return 9; - default: - return 0; - } -} - enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, struct i3bar_block *block, int x, int y, int rx, int ry, int w, int h, uint32_t button) { diff --git a/swaybar/input.c b/swaybar/input.c index bdd55e58..998b186f 100644 --- a/swaybar/input.c +++ b/swaybar/input.c @@ -22,6 +22,31 @@ void free_hotspots(struct wl_list *list) { } } +uint32_t event_to_x11_button(uint32_t event) { + switch (event) { + case BTN_LEFT: + return 1; + case BTN_MIDDLE: + return 2; + case BTN_RIGHT: + return 3; + case SWAY_SCROLL_UP: + return 4; + case SWAY_SCROLL_DOWN: + return 5; + case SWAY_SCROLL_LEFT: + return 6; + case SWAY_SCROLL_RIGHT: + return 7; + case BTN_SIDE: + return 8; + case BTN_EXTRA: + return 9; + default: + return 0; + } +} + static uint32_t wl_axis_to_button(uint32_t axis, wl_fixed_t value) { bool negative = wl_fixed_to_double(value) < 0; switch (axis) { diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 097f9161..0dc39439 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -313,11 +313,13 @@ static bool ipc_parse_config( int length = json_object_array_length(tray_bindings); for (int i = 0; i < length; ++i) { json_object *bind = json_object_array_get_idx(tray_bindings, i); - json_object *button, *command; - json_object_object_get_ex(bind, "input_code", &button); - json_object_object_get_ex(bind, "command", &command); - config->tray_bindings[json_object_get_int(button)] = - strdup(json_object_get_string(command)); + struct tray_binding *binding = + calloc(1, sizeof(struct tray_binding)); + binding->button = json_object_get_int( + json_object_object_get(bind, "event_code")); + binding->command = strdup(json_object_get_string( + json_object_object_get(bind, "command"))); + wl_list_insert(&config->tray_bindings, &binding->link); } } diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 0833dcb9..9056331e 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -301,8 +301,15 @@ void destroy_sni(struct swaybar_sni *sni) { } static void handle_click(struct swaybar_sni *sni, int x, int y, - enum x11_button button, int delta) { - const char *method = sni->tray->bar->config->tray_bindings[button]; + uint32_t button, int delta) { + const char *method = NULL; + struct tray_binding *binding = NULL; + wl_list_for_each(binding, &sni->tray->bar->config->tray_bindings, link) { + if (binding->button == button) { + method = binding->command; + break; + } + } if (!method) { static const char *default_bindings[10] = { "nop", @@ -316,7 +323,7 @@ static void handle_click(struct swaybar_sni *sni, int x, int y, "nop", "nop" }; - method = default_bindings[button]; + method = default_bindings[event_to_x11_button(button)]; } if (strcmp(method, "nop") == 0) { return; @@ -345,7 +352,7 @@ static int cmp_sni_id(const void *item, const void *cmp_to) { static enum hotspot_event_handling icon_hotspot_callback( struct swaybar_output *output, struct swaybar_hotspot *hotspot, - int x, int y, enum x11_button button, void *data) { + int x, int y, uint32_t button, void *data) { wlr_log(WLR_DEBUG, "Clicked on %s", (char *)data); struct swaybar_tray *tray = output->bar->tray;