Skip to content

Commit

Permalink
introduce the sync IPC command
Browse files Browse the repository at this point in the history
Sending the sync command via IPC ensures pending IPC messages are handled by i3
before the sync response is read. This is rarely useful for direct IPC
connections to i3, but becomes useful when synchronizing with i3bar, which might
have pending IPC messages in response to button clicks.
  • Loading branch information
stapelberg committed Mar 30, 2018
1 parent 725ee3c commit eca8fae
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 2 deletions.
16 changes: 15 additions & 1 deletion AnyEvent-I3/lib/AnyEvent/I3.pm
Expand Up @@ -100,11 +100,12 @@ use constant TYPE_GET_VERSION => 7;
use constant TYPE_GET_BINDING_MODES => 8; use constant TYPE_GET_BINDING_MODES => 8;
use constant TYPE_GET_CONFIG => 9; use constant TYPE_GET_CONFIG => 9;
use constant TYPE_SEND_TICK => 10; use constant TYPE_SEND_TICK => 10;
use constant TYPE_SYNC => 11;


our %EXPORT_TAGS = ( 'all' => [ our %EXPORT_TAGS = ( 'all' => [
qw(i3 TYPE_RUN_COMMAND TYPE_COMMAND TYPE_GET_WORKSPACES TYPE_SUBSCRIBE TYPE_GET_OUTPUTS 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_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} } ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } );
Expand Down Expand Up @@ -534,6 +535,19 @@ sub send_tick {
$self->message(TYPE_SEND_TICK, $payload); $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) =head2 command($content)
Makes i3 execute the given command Makes i3 execute the given command
Expand Down
13 changes: 13 additions & 0 deletions docs/ipc
Expand Up @@ -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. | 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. | 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. | 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: So, a typical message could look like this:
Expand Down Expand Up @@ -654,6 +655,18 @@ events generated prior to the +SEND_TICK+ message (happened-before relation).
{ "success": true } { "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


[[events]] [[events]]
Expand Down
4 changes: 4 additions & 0 deletions include/i3/ipc.h
Expand Up @@ -63,6 +63,9 @@ typedef struct i3_ipc_header {
/** Send a tick event to all subscribers. */ /** Send a tick event to all subscribers. */
#define I3_IPC_MESSAGE_TYPE_SEND_TICK 10 #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 * Messages from i3 to clients
* *
Expand All @@ -78,6 +81,7 @@ typedef struct i3_ipc_header {
#define I3_IPC_REPLY_TYPE_BINDING_MODES 8 #define I3_IPC_REPLY_TYPE_BINDING_MODES 8
#define I3_IPC_REPLY_TYPE_CONFIG 9 #define I3_IPC_REPLY_TYPE_CONFIG 9
#define I3_IPC_REPLY_TYPE_TICK 10 #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. * Events from i3 to clients. Events have the first bit set high.
Expand Down
62 changes: 61 additions & 1 deletion src/ipc.c
Expand Up @@ -1173,9 +1173,68 @@ IPC_HANDLER(send_tick) {
DLOG("Sent tick event\n"); 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 /* The index of each callback function corresponds to the numeric
* value of the message type (see include/i3/ipc.h) */ * value of the message type (see include/i3/ipc.h) */
handler_t handlers[11] = { handler_t handlers[12] = {
handle_run_command, handle_run_command,
handle_get_workspaces, handle_get_workspaces,
handle_subscribe, handle_subscribe,
Expand All @@ -1187,6 +1246,7 @@ handler_t handlers[11] = {
handle_get_binding_modes, handle_get_binding_modes,
handle_get_config, handle_get_config,
handle_send_tick, handle_send_tick,
handle_sync,
}; };


/* /*
Expand Down

0 comments on commit eca8fae

Please sign in to comment.