+
+ if (client->first_tick_sent) {
+ return;
+ }
+
+ bool is_tick = false;
+ for (int i = 0; i < client->num_events; i++) {
+ if (strcmp(client->events[i], "tick") == 0) {
+ is_tick = true;
+ break;
+ }
+ }
+ if (!is_tick) {
+ return;
+ }
+
+ client->first_tick_sent = true;
+ const char *payload = "{\"first\":true,\"payload\":\"\"}";
+ ipc_send_message(client->fd, strlen(payload), I3_IPC_EVENT_TICK, (const uint8_t *)payload);
+}
+
+/*
+ * Returns the raw last loaded i3 configuration file contents.
+ */
+IPC_HANDLER(get_config) {
+ yajl_gen gen = ygenalloc();
+
+ y(map_open);
+
+ ystr("config");
+ ystr(current_config);
+
+ y(map_close);
+
+ const unsigned char *payload;
+ ylength length;
+ y(get_buf, &payload, &length);
+
+ ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_CONFIG, payload);
+ y(free);
+}
+
+/*
+ * Sends the tick event from the message payload to subscribers. Establishes a
+ * synchronization point in event-related tests.
+ */
+IPC_HANDLER(send_tick) {
+ yajl_gen gen = ygenalloc();
+
+ y(map_open);
+
+ ystr("payload");
+ yajl_gen_string(gen, (unsigned char *)message, message_size);
+
+ y(map_close);
+
+ const unsigned char *payload;
+ ylength length;
+ y(get_buf, &payload, &length);
+
+ ipc_send_event("tick", I3_IPC_EVENT_TICK, (const char *)payload);
+ y(free);
+
+ const char *reply = "{\"success\":true}";
+ ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_TICK, (const uint8_t *)reply);
+ 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);