]> git.sur5r.net Git - i3/i3/blobdiff - src/ipc.c
format **/*.c with clang-format-3.5
[i3/i3] / src / ipc.c
index 5e91e21de27e325595602002b32a2c9ce1d76aba..0f3dcd82b7d2b115310a5cbbf012942e6aa769e4 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -1,3 +1,5 @@
+#undef I3__FILE__
+#define I3__FILE__ "ipc.c"
 /*
  * vim:ts=4:sw=4:expandtab
  *
@@ -8,6 +10,7 @@
  *
  */
 #include "all.h"
+#include "yajl_utils.h"
 
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <ev.h>
 #include <yajl/yajl_gen.h>
 #include <yajl/yajl_parse.h>
-#include <yajl/yajl_version.h>
 
 char *current_socketpath = NULL;
 
-/* Shorter names for all those yajl_gen_* functions */
-#define y(x, ...) yajl_gen_ ## x (gen, ##__VA_ARGS__)
-#define ystr(str) yajl_gen_string(gen, (unsigned char*)str, strlen(str))
-
 TAILQ_HEAD(ipc_client_head, ipc_client) all_clients = TAILQ_HEAD_INITIALIZER(all_clients);
 
 /*
@@ -50,10 +48,10 @@ static bool mkdirp(const char *path) {
         ELOG("mkdir(%s) failed: %s\n", path, strerror(errno));
         return false;
     }
-    char *copy = strdup(path);
+    char *copy = sstrdup(path);
     /* strip trailing slashes, if any */
-    while (copy[strlen(copy)-1] == '/')
-        copy[strlen(copy)-1] = '\0';
+    while (copy[strlen(copy) - 1] == '/')
+        copy[strlen(copy) - 1] = '\0';
 
     char *sep = strrchr(copy, '/');
     if (sep == NULL) {
@@ -76,7 +74,7 @@ static bool mkdirp(const char *path) {
  */
 void ipc_send_event(const char *event, uint32_t message_type, const char *payload) {
     ipc_client *current;
-    TAILQ_FOREACH(current, &all_clients, clients) {
+    TAILQ_FOREACH (current, &all_clients, clients) {
         /* see if this client is interested in this event */
         bool interested = false;
         for (int i = 0; i < current->num_events; i++) {
@@ -88,7 +86,7 @@ void ipc_send_event(const char *event, uint32_t message_type, const char *payloa
         if (!interested)
             continue;
 
-        ipc_send_message(current->fd, strlen(payload), message_type, (const uint8_t*)payload);
+        ipc_send_message(current->fd, strlen(payload), message_type, (const uint8_t *)payload);
     }
 }
 
@@ -117,20 +115,26 @@ IPC_HANDLER(command) {
     /* To get a properly terminated buffer, we copy
      * message_size bytes out of the buffer */
     char *command = scalloc(message_size + 1);
-    strncpy(command, (const char*)message, message_size);
+    strncpy(command, (const char *)message, message_size);
     LOG("IPC: received: *%s*\n", command);
-    struct CommandResult *command_output = parse_command((const char*)command);
+    yajl_gen gen = yajl_gen_alloc(NULL);
+
+    CommandResult *result = parse_command((const char *)command, gen);
     free(command);
 
-    if (command_output->needs_tree_render)
+    if (result->needs_tree_render)
         tree_render();
 
-    /* If no reply was provided, we just use the default success message */
-    ipc_send_message(fd, strlen(command_output->json_output),
-                     I3_IPC_REPLY_TYPE_COMMAND,
-                     (const uint8_t*)command_output->json_output);
+    command_result_free(result);
+
+    const unsigned char *reply;
+    ylength length;
+    yajl_gen_get_buf(gen, &reply, &length);
 
-    free(command_output->json_output);
+    ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_COMMAND,
+                     (const uint8_t *)reply);
+
+    yajl_gen_free(gen);
 }
 
 static void dump_rect(yajl_gen gen, const char *name, Rect r) {
@@ -153,19 +157,40 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     y(integer, (long int)con);
 
     ystr("type");
-    y(integer, con->type);
+    switch (con->type) {
+        case CT_ROOT:
+            ystr("root");
+            break;
+        case CT_OUTPUT:
+            ystr("output");
+            break;
+        case CT_CON:
+            ystr("con");
+            break;
+        case CT_FLOATING_CON:
+            ystr("floating_con");
+            break;
+        case CT_WORKSPACE:
+            ystr("workspace");
+            break;
+        case CT_DOCKAREA:
+            ystr("dockarea");
+            break;
+        default:
+            DLOG("About to dump unknown container type=%d. This is a bug.\n", con->type);
+            assert(false);
+            break;
+    }
 
+    /* provided for backwards compatibility only. */
     ystr("orientation");
-    switch (con->orientation) {
-        case NO_ORIENTATION:
-            ystr("none");
-            break;
-        case HORIZ:
+    if (!con_is_split(con))
+        ystr("none");
+    else {
+        if (con_orientation(con) == HORIZ)
             ystr("horizontal");
-            break;
-        case VERT:
+        else
             ystr("vertical");
-            break;
     }
 
     ystr("scratchpad_state");
@@ -184,7 +209,8 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     ystr("percent");
     if (con->percent == 0.0)
         y(null);
-    else y(double, con->percent);
+    else
+        y(double, con->percent);
 
     ystr("urgent");
     y(bool, con->urgent);
@@ -200,7 +226,14 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     ystr("layout");
     switch (con->layout) {
         case L_DEFAULT:
-            ystr("default");
+            DLOG("About to dump layout=default, this is a bug in the code.\n");
+            assert(false);
+            break;
+        case L_SPLITV:
+            ystr("splitv");
+            break;
+        case L_SPLITH:
+            ystr("splith");
             break;
         case L_STACKED:
             ystr("stacked");
@@ -216,6 +249,33 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
             break;
     }
 
+    ystr("workspace_layout");
+    switch (con->workspace_layout) {
+        case L_DEFAULT:
+            ystr("default");
+            break;
+        case L_STACKED:
+            ystr("stacked");
+            break;
+        case L_TABBED:
+            ystr("tabbed");
+            break;
+        default:
+            DLOG("About to dump workspace_layout=%d (none of default/stacked/tabbed), this is a bug.\n", con->workspace_layout);
+            assert(false);
+            break;
+    }
+
+    ystr("last_split_layout");
+    switch (con->layout) {
+        case L_SPLITV:
+            ystr("splitv");
+            break;
+        default:
+            ystr("splith");
+            break;
+    }
+
     ystr("border");
     switch (con->border_style) {
         case BS_NORMAL:
@@ -224,18 +284,21 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
         case BS_NONE:
             ystr("none");
             break;
-        case BS_1PIXEL:
-            ystr("1pixel");
+        case BS_PIXEL:
+            ystr("pixel");
             break;
     }
 
+    ystr("current_border_width");
+    y(integer, con->current_border_width);
+
     dump_rect(gen, "rect", con->rect);
     dump_rect(gen, "window_rect", con->window_rect);
     dump_rect(gen, "geometry", con->geometry);
 
     ystr("name");
-    if (con->window && con->window->name_json)
-        ystr(con->window->name_json);
+    if (con->window && con->window->name)
+        ystr(i3string_as_utf8(con->window->name));
     else
         ystr(con->name);
 
@@ -247,13 +310,41 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     ystr("window");
     if (con->window)
         y(integer, con->window->id);
-    else y(null);
+    else
+        y(null);
+
+    if (con->window && !inplace_restart) {
+        /* Window properties are useless to preserve when restarting because
+         * they will be queried again anyway. However, for i3-save-tree(1),
+         * they are very useful and save i3-save-tree dealing with X11. */
+        ystr("window_properties");
+        y(map_open);
+
+#define DUMP_PROPERTY(key, prop_name)         \
+    do {                                      \
+        if (con->window->prop_name != NULL) { \
+            ystr(key);                        \
+            ystr(con->window->prop_name);     \
+        }                                     \
+    } while (0)
+
+        DUMP_PROPERTY("class", class_class);
+        DUMP_PROPERTY("instance", class_instance);
+        DUMP_PROPERTY("window_role", role);
+
+        if (con->window->name != NULL) {
+            ystr("title");
+            ystr(i3string_as_utf8(con->window->name));
+        }
+
+        y(map_close);
+    }
 
     ystr("nodes");
     y(array_open);
     Con *node;
     if (con->type != CT_DOCKAREA || !inplace_restart) {
-        TAILQ_FOREACH(node, &(con->nodes_head), nodes) {
+        TAILQ_FOREACH (node, &(con->nodes_head), nodes) {
             dump_node(gen, node, inplace_restart);
         }
     }
@@ -261,14 +352,14 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
 
     ystr("floating_nodes");
     y(array_open);
-    TAILQ_FOREACH(node, &(con->floating_head), floating_windows) {
+    TAILQ_FOREACH (node, &(con->floating_head), floating_windows) {
         dump_node(gen, node, inplace_restart);
     }
     y(array_close);
 
     ystr("focus");
     y(array_open);
-    TAILQ_FOREACH(node, &(con->focus_head), focused) {
+    TAILQ_FOREACH (node, &(con->focus_head), focused) {
         y(integer, (long int)node);
     }
     y(array_close);
@@ -295,17 +386,30 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     ystr("swallows");
     y(array_open);
     Match *match;
-    TAILQ_FOREACH(match, &(con->swallow_head), matches) {
+    TAILQ_FOREACH (match, &(con->swallow_head), matches) {
+        y(map_open);
         if (match->dock != -1) {
-            y(map_open);
             ystr("dock");
             y(integer, match->dock);
             ystr("insert_where");
             y(integer, match->insert_where);
-            y(map_close);
         }
 
-        /* TODO: the other swallow keys */
+#define DUMP_REGEX(re_name)                \
+    do {                                   \
+        if (match->re_name != NULL) {      \
+            ystr(#re_name);                \
+            ystr(match->re_name->pattern); \
+        }                                  \
+    } while (0)
+
+        DUMP_REGEX(class);
+        DUMP_REGEX(instance);
+        DUMP_REGEX(window_role);
+        DUMP_REGEX(title);
+
+#undef DUMP_REGEX
+        y(map_close);
     }
 
     if (inplace_restart) {
@@ -320,60 +424,186 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     }
     y(array_close);
 
+    if (inplace_restart && con->window != NULL) {
+        ystr("depth");
+        y(integer, con->depth);
+    }
+
+    y(map_close);
+}
+
+static void dump_bar_config(yajl_gen gen, Barconfig *config) {
+    y(map_open);
+
+    ystr("id");
+    ystr(config->id);
+
+    if (config->num_outputs > 0) {
+        ystr("outputs");
+        y(array_open);
+        for (int c = 0; c < config->num_outputs; c++)
+            ystr(config->outputs[c]);
+        y(array_close);
+    }
+
+#define YSTR_IF_SET(name)       \
+    do {                        \
+        if (config->name) {     \
+            ystr(#name);        \
+            ystr(config->name); \
+        }                       \
+    } while (0)
+
+    YSTR_IF_SET(tray_output);
+    YSTR_IF_SET(socket_path);
+
+    ystr("mode");
+    switch (config->mode) {
+        case M_HIDE:
+            ystr("hide");
+            break;
+        case M_INVISIBLE:
+            ystr("invisible");
+            break;
+        case M_DOCK:
+        default:
+            ystr("dock");
+            break;
+    }
+
+    ystr("hidden_state");
+    switch (config->hidden_state) {
+        case S_SHOW:
+            ystr("show");
+            break;
+        case S_HIDE:
+        default:
+            ystr("hide");
+            break;
+    }
+
+    ystr("modifier");
+    switch (config->modifier) {
+        case M_CONTROL:
+            ystr("ctrl");
+            break;
+        case M_SHIFT:
+            ystr("shift");
+            break;
+        case M_MOD1:
+            ystr("Mod1");
+            break;
+        case M_MOD2:
+            ystr("Mod2");
+            break;
+        case M_MOD3:
+            ystr("Mod3");
+            break;
+        /*
+               case M_MOD4:
+               ystr("Mod4");
+               break;
+               */
+        case M_MOD5:
+            ystr("Mod5");
+            break;
+        default:
+            ystr("Mod4");
+            break;
+    }
+
+    ystr("position");
+    if (config->position == P_BOTTOM)
+        ystr("bottom");
+    else
+        ystr("top");
+
+    YSTR_IF_SET(status_command);
+    YSTR_IF_SET(font);
+
+    ystr("workspace_buttons");
+    y(bool, !config->hide_workspace_buttons);
+
+    ystr("strip_workspace_numbers");
+    y(bool, config->strip_workspace_numbers);
+
+    ystr("binding_mode_indicator");
+    y(bool, !config->hide_binding_mode_indicator);
+
+    ystr("verbose");
+    y(bool, config->verbose);
+
+#undef YSTR_IF_SET
+#define YSTR_IF_SET(name)              \
+    do {                               \
+        if (config->colors.name) {     \
+            ystr(#name);               \
+            ystr(config->colors.name); \
+        }                              \
+    } while (0)
+
+    ystr("colors");
+    y(map_open);
+    YSTR_IF_SET(background);
+    YSTR_IF_SET(statusline);
+    YSTR_IF_SET(separator);
+    YSTR_IF_SET(focused_workspace_border);
+    YSTR_IF_SET(focused_workspace_bg);
+    YSTR_IF_SET(focused_workspace_text);
+    YSTR_IF_SET(active_workspace_border);
+    YSTR_IF_SET(active_workspace_bg);
+    YSTR_IF_SET(active_workspace_text);
+    YSTR_IF_SET(inactive_workspace_border);
+    YSTR_IF_SET(inactive_workspace_bg);
+    YSTR_IF_SET(inactive_workspace_text);
+    YSTR_IF_SET(urgent_workspace_border);
+    YSTR_IF_SET(urgent_workspace_bg);
+    YSTR_IF_SET(urgent_workspace_text);
     y(map_close);
+
+    y(map_close);
+#undef YSTR_IF_SET
 }
 
 IPC_HANDLER(tree) {
     setlocale(LC_NUMERIC, "C");
-#if YAJL_MAJOR >= 2
-    yajl_gen gen = yajl_gen_alloc(NULL);
-#else
-    yajl_gen gen = yajl_gen_alloc(NULL, NULL);
-#endif
+    yajl_gen gen = ygenalloc();
     dump_node(gen, croot, false);
     setlocale(LC_NUMERIC, "");
 
     const unsigned char *payload;
-#if YAJL_MAJOR >= 2
-    size_t length;
-#else
-    unsigned int length;
-#endif
+    ylength length;
     y(get_buf, &payload, &length);
 
     ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_TREE, payload);
     y(free);
 }
 
-
 /*
  * Formats the reply message for a GET_WORKSPACES request and sends it to the
  * client
  *
  */
 IPC_HANDLER(get_workspaces) {
-#if YAJL_MAJOR >= 2
-    yajl_gen gen = yajl_gen_alloc(NULL);
-#else
-    yajl_gen gen = yajl_gen_alloc(NULL, NULL);
-#endif
+    yajl_gen gen = ygenalloc();
     y(array_open);
 
     Con *focused_ws = con_get_workspace(focused);
 
     Con *output;
-    TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
-        if (output->name[0] == '_' && output->name[1] == '_')
+    TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
+        if (con_is_internal(output))
             continue;
         Con *ws;
-        TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
+        TAILQ_FOREACH (ws, &(output_get_content(output)->nodes_head), nodes) {
             assert(ws->type == CT_WORKSPACE);
             y(map_open);
 
             ystr("num");
             if (ws->num == -1)
                 y(null);
-            else y(integer, ws->num);
+            else
+                y(integer, ws->num);
 
             ystr("name");
             ystr(ws->name);
@@ -409,11 +639,7 @@ IPC_HANDLER(get_workspaces) {
     y(array_close);
 
     const unsigned char *payload;
-#if YAJL_MAJOR >= 2
-    size_t length;
-#else
-    unsigned int length;
-#endif
+    ylength length;
     y(get_buf, &payload, &length);
 
     ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_WORKSPACES, payload);
@@ -426,15 +652,11 @@ IPC_HANDLER(get_workspaces) {
  *
  */
 IPC_HANDLER(get_outputs) {
-#if YAJL_MAJOR >= 2
-    yajl_gen gen = yajl_gen_alloc(NULL);
-#else
-    yajl_gen gen = yajl_gen_alloc(NULL, NULL);
-#endif
+    yajl_gen gen = ygenalloc();
     y(array_open);
 
     Output *output;
-    TAILQ_FOREACH(output, &outputs, outputs) {
+    TAILQ_FOREACH (output, &outputs, outputs) {
         y(map_open);
 
         ystr("name");
@@ -462,7 +684,8 @@ IPC_HANDLER(get_outputs) {
         Con *ws = NULL;
         if (output->con && (ws = con_get_fullscreen_con(output->con, CF_OUTPUT)))
             ystr(ws->name);
-        else y(null);
+        else
+            y(null);
 
         y(map_close);
     }
@@ -470,11 +693,7 @@ IPC_HANDLER(get_outputs) {
     y(array_close);
 
     const unsigned char *payload;
-#if YAJL_MAJOR >= 2
-    size_t length;
-#else
-    unsigned int length;
-#endif
+    ylength length;
     y(get_buf, &payload, &length);
 
     ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_OUTPUTS, payload);
@@ -487,59 +706,73 @@ IPC_HANDLER(get_outputs) {
  *
  */
 IPC_HANDLER(get_marks) {
-#if YAJL_MAJOR >= 2
-    yajl_gen gen = yajl_gen_alloc(NULL);
-#else
-    yajl_gen gen = yajl_gen_alloc(NULL, NULL);
-#endif
+    yajl_gen gen = ygenalloc();
     y(array_open);
 
     Con *con;
-    TAILQ_FOREACH(con, &all_cons, all_cons)
+    TAILQ_FOREACH (con, &all_cons, all_cons)
         if (con->mark != NULL)
             ystr(con->mark);
 
     y(array_close);
 
     const unsigned char *payload;
-#if YAJL_MAJOR >= 2
-    size_t length;
-#else
-    unsigned int length;
-#endif
+    ylength length;
     y(get_buf, &payload, &length);
 
     ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_MARKS, payload);
     y(free);
 }
 
+/*
+ * Returns the version of i3
+ *
+ */
+IPC_HANDLER(get_version) {
+    yajl_gen gen = ygenalloc();
+    y(map_open);
+
+    ystr("major");
+    y(integer, MAJOR_VERSION);
+
+    ystr("minor");
+    y(integer, MINOR_VERSION);
+
+    ystr("patch");
+    y(integer, PATCH_VERSION);
+
+    ystr("human_readable");
+    ystr(I3_VERSION);
+
+    y(map_close);
+
+    const unsigned char *payload;
+    ylength length;
+    y(get_buf, &payload, &length);
+
+    ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_VERSION, payload);
+    y(free);
+}
+
 /*
  * Formats the reply message for a GET_BAR_CONFIG request and sends it to the
  * client.
  *
  */
 IPC_HANDLER(get_bar_config) {
-#if YAJL_MAJOR >= 2
-    yajl_gen gen = yajl_gen_alloc(NULL);
-#else
-    yajl_gen gen = yajl_gen_alloc(NULL, NULL);
-#endif
+    yajl_gen gen = ygenalloc();
 
     /* If no ID was passed, we return a JSON array with all IDs */
     if (message_size == 0) {
         y(array_open);
         Barconfig *current;
-        TAILQ_FOREACH(current, &barconfigs, configs) {
+        TAILQ_FOREACH (current, &barconfigs, configs) {
             ystr(current->id);
         }
         y(array_close);
 
         const unsigned char *payload;
-#if YAJL_MAJOR >= 2
-        size_t length;
-#else
-        unsigned int length;
-#endif
+        ylength length;
         y(get_buf, &payload, &length);
 
         ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
@@ -550,10 +783,10 @@ IPC_HANDLER(get_bar_config) {
     /* To get a properly terminated buffer, we copy
      * message_size bytes out of the buffer */
     char *bar_id = scalloc(message_size + 1);
-    strncpy(bar_id, (const char*)message, message_size);
+    strncpy(bar_id, (const char *)message, message_size);
     LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id);
     Barconfig *current, *config = NULL;
-    TAILQ_FOREACH(current, &barconfigs, configs) {
+    TAILQ_FOREACH (current, &barconfigs, configs) {
         if (strcmp(current->id, bar_id) != 0)
             continue;
 
@@ -561,123 +794,21 @@ IPC_HANDLER(get_bar_config) {
         break;
     }
 
-    y(map_open);
-
     if (!config) {
         /* If we did not find a config for the given ID, the reply will contain
          * a null 'id' field. */
+        y(map_open);
+
         ystr("id");
         y(null);
-    } else {
-        ystr("id");
-        ystr(config->id);
-
-        if (config->num_outputs > 0) {
-            ystr("outputs");
-            y(array_open);
-            for (int c = 0; c < config->num_outputs; c++)
-                ystr(config->outputs[c]);
-            y(array_close);
-        }
-
-#define YSTR_IF_SET(name) \
-        do { \
-            if (config->name) { \
-                ystr( # name); \
-                ystr(config->name); \
-            } \
-        } while (0)
 
-        YSTR_IF_SET(tray_output);
-        YSTR_IF_SET(socket_path);
-
-        ystr("mode");
-        if (config->mode == M_HIDE)
-            ystr("hide");
-        else ystr("dock");
-
-        ystr("modifier");
-        switch (config->modifier) {
-            case M_CONTROL:
-                ystr("ctrl");
-                break;
-            case M_SHIFT:
-                ystr("shift");
-                break;
-            case M_MOD1:
-                ystr("Mod1");
-                break;
-            case M_MOD2:
-                ystr("Mod2");
-                break;
-            case M_MOD3:
-                ystr("Mod3");
-                break;
-            /*
-            case M_MOD4:
-                ystr("Mod4");
-                break;
-            */
-            case M_MOD5:
-                ystr("Mod5");
-                break;
-            default:
-                ystr("Mod4");
-                break;
-        }
-
-        ystr("position");
-        if (config->position == P_BOTTOM)
-            ystr("bottom");
-        else ystr("top");
-
-        YSTR_IF_SET(status_command);
-        YSTR_IF_SET(font);
-
-        ystr("workspace_buttons");
-        y(bool, !config->hide_workspace_buttons);
-
-        ystr("verbose");
-        y(bool, config->verbose);
-
-#undef YSTR_IF_SET
-#define YSTR_IF_SET(name) \
-        do { \
-            if (config->colors.name) { \
-                ystr( # name); \
-                ystr(config->colors.name); \
-            } \
-        } while (0)
-
-        ystr("colors");
-        y(map_open);
-        YSTR_IF_SET(background);
-        YSTR_IF_SET(statusline);
-        YSTR_IF_SET(focused_workspace_border);
-        YSTR_IF_SET(focused_workspace_bg);
-        YSTR_IF_SET(focused_workspace_text);
-        YSTR_IF_SET(active_workspace_border);
-        YSTR_IF_SET(active_workspace_bg);
-        YSTR_IF_SET(active_workspace_text);
-        YSTR_IF_SET(inactive_workspace_border);
-        YSTR_IF_SET(inactive_workspace_bg);
-        YSTR_IF_SET(inactive_workspace_text);
-        YSTR_IF_SET(urgent_workspace_border);
-        YSTR_IF_SET(urgent_workspace_bg);
-        YSTR_IF_SET(urgent_workspace_text);
         y(map_close);
-
-#undef YSTR_IF_SET
+    } else {
+        dump_bar_config(gen, config);
     }
 
-    y(map_close);
-
     const unsigned char *payload;
-#if YAJL_MAJOR >= 2
-    size_t length;
-#else
-    unsigned int length;
-#endif
+    ylength length;
     y(get_buf, &payload, &length);
 
     ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
@@ -688,23 +819,18 @@ IPC_HANDLER(get_bar_config) {
  * Callback for the YAJL parser (will be called when a string is parsed).
  *
  */
-#if YAJL_MAJOR < 2
-static int add_subscription(void *extra, const unsigned char *s,
-                            unsigned int len) {
-#else
 static int add_subscription(void *extra, const unsigned char *s,
-                            size_t len) {
-#endif
+                            ylength len) {
     ipc_client *client = extra;
 
-    DLOG("should add subscription to extra %p, sub %.*s\n", client, len, s);
+    DLOG("should add subscription to extra %p, sub %.*s\n", client, (int)len, s);
     int event = client->num_events;
 
     client->num_events++;
-    client->events = realloc(client->events, client->num_events * sizeof(char*));
+    client->events = realloc(client->events, client->num_events * sizeof(char *));
     /* We copy the string because it is not null-terminated and strndup()
      * is missing on some BSD systems */
-    client->events[event] = scalloc(len+1);
+    client->events[event] = scalloc(len + 1);
     memcpy(client->events[event], s, len);
 
     DLOG("client is now subscribed to:\n");
@@ -722,12 +848,11 @@ static int add_subscription(void *extra, const unsigned char *s,
  */
 IPC_HANDLER(subscribe) {
     yajl_handle p;
-    yajl_callbacks callbacks;
     yajl_status stat;
     ipc_client *current, *client = NULL;
 
     /* Search the ipc_client structure for this connection */
-    TAILQ_FOREACH(current, &all_clients, clients) {
+    TAILQ_FOREACH (current, &all_clients, clients) {
         if (current->fd != fd)
             continue;
 
@@ -741,35 +866,32 @@ IPC_HANDLER(subscribe) {
     }
 
     /* Setup the JSON parser */
-    memset(&callbacks, 0, sizeof(yajl_callbacks));
-    callbacks.yajl_string = add_subscription;
-
-#if YAJL_MAJOR >= 2
-    p = yajl_alloc(&callbacks, NULL, (void*)client);
-#else
-    p = yajl_alloc(&callbacks, NULL, NULL, (void*)client);
-#endif
-    stat = yajl_parse(p, (const unsigned char*)message, message_size);
+    static yajl_callbacks callbacks = {
+        .yajl_string = add_subscription,
+    };
+
+    p = yalloc(&callbacks, (void *)client);
+    stat = yajl_parse(p, (const unsigned char *)message, message_size);
     if (stat != yajl_status_ok) {
         unsigned char *err;
-        err = yajl_get_error(p, true, (const unsigned char*)message,
+        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_SUBSCRIBE, (const uint8_t*)reply);
+        ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply);
         yajl_free(p);
         return;
     }
     yajl_free(p);
     const char *reply = "{\"success\":true}";
-    ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t*)reply);
+    ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (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[7] = {
+handler_t handlers[8] = {
     handle_command,
     handle_get_workspaces,
     handle_subscribe,
@@ -777,6 +899,7 @@ handler_t handlers[7] = {
     handle_tree,
     handle_get_marks,
     handle_get_bar_config,
+    handle_get_version,
 };
 
 /*
@@ -790,18 +913,18 @@ handler_t handlers[7] = {
  *
  */
 static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
-    char buf[2048];
-    int n = read(w->fd, buf, sizeof(buf));
-
-    /* On error or an empty message, we close the connection */
-    if (n <= 0) {
-#if 0
-        /* FIXME: I get these when closing a client socket,
-         * therefore we just treat them as an error. Is this
-         * correct? */
-        if (errno == EAGAIN || errno == EWOULDBLOCK)
-                return;
-#endif
+    uint32_t message_type;
+    uint32_t message_length;
+    uint8_t *message = NULL;
+
+    int ret = ipc_recv_message(w->fd, &message_type, &message_length, &message);
+    /* EOF or other error */
+    if (ret < 0) {
+        /* Was this a spurious read? See ev(3) */
+        if (ret == -1 && errno == EAGAIN) {
+            FREE(message);
+            return;
+        }
 
         /* If not, there was some kind of error. We don’t bother
          * and close the connection */
@@ -809,7 +932,7 @@ static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
 
         /* Delete the client from the list of clients */
         ipc_client *current;
-        TAILQ_FOREACH(current, &all_clients, clients) {
+        TAILQ_FOREACH (current, &all_clients, clients) {
             if (current->fd != w->fd)
                 continue;
 
@@ -824,57 +947,20 @@ static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
 
         ev_io_stop(EV_A_ w);
         free(w);
+        FREE(message);
 
         DLOG("IPC: client disconnected\n");
         return;
     }
 
-    /* Terminate the message correctly */
-    buf[n] = '\0';
-
-    /* Check if the message starts with the i3 IPC magic code */
-    if (n < strlen(I3_IPC_MAGIC)) {
-        DLOG("IPC: message too short, ignoring\n");
-        return;
+    if (message_type >= (sizeof(handlers) / sizeof(handler_t)))
+        DLOG("Unhandled message type: %d\n", message_type);
+    else {
+        handler_t h = handlers[message_type];
+        h(w->fd, message, 0, message_length, message_type);
     }
 
-    if (strncmp(buf, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC)) != 0) {
-        DLOG("IPC: message does not start with the IPC magic\n");
-        return;
-    }
-
-    uint8_t *message = (uint8_t*)buf;
-    while (n > 0) {
-        DLOG("IPC: n = %d\n", n);
-        message += strlen(I3_IPC_MAGIC);
-        n -= strlen(I3_IPC_MAGIC);
-
-        /* The next 32 bit after the magic are the message size */
-        uint32_t message_size;
-        memcpy(&message_size, (uint32_t*)message, sizeof(uint32_t));
-        message += sizeof(uint32_t);
-        n -= sizeof(uint32_t);
-
-        if (message_size > n) {
-            DLOG("IPC: Either the message size was wrong or the message was not read completely, dropping\n");
-            return;
-        }
-
-        /* The last 32 bits of the header are the message type */
-        uint32_t message_type;
-        memcpy(&message_type, (uint32_t*)message, sizeof(uint32_t));
-        message += sizeof(uint32_t);
-        n -= sizeof(uint32_t);
-
-        if (message_type >= (sizeof(handlers) / sizeof(handler_t)))
-            DLOG("Unhandled message type: %d\n", message_type);
-        else {
-            handler_t h = handlers[message_type];
-            h(w->fd, message, n, message_size, message_type);
-        }
-        n -= message_size;
-        message += message_size;
-    }
+    FREE(message);
 }
 
 /*
@@ -888,10 +974,11 @@ void ipc_new_client(EV_P_ struct ev_io *w, int revents) {
     struct sockaddr_un peer;
     socklen_t len = sizeof(struct sockaddr_un);
     int client;
-    if ((client = accept(w->fd, (struct sockaddr*)&peer, &len)) < 0) {
+    if ((client = accept(w->fd, (struct sockaddr *)&peer, &len)) < 0) {
         if (errno == EINTR)
             return;
-        else perror("accept()");
+        else
+            perror("accept()");
         return;
     }
 
@@ -945,7 +1032,7 @@ int ipc_create_socket(const char *filename) {
     memset(&addr, 0, sizeof(struct sockaddr_un));
     addr.sun_family = AF_LOCAL;
     strncpy(addr.sun_path, resolved, sizeof(addr.sun_path) - 1);
-    if (bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)) < 0) {
+    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
         perror("bind()");
         free(resolved);
         return -1;
@@ -962,3 +1049,86 @@ int ipc_create_socket(const char *filename) {
     current_socketpath = resolved;
     return sockfd;
 }
+
+/*
+ * For the workspace "focus" event we send, along the usual "change" field,
+ * also the current and previous workspace, in "current" and "old"
+ * respectively.
+ */
+void ipc_send_workspace_focus_event(Con *current, Con *old) {
+    setlocale(LC_NUMERIC, "C");
+    yajl_gen gen = ygenalloc();
+
+    y(map_open);
+
+    ystr("change");
+    ystr("focus");
+
+    ystr("current");
+    dump_node(gen, current, false);
+
+    ystr("old");
+    if (old == NULL)
+        y(null);
+    else
+        dump_node(gen, old, false);
+
+    y(map_close);
+
+    const unsigned char *payload;
+    ylength length;
+    y(get_buf, &payload, &length);
+
+    ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
+    y(free);
+    setlocale(LC_NUMERIC, "");
+}
+
+/**
+ * For the window events we send, along the usual "change" field,
+ * also the window container, in "container".
+ */
+void ipc_send_window_event(const char *property, Con *con) {
+    DLOG("Issue IPC window %s event (con = %p, window = 0x%08x)\n",
+         property, con, (con->window ? con->window->id : XCB_WINDOW_NONE));
+
+    setlocale(LC_NUMERIC, "C");
+    yajl_gen gen = ygenalloc();
+
+    y(map_open);
+
+    ystr("change");
+    ystr(property);
+
+    ystr("container");
+    dump_node(gen, con, false);
+
+    y(map_close);
+
+    const unsigned char *payload;
+    ylength length;
+    y(get_buf, &payload, &length);
+
+    ipc_send_event("window", I3_IPC_EVENT_WINDOW, (const char *)payload);
+    y(free);
+    setlocale(LC_NUMERIC, "");
+}
+
+/**
+ * For the barconfig update events, we send the serialized barconfig.
+ */
+void ipc_send_barconfig_update_event(Barconfig *barconfig) {
+    DLOG("Issue barconfig_update event for id = %s\n", barconfig->id);
+    setlocale(LC_NUMERIC, "C");
+    yajl_gen gen = ygenalloc();
+
+    dump_bar_config(gen, barconfig);
+
+    const unsigned char *payload;
+    ylength length;
+    y(get_buf, &payload, &length);
+
+    ipc_send_event("barconfig_update", I3_IPC_EVENT_BARCONFIG_UPDATE, (const char *)payload);
+    y(free);
+    setlocale(LC_NUMERIC, "");
+}