diff --git a/common/util.c b/common/util.c index ed6d033f..b5037d35 100644 --- a/common/util.c +++ b/common/util.c @@ -13,3 +13,41 @@ int numlen(int n) { if (n >= 10) return 2; return 1; } + +static struct modifier_key { + char *name; + uint32_t mod; +} modifiers[] = { + { XKB_MOD_NAME_SHIFT, WLC_BIT_MOD_SHIFT }, + { XKB_MOD_NAME_CAPS, WLC_BIT_MOD_CAPS }, + { XKB_MOD_NAME_CTRL, WLC_BIT_MOD_CTRL }, + { "Ctrl", WLC_BIT_MOD_CTRL }, + { XKB_MOD_NAME_ALT, WLC_BIT_MOD_ALT }, + { "Alt", WLC_BIT_MOD_ALT }, + { XKB_MOD_NAME_NUM, WLC_BIT_MOD_MOD2 }, + { "Mod3", WLC_BIT_MOD_MOD3 }, + { XKB_MOD_NAME_LOGO, WLC_BIT_MOD_LOGO }, + { "Mod5", WLC_BIT_MOD_MOD5 }, +}; + +uint32_t get_modifier_mask_by_name(const char *name) { + int i; + for (i = 0; i < (int)(sizeof(modifiers) / sizeof(struct modifier_key)); ++i) { + if (strcasecmp(modifiers[i].name, name) == 0) { + return modifiers[i].mod; + } + } + + return 0; +} + +const char *get_modifier_name_by_mask(uint32_t modifier) { + int i; + for (i = 0; i < (int)(sizeof(modifiers) / sizeof(struct modifier_key)); ++i) { + if (modifiers[i].mod == modifier) { + return modifiers[i].name; + } + } + + return NULL; +} diff --git a/include/config.h b/include/config.h index a915fbed..5e1c39f3 100644 --- a/include/config.h +++ b/include/config.h @@ -135,6 +135,7 @@ struct sway_config { list_t *workspace_outputs; list_t *output_configs; list_t *criteria; + list_t *active_bar_modifiers; struct sway_mode *current_mode; struct bar_config *current_bar; uint32_t floating_mod; @@ -176,6 +177,11 @@ void merge_output_config(struct output_config *dst, struct output_config *src); void apply_output_config(struct output_config *oc, swayc_t *output); void free_output_config(struct output_config *oc); +/** + * Updates the list of active bar modifiers + */ +void update_active_bar_modifiers(void); + int workspace_output_cmp_workspace(const void *a, const void *b); int sway_binding_cmp(const void *a, const void *b); diff --git a/include/input_state.h b/include/input_state.h index a1f238e1..79e27d91 100644 --- a/include/input_state.h +++ b/include/input_state.h @@ -60,6 +60,12 @@ extern struct pointer_state { int mode; } pointer_state; +enum modifier_state { + MOD_STATE_UNCHANGED = 0, + MOD_STATE_PRESSED = 1, + MOD_STATE_RELEASED = 2 +}; + void pointer_position_set(struct wlc_origin *new_origin, bool force_focus); void center_pointer_on(swayc_t *view); @@ -75,5 +81,19 @@ void pointer_mode_reset(void); void input_init(void); +/** + * Check if state of mod changed from current state to new_state. + * + * Returns MOD_STATE_UNCHANGED if the state didn't change, MOD_STATE_PRESSED if + * the state changed to pressed and MOD_STATE_RELEASED if the state changed to + * released. + */ +uint32_t modifier_state_changed(uint32_t new_state, uint32_t mod); + +/** + * Update the current modifiers state to new_state. + */ +void modifiers_state_update(uint32_t new_state); + #endif diff --git a/include/ipc-server.h b/include/ipc-server.h index 04975093..47026bfd 100644 --- a/include/ipc-server.h +++ b/include/ipc-server.h @@ -15,6 +15,12 @@ void ipc_event_barconfig_update(struct bar_config *bar); * Send IPC mode event to all listening clients */ void ipc_event_mode(const char *mode); +/** + * Sends an IPC modifier event to all listening clients. The modifier event + * includes a key 'change' with the value of state and a key 'modifier' with + * the name of that modifier. + */ +void ipc_event_modifier(uint32_t modifier, const char *state); const char *swayc_type_string(enum swayc_types type); #endif diff --git a/include/ipc.h b/include/ipc.h index e0b3b736..56593529 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -17,6 +17,7 @@ enum ipc_command_type { IPC_EVENT_WINDOW = (1 << 31 | 3), IPC_EVENT_BARCONFIG_UPDATE = (1 << 31 | 4), IPC_EVENT_BINDING = (1 << 31 | 5), + IPC_EVENT_MODIFIER = (1 << 31 | 6), IPC_SWAY_GET_PIXELS = 0x81 }; diff --git a/include/util.h b/include/util.h index 9cb861dd..4bbb64c8 100644 --- a/include/util.h +++ b/include/util.h @@ -1,6 +1,10 @@ #ifndef _SWAY_UTIL_H #define _SWAY_UTIL_H +#include +#include +#include + /** * Wrap i into the range [0, max[ */ @@ -11,4 +15,18 @@ int wrap(int i, int max); */ int numlen(int n); +/** + * Get modifier mask from modifier name. + * + * Returns the modifer mask or 0 if the name isn't found. + */ +uint32_t get_modifier_mask_by_name(const char *name); + +/** + * Get modifier name from modifier mask. + * + * Returns the modifier name or NULL if it isn't found. + */ +const char *get_modifier_name_by_mask(uint32_t modifier); + #endif diff --git a/sway/commands.c b/sway/commands.c index 9cc33b9c..4e5bc712 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -99,22 +99,6 @@ static sway_cmd bar_colors_cmd_urgent_workspace; swayc_t *sp_view; int sp_index = 0; -static struct modifier_key { - char *name; - uint32_t mod; -} modifiers[] = { - { XKB_MOD_NAME_SHIFT, WLC_BIT_MOD_SHIFT }, - { XKB_MOD_NAME_CAPS, WLC_BIT_MOD_CAPS }, - { XKB_MOD_NAME_CTRL, WLC_BIT_MOD_CTRL }, - { "Ctrl", WLC_BIT_MOD_CTRL }, - { XKB_MOD_NAME_ALT, WLC_BIT_MOD_ALT }, - { "Alt", WLC_BIT_MOD_ALT }, - { XKB_MOD_NAME_NUM, WLC_BIT_MOD_MOD2 }, - { "Mod3", WLC_BIT_MOD_MOD3 }, - { XKB_MOD_NAME_LOGO, WLC_BIT_MOD_LOGO }, - { "Mod5", WLC_BIT_MOD_MOD5 }, -}; - static char *bg_options[] = { "stretch", "center", @@ -187,16 +171,11 @@ static struct cmd_results *cmd_bindsym(int argc, char **argv) { list_t *split = split_string(argv[0], "+"); for (int i = 0; i < split->length; ++i) { // Check for a modifier key - int j; - bool is_mod = false; - for (j = 0; j < (int)(sizeof(modifiers) / sizeof(struct modifier_key)); ++j) { - if (strcasecmp(modifiers[j].name, split->items[i]) == 0) { - binding->modifiers |= modifiers[j].mod; - is_mod = true; - break; - } + uint32_t mod; + if ((mod = get_modifier_mask_by_name(split->items[i])) > 0) { + binding->modifiers |= mod; + continue; } - if (is_mod) continue; // Check for xkb key xkb_keysym_t sym = xkb_keysym_from_name(split->items[i], XKB_KEYSYM_CASE_INSENSITIVE); if (!sym) { @@ -408,17 +387,13 @@ static struct cmd_results *cmd_floating_mod(int argc, char **argv) { if ((error = checkarg(argc, "floating_modifier", EXPECTED_AT_LEAST, 1))) { return error; } - int i, j; + int i; list_t *split = split_string(argv[0], "+"); config->floating_mod = 0; // set modifier keys for (i = 0; i < split->length; ++i) { - for (j = 0; j < (int)(sizeof(modifiers) / sizeof(struct modifier_key)); ++j) { - if (strcasecmp(modifiers[j].name, split->items[i]) == 0) { - config->floating_mod |= modifiers[j].mod; - } - } + config->floating_mod |= get_modifier_mask_by_name(split->items[i]); } free_flat_list(split); if (!config->floating_mod) { @@ -1776,6 +1751,9 @@ static struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) { } } + // active bar modifiers might have changed. + update_active_bar_modifiers(); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } @@ -1800,6 +1778,9 @@ static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode if (strcmp(old_mode, bar->mode) != 0) { if (!config->reading) { ipc_event_barconfig_update(bar); + + // active bar modifiers might have changed. + update_active_bar_modifiers(); } sway_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id); } @@ -1893,16 +1874,11 @@ static struct cmd_results *bar_cmd_modifier(int argc, char **argv) { list_t *split = split_string(argv[0], "+"); for (int i = 0; i < split->length; ++i) { - int j; - bool is_mod = false; - for (j = 0; j < (int)(sizeof(modifiers) / sizeof(struct modifier_key)); ++j) { - if (strcasecmp(modifiers[j].name, split->items[i]) == 0) { - mod |= modifiers[j].mod; - is_mod = true; - break; - } - } - if (!is_mod) { + uint32_t tmp_mod; + if ((tmp_mod = get_modifier_mask_by_name(split->items[i])) > 0) { + mod |= tmp_mod; + continue; + } else { free_flat_list(split); return cmd_results_new(CMD_INVALID, "modifier", "Unknown modifier '%s'", split->items[i]); } diff --git a/sway/config.c b/sway/config.c index 1973de02..95d8f339 100644 --- a/sway/config.c +++ b/sway/config.c @@ -103,6 +103,8 @@ static void free_config(struct sway_config *config) { free_output_config(config->output_configs->items[i]); } list_free(config->output_configs); + + list_free(config->active_bar_modifiers); free(config); } @@ -145,6 +147,33 @@ static void config_defaults(struct sway_config *config) { config->edge_gaps = true; config->gaps_inner = 0; config->gaps_outer = 0; + + config->active_bar_modifiers = create_list(); +} + +static int compare_modifiers(const void *left, const void *right) { + uint32_t a = *(uint32_t *)left; + uint32_t b = *(uint32_t *)right; + + return a - b; +} + +void update_active_bar_modifiers() { + if (config->active_bar_modifiers->length > 0) { + list_free(config->active_bar_modifiers); + config->active_bar_modifiers = create_list(); + } + + struct bar_config *bar; + int i; + for (i = 0; i < config->bars->length; ++i) { + bar = config->bars->items[i]; + if (strcmp(bar->mode, "hide") == 0 && strcmp(bar->hidden_state, "hide") == 0) { + if (list_seq_find(config->active_bar_modifiers, compare_modifiers, &bar->modifier) < 0) { + list_add(config->active_bar_modifiers, &bar->modifier); + } + } + } } static char *get_config_path(void) { @@ -215,6 +244,8 @@ bool load_config(const char *file) { } fclose(f); + update_active_bar_modifiers(); + return config_load_success; } @@ -695,7 +726,7 @@ struct bar_config *default_bar_config(void) { bar = malloc(sizeof(struct bar_config)); bar->mode = strdup("dock"); bar->hidden_state = strdup("hide"); - bar->modifier = 0; + bar->modifier = WLC_BIT_MOD_LOGO; bar->outputs = NULL; bar->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM; bar->bindings = create_list(); diff --git a/sway/handlers.c b/sway/handlers.c index 3cc5cf3e..470f3c95 100644 --- a/sway/handlers.c +++ b/sway/handlers.c @@ -20,6 +20,7 @@ #include "resize.h" #include "extensions.h" #include "criteria.h" +#include "ipc-server.h" // Event should be sent to client #define EVENT_PASSTHROUGH false @@ -391,6 +392,23 @@ static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifier } } } + + // handle bar modifiers pressed/released + uint32_t modifier; + for (i = 0; i < config->active_bar_modifiers->length; ++i) { + modifier = *(uint32_t *)config->active_bar_modifiers->items[i]; + + switch (modifier_state_changed(modifiers->mods, modifier)) { + case MOD_STATE_PRESSED: + ipc_event_modifier(modifier, "pressed"); + break; + case MOD_STATE_RELEASED: + ipc_event_modifier(modifier, "released"); + break; + } + } + // update modifiers state + modifiers_state_update(modifiers->mods); return EVENT_PASSTHROUGH; } diff --git a/sway/input_state.c b/sway/input_state.c index 58619d1f..2f40b6c2 100644 --- a/sway/input_state.c +++ b/sway/input_state.c @@ -22,12 +22,36 @@ struct key_state { static struct key_state key_state_array[KEY_STATE_MAX_LENGTH]; +static uint32_t modifiers_state; + void input_init(void) { int i; for (i = 0; i < KEY_STATE_MAX_LENGTH; ++i) { struct key_state none = { 0, 0, 0 }; key_state_array[i] = none; } + + modifiers_state = 0; +} + +uint32_t modifier_state_changed(uint32_t new_state, uint32_t mod) { + if ((new_state & mod) != 0) { // pressed + if ((modifiers_state & mod) != 0) { // already pressed + return MOD_STATE_UNCHANGED; + } else { // pressed + return MOD_STATE_PRESSED; + } + } else { // not pressed + if ((modifiers_state & mod) != 0) { // released + return MOD_STATE_RELEASED; + } else { // already released + return MOD_STATE_UNCHANGED; + } + } +} + +void modifiers_state_update(uint32_t new_state) { + modifiers_state = new_state; } static uint8_t find_key(uint32_t key_sym, uint32_t key_code, bool update) { diff --git a/sway/ipc-server.c b/sway/ipc-server.c index ed3977d5..f3a4647b 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -19,6 +19,7 @@ #include "commands.h" #include "list.h" #include "stringop.h" +#include "util.h" static int ipc_socket = -1; static struct wlc_event_source *ipc_event_source = NULL; @@ -295,6 +296,8 @@ void ipc_client_handle_command(struct ipc_client *client) { client->subscribed_events |= IPC_EVENT_BARCONFIG_UPDATE; } else if (strcmp(event_type, "mode") == 0) { client->subscribed_events |= IPC_EVENT_MODE; + } else if (strcmp(event_type, "modifier") == 0) { + client->subscribed_events |= IPC_EVENT_MODIFIER; } else { ipc_send_reply(client, "{\"success\": false}", 18); ipc_client_disconnect(client); @@ -508,7 +511,7 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) { json_object_object_add(json, "tray_output", NULL); json_object_object_add(json, "mode", json_object_new_string(bar->mode)); json_object_object_add(json, "hidden_state", json_object_new_string(bar->hidden_state)); - //json_object_object_add(json, "modifier", json_object_new_string(bar->modifier)); // TODO: Fix modifier + json_object_object_add(json, "modifier", json_object_new_string(get_modifier_name_by_mask(bar->modifier))); switch (bar->position) { case DESKTOP_SHELL_PANEL_POSITION_TOP: json_object_object_add(json, "position", json_object_new_string("top")); @@ -617,3 +620,16 @@ void ipc_event_mode(const char *mode) { json_object_put(obj); // free } + +void ipc_event_modifier(uint32_t modifier, const char *state) { + json_object *obj = json_object_new_object(); + json_object_object_add(obj, "change", json_object_new_string(state)); + + const char *modifier_name = get_modifier_name_by_mask(modifier); + json_object_object_add(obj, "modifier", json_object_new_string(modifier_name)); + + const char *json_string = json_object_to_json_string(obj); + ipc_send_event(json_string, IPC_EVENT_MODIFIER); + + json_object_put(obj); // free +}