From 45feaac54c558459ab6396eed3fec0cfcb84a9c3 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 6 Jun 2020 20:30:29 +0200 Subject: [PATCH] Introduce GET_BINDING_STATE IPC command fixes #3892 --- AnyEvent-I3/lib/AnyEvent/I3.pm | 4 ++- docs/ipc | 14 ++++++++++ include/i3.h | 1 + include/i3/ipc.h | 4 +++ src/bindings.c | 1 + src/config.c | 1 + src/ipc.c | 21 ++++++++++++++- src/main.c | 1 + testcases/t/311-get-binding-modes.t | 41 +++++++++++++++++++++++++++++ 9 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 testcases/t/311-get-binding-modes.t diff --git a/AnyEvent-I3/lib/AnyEvent/I3.pm b/AnyEvent-I3/lib/AnyEvent/I3.pm index ae9e5bea2..1f4e5bd3d 100644 --- a/AnyEvent-I3/lib/AnyEvent/I3.pm +++ b/AnyEvent-I3/lib/AnyEvent/I3.pm @@ -101,11 +101,13 @@ use constant TYPE_GET_BINDING_MODES => 8; use constant TYPE_GET_CONFIG => 9; use constant TYPE_SEND_TICK => 10; use constant TYPE_SYNC => 11; +use constant TYPE_GET_BINDING_STATE => 12; our %EXPORT_TAGS = ( 'all' => [ qw(i3 TYPE_RUN_COMMAND TYPE_COMMAND TYPE_GET_WORKSPACES TYPE_SUBSCRIBE TYPE_GET_OUTPUTS TYPE_GET_TREE TYPE_GET_MARKS TYPE_GET_BAR_CONFIG TYPE_GET_VERSION - TYPE_GET_BINDING_MODES TYPE_GET_CONFIG TYPE_SEND_TICK TYPE_SYNC) + TYPE_GET_BINDING_MODES TYPE_GET_CONFIG TYPE_SEND_TICK TYPE_SYNC + TYPE_GET_BINDING_STATE) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } ); diff --git a/docs/ipc b/docs/ipc index 5bc40e264..6cd43f588 100644 --- a/docs/ipc +++ b/docs/ipc @@ -66,6 +66,7 @@ to do that). | 9 | +GET_CONFIG+ | <<_config_reply,CONFIG>> | Returns the last loaded i3 config. | 10 | +SEND_TICK+ | <<_tick_reply,TICK>> | Sends a tick event with the specified payload. | 11 | +SYNC+ | <<_sync_reply,SYNC>> | Sends an i3 sync event with the specified random value to the specified window. +| 12 | +GET_BINDING_STATE+ | <<_binding_state_reply,BINDING_STATE>> | Request the current binding state, e.g. the currently active binding mode name. |====================================================== So, a typical message could look like this: @@ -131,6 +132,8 @@ GET_CONFIG (9):: Reply to the GET_CONFIG message. TICK (10):: Reply to the SEND_TICK message. +GET_BINDING_STATE (12):: + Reply to the GET_BINDING_STATE message. [[_command_reply]] === COMMAND reply @@ -709,6 +712,17 @@ responded to. { "success": true } ------------------- +[[_binding_state_reply]] +=== GET_BINDING_STATE reply + +The binding_state reply is a map which currently only contains the "name" +member, which is the name of the currently active binding mode as a string. + +*Example:* +------------------- +{ "name": "default" } +------------------- + == Events [[events]] diff --git a/include/i3.h b/include/i3.h index aef34715c..158c032d6 100644 --- a/include/i3.h +++ b/include/i3.h @@ -58,6 +58,7 @@ extern char **start_argv; extern Display *xlibdpy, *xkbdpy; extern int xkb_current_group; extern TAILQ_HEAD(bindings_head, Binding) * bindings; +extern const char *current_binding_mode; extern TAILQ_HEAD(autostarts_head, Autostart) autostarts; extern TAILQ_HEAD(autostarts_always_head, Autostart) autostarts_always; extern TAILQ_HEAD(ws_assignments_head, Workspace_Assignment) ws_assignments; diff --git a/include/i3/ipc.h b/include/i3/ipc.h index 884a0cf63..187640cdc 100644 --- a/include/i3/ipc.h +++ b/include/i3/ipc.h @@ -66,6 +66,9 @@ typedef struct i3_ipc_header { /** Trigger an i3 sync protocol message via IPC. */ #define I3_IPC_MESSAGE_TYPE_SYNC 11 +/** Request the current binding state. */ +#define I3_IPC_MESSAGE_TYPE_GET_BINDING_STATE 12 + /* * Messages from i3 to clients * @@ -82,6 +85,7 @@ typedef struct i3_ipc_header { #define I3_IPC_REPLY_TYPE_CONFIG 9 #define I3_IPC_REPLY_TYPE_TICK 10 #define I3_IPC_REPLY_TYPE_SYNC 11 +#define I3_IPC_REPLY_TYPE_GET_BINDING_STATE 12 /* * Events from i3 to clients. Events have the first bit set high. diff --git a/src/bindings.c b/src/bindings.c index e29f2c2dc..d6255e735 100644 --- a/src/bindings.c +++ b/src/bindings.c @@ -628,6 +628,7 @@ void switch_mode(const char *new_mode) { ungrab_all_keys(conn); bindings = mode->bindings; + current_binding_mode = mode->name; translate_keysyms(); grab_all_keys(conn); diff --git a/src/config.c b/src/config.c index 73ef5be5f..ecc154c61 100644 --- a/src/config.c +++ b/src/config.c @@ -174,6 +174,7 @@ bool load_configuration(const char *override_configpath, config_load_t load_type SLIST_INSERT_HEAD(&modes, default_mode, modes); bindings = default_mode->bindings; + current_binding_mode = default_mode->name; /* Clear the old config or initialize the data structure */ memset(&config, 0, sizeof(config)); diff --git a/src/ipc.c b/src/ipc.c index e616a53e1..70b4f248b 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -1337,9 +1337,27 @@ IPC_HANDLER(sync) { ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (const uint8_t *)reply); } +IPC_HANDLER(get_binding_state) { + yajl_gen gen = ygenalloc(); + + y(map_open); + + ystr("name"); + ystr(current_binding_mode); + + y(map_close); + + const unsigned char *payload; + ylength length; + y(get_buf, &payload, &length); + + ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_GET_BINDING_STATE, payload); + y(free); +} + /* The index of each callback function corresponds to the numeric * value of the message type (see include/i3/ipc.h) */ -handler_t handlers[12] = { +handler_t handlers[13] = { handle_run_command, handle_get_workspaces, handle_subscribe, @@ -1352,6 +1370,7 @@ handler_t handlers[12] = { handle_get_config, handle_send_tick, handle_sync, + handle_get_binding_state, }; /* diff --git a/src/main.c b/src/main.c index 83b3e1819..220195597 100644 --- a/src/main.c +++ b/src/main.c @@ -79,6 +79,7 @@ const int default_shmlog_size = 25 * 1024 * 1024; /* The list of key bindings */ struct bindings_head *bindings; +const char *current_binding_mode = NULL; /* The list of exec-lines */ struct autostarts_head autostarts = TAILQ_HEAD_INITIALIZER(autostarts); diff --git a/testcases/t/311-get-binding-modes.t b/testcases/t/311-get-binding-modes.t new file mode 100644 index 000000000..3a00f6951 --- /dev/null +++ b/testcases/t/311-get-binding-modes.t @@ -0,0 +1,41 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • https://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • https://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • https://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Verifies the GET_BINDING_MODE IPC command +# Ticket: #3892 +# Bug still in: 4.18-318-g50160eb1 +use i3test i3_config => <connect->recv; +# TODO: use the symbolic name for the command/reply type instead of the +# numerical 12: +my $binding_state = $i3->message(12, "")->recv; +is($binding_state->{name}, 'default', 'at startup, binding mode is default'); + +cmd 'mode extra'; + +$binding_state = $i3->message(12, "")->recv; +is($binding_state->{name}, 'extra', 'after switching, binding mode is extra'); + +done_testing;