diff --git a/AnyEvent-I3/lib/AnyEvent/I3.pm b/AnyEvent-I3/lib/AnyEvent/I3.pm index 198c41c9..ae9e5bea 100644 --- a/AnyEvent-I3/lib/AnyEvent/I3.pm +++ b/AnyEvent-I3/lib/AnyEvent/I3.pm @@ -100,11 +100,12 @@ use constant TYPE_GET_VERSION => 7; use constant TYPE_GET_BINDING_MODES => 8; use constant TYPE_GET_CONFIG => 9; use constant TYPE_SEND_TICK => 10; +use constant TYPE_SYNC => 11; 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_GET_BINDING_MODES TYPE_GET_CONFIG TYPE_SEND_TICK TYPE_SYNC) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } ); @@ -534,6 +535,19 @@ sub send_tick { $self->message(TYPE_SEND_TICK, $payload); } +=head2 sync + +Sends an i3 sync event. Requires i3 >= 4.16 + +=cut +sub sync { + my ($self, $payload) = @_; + + $self->_ensure_connection; + + $self->message(TYPE_SYNC, $payload); +} + =head2 command($content) Makes i3 execute the given command diff --git a/docs/ipc b/docs/ipc index 8b767ade..f011e773 100644 --- a/docs/ipc +++ b/docs/ipc @@ -65,6 +65,7 @@ to do that). | 8 | +GET_BINDING_MODES+ | <<_binding_modes_reply,BINDING_MODES>> | Gets the names of all currently configured binding modes. | 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. |====================================================== So, a typical message could look like this: @@ -654,6 +655,18 @@ events generated prior to the +SEND_TICK+ message (happened-before relation). { "success": true } ------------------- +[[_sync_reply]] +=== SYNC reply + +The reply is a map containing the "success" member. After the reply was +received, the https://i3wm.org/docs/testsuite.html#i3_sync[i3 sync message] was +responded to. + +*Example:* +------------------- +{ "success": true } +------------------- + == Events [[events]] diff --git a/include/i3/ipc.h b/include/i3/ipc.h index 9e0280c9..0c57f7fd 100644 --- a/include/i3/ipc.h +++ b/include/i3/ipc.h @@ -63,6 +63,9 @@ typedef struct i3_ipc_header { /** Send a tick event to all subscribers. */ #define I3_IPC_MESSAGE_TYPE_SEND_TICK 10 +/** Trigger an i3 sync protocol message via IPC. */ +#define I3_IPC_MESSAGE_TYPE_SYNC 11 + /* * Messages from i3 to clients * @@ -78,6 +81,7 @@ typedef struct i3_ipc_header { #define I3_IPC_REPLY_TYPE_BINDING_MODES 8 #define I3_IPC_REPLY_TYPE_CONFIG 9 #define I3_IPC_REPLY_TYPE_TICK 10 +#define I3_IPC_REPLY_TYPE_SYNC 11 /* * Events from i3 to clients. Events have the first bit set high. diff --git a/src/ipc.c b/src/ipc.c index 6b6383ec..d422217e 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -1173,9 +1173,68 @@ IPC_HANDLER(send_tick) { DLOG("Sent tick event\n"); } +struct sync_state { + char *last_key; + uint32_t rnd; + xcb_window_t window; +}; + +static int _sync_json_key(void *extra, const unsigned char *val, size_t len) { + struct sync_state *state = extra; + FREE(state->last_key); + state->last_key = scalloc(len + 1, 1); + memcpy(state->last_key, val, len); + return 1; +} + +static int _sync_json_int(void *extra, long long val) { + struct sync_state *state = extra; + if (strcasecmp(state->last_key, "rnd") == 0) { + state->rnd = val; + } else if (strcasecmp(state->last_key, "window") == 0) { + state->window = (xcb_window_t)val; + } + return 1; +} + +IPC_HANDLER(sync) { + yajl_handle p; + yajl_status stat; + + /* Setup the JSON parser */ + static yajl_callbacks callbacks = { + .yajl_map_key = _sync_json_key, + .yajl_integer = _sync_json_int, + }; + + struct sync_state state; + memset(&state, '\0', sizeof(struct sync_state)); + p = yalloc(&callbacks, (void *)&state); + stat = yajl_parse(p, (const unsigned char *)message, message_size); + FREE(state.last_key); + if (stat != yajl_status_ok) { + unsigned char *err; + err = yajl_get_error(p, true, (const unsigned char *)message, + message_size); + ELOG("YAJL parse error: %s\n", err); + yajl_free_error(p, err); + + const char *reply = "{\"success\":false}"; + ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (const uint8_t *)reply); + yajl_free(p); + return; + } + yajl_free(p); + + DLOG("received IPC sync request (rnd = %d, window = 0x%08x)\n", state.rnd, state.window); + sync_respond(state.window, state.rnd); + const char *reply = "{\"success\":true}"; + ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (const uint8_t *)reply); +} + /* The index of each callback function corresponds to the numeric * value of the message type (see include/i3/ipc.h) */ -handler_t handlers[11] = { +handler_t handlers[12] = { handle_run_command, handle_get_workspaces, handle_subscribe, @@ -1187,6 +1246,7 @@ handler_t handlers[11] = { handle_get_binding_modes, handle_get_config, handle_send_tick, + handle_sync, }; /*