From b77ad945be898cf5bd4b1caaa04c8cdd9944f147 Mon Sep 17 00:00:00 2001 From: Ronan Pigott Date: Thu, 16 Jul 2020 12:11:09 -0700 Subject: [PATCH 1/2] ipc: add query for mode bindings --- include/sway/ipc-json.h | 2 + sway/ipc-json.c | 191 ++++++++++++++++++++++++++++++++++++++++ sway/ipc-server.c | 115 ++++++++---------------- 3 files changed, 232 insertions(+), 76 deletions(-) diff --git a/include/sway/ipc-json.h b/include/sway/ipc-json.h index bc9f4985..96d69858 100644 --- a/include/sway/ipc-json.h +++ b/include/sway/ipc-json.h @@ -8,6 +8,8 @@ json_object *ipc_json_get_version(void); json_object *ipc_json_get_binding_mode(void); +json_object *ipc_json_describe_binding(struct sway_binding *binding); +json_object *ipc_json_describe_binding_mode(struct sway_mode *mode); json_object *ipc_json_describe_disabled_output(struct sway_output *o); json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop *o); diff --git a/sway/ipc-json.c b/sway/ipc-json.c index d3adedd4..679d5270 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -19,6 +19,7 @@ #include "sway/output.h" #include "sway/input/input-manager.h" #include "sway/input/cursor.h" +#include "sway/input/keyboard.h" #include "sway/input/seat.h" #include "wlr-layer-shell-unstable-v1-protocol.h" #include "sway/desktop/idle_inhibit_v1.h" @@ -1468,3 +1469,193 @@ json_object *ipc_json_get_binding_mode(void) { json_object_new_string(config->current_mode->name)); return current_mode; } + +json_object *ipc_json_describe_binding_flags(uint32_t flags) { + json_object *json_flags = json_object_new_array(); + if (flags & BINDING_RELEASE) { + json_object_array_add(json_flags, json_object_new_string("release")); + } else if (flags & BINDING_LOCKED) { + json_object_array_add(json_flags, json_object_new_string("locked")); + } else if (flags & BINDING_BORDER) { + json_object_array_add(json_flags, json_object_new_string("border")); + } else if (flags & BINDING_CONTENTS) { + json_object_array_add(json_flags, json_object_new_string("contents")); + } else if (flags & BINDING_TITLEBAR) { + json_object_array_add(json_flags, json_object_new_string("titlebar")); + } else if (flags & BINDING_CODE) { + json_object_array_add(json_flags, json_object_new_string("code")); + } else if (flags & BINDING_RELOAD) { + json_object_array_add(json_flags, json_object_new_string("reload")); + } else if (flags & BINDING_INHIBITED) { + json_object_array_add(json_flags, json_object_new_string("inhibited")); + } else if (flags & BINDING_NOREPEAT) { + json_object_array_add(json_flags, json_object_new_string("norepeat")); + } + return json_flags; +} + +json_object *ipc_json_describe_switch_binding(struct sway_switch_binding *binding) { + json_object *json_binding = json_object_new_object(); + json_object_object_add(json_binding, "command", json_object_new_string(binding->command)); + + json_object *type = NULL; + switch (binding->type) { + case WLR_SWITCH_TYPE_LID: + type = json_object_new_string("lid"); + break; + case WLR_SWITCH_TYPE_TABLET_MODE: + type = json_object_new_string("tablet_mode"); + break; + } + json_object_object_add(json_binding, "type", type); + + json_object *trigger = NULL; + switch (binding->trigger) { + case SWAY_SWITCH_TRIGGER_OFF: + trigger = json_object_new_string("off"); + break; + case SWAY_SWITCH_TRIGGER_ON: + trigger = json_object_new_string("on"); + break; + case SWAY_SWITCH_TRIGGER_TOGGLE: + trigger = json_object_new_string("on"); + break; + } + json_object_object_add(json_binding, "trigger", trigger); + + json_object *flags = ipc_json_describe_binding_flags(binding->flags); + json_object_object_add(json_binding, "flags", flags); + + return json_binding; +} + +json_object *ipc_json_describe_binding(struct sway_binding *binding) { + json_object *json_binding = json_object_new_object(); + json_object_object_add(json_binding, "command", json_object_new_string(binding->command)); + + const char *names[10]; + int len = get_modifier_names(names, binding->modifiers); + json_object *modifiers = json_object_new_array(); + for (int i = 0; i < len; ++i) { + json_object_array_add(modifiers, json_object_new_string(names[i])); + } + json_object_object_add(json_binding, "modifiers", modifiers); + + json_object *input_codes = json_object_new_array(); + int input_code = 0; + json_object *symbols = json_object_new_array(); + json_object *symbol = NULL; + + json_object *binding_flags = ipc_json_describe_binding_flags(binding->flags); + json_object_object_add(json_binding, "flags", binding_flags); + + switch (binding->type) { + case BINDING_KEYCODE: + json_object_object_add(json_binding, "type", json_object_new_string("keycode")); + // bindcode: populate input_codes + uint32_t keycode; + for (int i = 0; i < binding->keys->length; ++i) { + keycode = *(uint32_t *)binding->keys->items[i]; + json_object_array_add(input_codes, json_object_new_int(keycode)); + if (i == 0) { + input_code = keycode; + } + } + break; + + case BINDING_KEYSYM: + json_object_object_add(json_binding, "type", json_object_new_string("keysym")); + goto symbols; + case BINDING_MOUSESYM: + json_object_object_add(json_binding, "type", json_object_new_string("mousesym")); + goto symbols; + case BINDING_MOUSECODE: + json_object_object_add(json_binding, "type", json_object_new_string("mousecode")); + symbols:; // bindsym/mouse: populate symbols + uint32_t keysym; + char buffer[64]; + for (int i = 0; i < binding->keys->length; ++i) { + keysym = *(uint32_t *)binding->keys->items[i]; + if (keysym >= BTN_LEFT && keysym <= BTN_LEFT + 8) { + snprintf(buffer, 64, "button%u", keysym - BTN_LEFT + 1); + } else if (xkb_keysym_get_name(keysym, buffer, 64) < 0) { + continue; + } + + json_object *str = json_object_new_string(buffer); + if (i == 0) { + // str is owned by both symbol and symbols. Make sure + // to bump the ref count. + json_object_array_add(symbols, json_object_get(str)); + symbol = str; + } else { + json_object_array_add(symbols, str); + } + } + break; + + default: + json_object_put(input_codes); + json_object_put(symbols); + json_object_put(json_binding); + return NULL; // do not send any event + } + + json_object_object_add(json_binding, "input_codes", input_codes); + 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); + + 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_object_add(json_binding, "input_device", + json_object_new_string(binding->input)); + + return json_binding; +} + +json_object *ipc_json_describe_binding_mode(struct sway_mode *mode) { + json_object *json_mode = json_object_new_object(); + json_object_object_add(json_mode, "name", json_object_new_string(mode->name)); + + json_object *bindings = json_object_new_array(); + for (int i = 0; i < mode->keysym_bindings->length; i++) { + struct sway_binding *binding = mode->keysym_bindings->items[i]; + json_object *json_binding = ipc_json_describe_binding(binding); + if (json_binding) { + json_object_array_add(bindings, json_binding); + } + } + + for (int i = 0; i < mode->keycode_bindings->length; i++) { + struct sway_binding *binding = mode->keycode_bindings->items[i]; + json_object *json_binding = ipc_json_describe_binding(binding); + if (json_binding) { + json_object_array_add(bindings, json_binding); + } + } + + for (int i = 0; i < mode->mouse_bindings->length; i++) { + struct sway_binding *binding = mode->mouse_bindings->items[i]; + json_object *json_binding = ipc_json_describe_binding(binding); + if (json_binding) { + json_object_array_add(bindings, json_binding); + } + } + + for (int i = 0; i < mode->switch_bindings->length; i++) { + struct sway_switch_binding *binding = mode->switch_bindings->items[i]; + json_object *json_binding = ipc_json_describe_switch_binding(binding); + if (json_binding) { + json_object_array_add(bindings, json_binding); + } + } + + json_object_object_add(json_mode, "bindings", bindings); + + return json_mode; +} diff --git a/sway/ipc-server.c b/sway/ipc-server.c index b934bb56..716e3686 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -397,77 +397,17 @@ void ipc_event_binding(struct sway_binding *binding) { } sway_log(SWAY_DEBUG, "Sending binding event"); - json_object *json_binding = json_object_new_object(); - json_object_object_add(json_binding, "command", json_object_new_string(binding->command)); - - const char *names[10]; - int len = get_modifier_names(names, binding->modifiers); - json_object *modifiers = json_object_new_array(); - for (int i = 0; i < len; ++i) { - json_object_array_add(modifiers, json_object_new_string(names[i])); - } - json_object_object_add(json_binding, "event_state_mask", modifiers); - - json_object *input_codes = json_object_new_array(); - int input_code = 0; - json_object *symbols = json_object_new_array(); - json_object *symbol = NULL; - - switch (binding->type) { - case BINDING_KEYCODE:; // bindcode: populate input_codes - uint32_t keycode; - for (int i = 0; i < binding->keys->length; ++i) { - keycode = *(uint32_t *)binding->keys->items[i]; - json_object_array_add(input_codes, json_object_new_int(keycode)); - if (i == 0) { - input_code = keycode; - } - } - break; - - case BINDING_KEYSYM: - case BINDING_MOUSESYM: - case BINDING_MOUSECODE:; // bindsym/mouse: populate symbols - uint32_t keysym; - char buffer[64]; - for (int i = 0; i < binding->keys->length; ++i) { - keysym = *(uint32_t *)binding->keys->items[i]; - if (keysym >= BTN_LEFT && keysym <= BTN_LEFT + 8) { - snprintf(buffer, 64, "button%u", keysym - BTN_LEFT + 1); - } else if (xkb_keysym_get_name(keysym, buffer, 64) < 0) { - continue; - } - - json_object *str = json_object_new_string(buffer); - if (i == 0) { - // str is owned by both symbol and symbols. Make sure - // to bump the ref count. - json_object_array_add(symbols, json_object_get(str)); - symbol = str; - } else { - json_object_array_add(symbols, str); - } - } - break; - - default: + json_object *json_binding = ipc_json_describe_binding(binding); + if (!json_binding) { sway_log(SWAY_DEBUG, "Unsupported ipc binding event"); - json_object_put(input_codes); - json_object_put(symbols); - json_object_put(json_binding); return; // do not send any event } - json_object_object_add(json_binding, "input_codes", input_codes); - 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); - - 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")); + // Modifiers are "event_state_mask" in i3 ipc binding event + json_object *modifiers = json_object_object_get(json_binding, "modifiers"); + json_object_get(modifiers); + json_object_object_del(json_binding, "modifiers"); + json_object_object_add(json_binding, "event_state_mask", modifiers); json_object *json = json_object_new_object(); json_object_object_add(json, "change", json_object_new_string("run")); @@ -882,16 +822,39 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt case IPC_GET_BINDING_MODES: { - json_object *modes = json_object_new_array(); - for (int i = 0; i < config->modes->length; i++) { - struct sway_mode *mode = config->modes->items[i]; - json_object_array_add(modes, json_object_new_string(mode->name)); + if (!buf[0]) { + json_object *modes = json_object_new_array(); + for (int i = 0; i < config->modes->length; i++) { + struct sway_mode *mode = config->modes->items[i]; + json_object_array_add(modes, json_object_new_string(mode->name)); + } + const char *json_string = json_object_to_json_string(modes); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); + json_object_put(modes); // free + goto exit_cleanup; + } else { + struct sway_mode *mode = NULL; + for (int i = 0; i < config->modes->length; i++) { + mode = config->modes->items[i]; + if (strcmp(buf, mode->name) == 0) { + break; + } + mode = NULL; + } + if (!mode) { + const char *error = "{ \"success\": false, \"error\": \"No mode with that name\" }"; + ipc_send_reply(client, payload_type, error, + (uint32_t)strlen(error)); + goto exit_cleanup; + } else { + json_object *json_mode = ipc_json_describe_binding_mode(mode); + const char *json_string = json_object_to_json_string(json_mode); + ipc_send_reply(client, payload_type, json_string, (uint32_t)strlen(json_string)); + json_object_put(json_mode); + goto exit_cleanup; + } } - const char *json_string = json_object_to_json_string(modes); - ipc_send_reply(client, payload_type, json_string, - (uint32_t)strlen(json_string)); - json_object_put(modes); // free - goto exit_cleanup; } case IPC_GET_BINDING_STATE: From d1587392de1ba4124f4a3b85c18ba5b39b639105 Mon Sep 17 00:00:00 2001 From: Ronan Pigott Date: Sat, 21 Nov 2020 13:38:54 -0700 Subject: [PATCH 2/2] Document get_binding_modes with payload --- sway/sway-ipc.7.scd | 282 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 281 insertions(+), 1 deletion(-) diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 833db0ef..2f78708c 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -1030,7 +1030,7 @@ An object containing the following properties: } ``` -## 8. GET_BINDING_MODES +## 8. GET_BINDING_MODES (WITHOUT A PAYLOAD) *MESSAGE*++ Retrieve the list of binding modes that currently configured @@ -1048,6 +1048,286 @@ default binding mode ] ``` +## 8. GET_BINDING_MODES (WITH A PAYLOAD) + +*MESSAGE*++ +When sent with a mode name as the payload, this retrieves the list of keybinds +configured for that mode. + +*REPLY*++ +An object that represents the configuration for the mode with the name sent as +the payload. It has the following properties: + + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- name +: string +:[ The mode name +|- bindings +: array +: The list of keybinds for the mode + +The objects in the keybind list describe one keybind each, and have the +following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- type +: string +:[ The type of this keybind. +|- command +: string +: The sway command that is configured to run for this binding. +|- flags +: array +: Flags describing special properties of this binding. + +Bindings of type *keysym*, *keycode*, *mousesym*, and *mousecode* have these +properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +| modifiers +: array +:[ The modifier keys that are configured for this binding +|- input_codes +: array +: For code keybinds, the keycodes or mousecodes that are configured for this + binding +|- input_code +: integer +: The first value of input_codes, or 0 if it is empty. For i3 compatibility. +|- symbols +: array +: For symbol keybinds, the keysyms or mouse buttons that are configured for + this binding. +|- symbol +: string +: The first value of symbols, or null if it is empty. For i3 compatiblity. +|- input_type +: string +: Has the value *keyboard* or *mouse* if the binding is a keysym/keycode or + mousesym/mousecode binding respectively. +|- input_device +: string +: The input identifier of devices that may trigger this binding. + +And bindings of type *lid* have these properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- state +: string +:[ The lid state which triggers this bindings. + +The *flags* property has these values + +[- *FLAG* +:- *DESCRIPTION* +|- release +:[ The binding will run when the keycombo is released +|- locked +: The binding will run even if a screen locking program is active +|- border +: Mouse only. The binding will run when the cursor is over the window border. +|- contents +: Mouse only. The binding will run when the cursor is over the window content. +|- titlebar +: Mouse only. The binding will run when the cursor is over the window titlebar. +|- code +: The code binding was converted from a keysym. +|- reload +: Switch only. The binding will also execute on config reload. +|- inhibited +: The binding will ignore shortcut inhibitors. +|- norepeat +: The binding will not run when repeating a held key. + + +*Example Reply:* +``` +{ + "name": "resize", + "bindings": [ + { + "command": "resize shrink width 10px", + "modifiers": [ + ], + "flags": [ + ], + "type": "keysym", + "input_codes": [ + ], + "input_code": 0, + "symbols": [ + "h" + ], + "symbol": "h", + "input_type": "keyboard", + "input_device": "*" + }, + { + "command": "resize grow height 10px", + "modifiers": [ + ], + "flags": [ + ], + "type": "keysym", + "input_codes": [ + ], + "input_code": 0, + "symbols": [ + "j" + ], + "symbol": "j", + "input_type": "keyboard", + "input_device": "*" + }, + { + "command": "resize shrink height 10px", + "modifiers": [ + ], + "flags": [ + ], + "type": "keysym", + "input_codes": [ + ], + "input_code": 0, + "symbols": [ + "k" + ], + "symbol": "k", + "input_type": "keyboard", + "input_device": "*" + }, + { + "command": "resize grow width 10px", + "modifiers": [ + ], + "flags": [ + ], + "type": "keysym", + "input_codes": [ + ], + "input_code": 0, + "symbols": [ + "l" + ], + "symbol": "l", + "input_type": "keyboard", + "input_device": "*" + }, + { + "command": "resize shrink width 10px", + "modifiers": [ + ], + "flags": [ + ], + "type": "keysym", + "input_codes": [ + ], + "input_code": 0, + "symbols": [ + "Left" + ], + "symbol": "Left", + "input_type": "keyboard", + "input_device": "*" + }, + { + "command": "resize grow height 10px", + "modifiers": [ + ], + "flags": [ + ], + "type": "keysym", + "input_codes": [ + ], + "input_code": 0, + "symbols": [ + "Down" + ], + "symbol": "Down", + "input_type": "keyboard", + "input_device": "*" + }, + { + "command": "resize shrink height 10px", + "modifiers": [ + ], + "flags": [ + ], + "type": "keysym", + "input_codes": [ + ], + "input_code": 0, + "symbols": [ + "Up" + ], + "symbol": "Up", + "input_type": "keyboard", + "input_device": "*" + }, + { + "command": "resize grow width 10px", + "modifiers": [ + ], + "flags": [ + ], + "type": "keysym", + "input_codes": [ + ], + "input_code": 0, + "symbols": [ + "Right" + ], + "symbol": "Right", + "input_type": "keyboard", + "input_device": "*" + }, + { + "command": "mode \"default\"", + "modifiers": [ + ], + "flags": [ + ], + "type": "keysym", + "input_codes": [ + ], + "input_code": 0, + "symbols": [ + "Return" + ], + "symbol": "Return", + "input_type": "keyboard", + "input_device": "*" + }, + { + "command": "mode \"default\"", + "modifiers": [ + ], + "flags": [ + ], + "type": "keysym", + "input_codes": [ + ], + "input_code": 0, + "symbols": [ + "Escape" + ], + "symbol": "Escape", + "input_type": "keyboard", + "input_device": "*" + } + ] +} +``` + ## 9. GET_CONFIG *MESSAGE*++