]> git.sur5r.net Git - i3/i3/blobdiff - src/ipc.c
Merge branch 'master' into next
[i3/i3] / src / ipc.c
index c3b82b519a07820d2367a01d092c4eaa1e55cea9..acd2267bdb61f761f34511d3adaf3bec7afcff50 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -41,7 +41,7 @@ static void set_nonblock(int sockfd) {
  * Emulates mkdir -p (creates any missing folders)
  *
  */
-static bool mkdirp(const char *path) {
+bool mkdirp(const char *path) {
     if (mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0)
         return true;
     if (errno != ENOENT) {
@@ -50,8 +50,8 @@ static bool mkdirp(const char *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) {
@@ -86,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);
     }
 }
 
@@ -115,22 +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 CommandResultIR *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();
 
+    command_result_free(result);
+
     const unsigned char *reply;
     ylength length;
-    yajl_gen_get_buf(command_output->json_gen, &reply, &length);
+    yajl_gen_get_buf(gen, &reply, &length);
 
     ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_COMMAND,
-                     (const uint8_t*)reply);
+                     (const uint8_t *)reply);
 
-    yajl_gen_free(command_output->json_gen);
+    yajl_gen_free(gen);
 }
 
 static void dump_rect(yajl_gen gen, const char *name, Rect r) {
@@ -147,6 +151,60 @@ static void dump_rect(yajl_gen gen, const char *name, Rect r) {
     y(map_close);
 }
 
+static void dump_binding(yajl_gen gen, Binding *bind) {
+    y(map_open);
+    ystr("input_code");
+    y(integer, bind->keycode);
+
+    ystr("input_type");
+    ystr((const char *)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse"));
+
+    ystr("symbol");
+    if (bind->symbol == NULL)
+        y(null);
+    else
+        ystr(bind->symbol);
+
+    ystr("command");
+    ystr(bind->command);
+
+    ystr("mods");
+    y(array_open);
+    for (int i = 0; i < 8; i++) {
+        if (bind->mods & (1 << i)) {
+            switch (1 << i) {
+                case XCB_MOD_MASK_SHIFT:
+                    ystr("shift");
+                    break;
+                case XCB_MOD_MASK_LOCK:
+                    ystr("lock");
+                    break;
+                case XCB_MOD_MASK_CONTROL:
+                    ystr("ctrl");
+                    break;
+                case XCB_MOD_MASK_1:
+                    ystr("Mod1");
+                    break;
+                case XCB_MOD_MASK_2:
+                    ystr("Mod2");
+                    break;
+                case XCB_MOD_MASK_3:
+                    ystr("Mod3");
+                    break;
+                case XCB_MOD_MASK_4:
+                    ystr("Mod4");
+                    break;
+                case XCB_MOD_MASK_5:
+                    ystr("Mod5");
+                    break;
+            }
+        }
+    }
+    y(array_close);
+
+    y(map_close);
+}
+
 void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     y(map_open);
     ystr("id");
@@ -185,7 +243,8 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     else {
         if (con_orientation(con) == HORIZ)
             ystr("horizontal");
-        else ystr("vertical");
+        else
+            ystr("vertical");
     }
 
     ystr("scratchpad_state");
@@ -204,7 +263,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);
@@ -287,14 +347,17 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     y(integer, con->current_border_width);
 
     dump_rect(gen, "rect", con->rect);
+    dump_rect(gen, "deco_rect", con->deco_rect);
     dump_rect(gen, "window_rect", con->window_rect);
     dump_rect(gen, "geometry", con->geometry);
 
     ystr("name");
     if (con->window && con->window->name)
         ystr(i3string_as_utf8(con->window->name));
-    else
+    else if (con->name != NULL)
         ystr(con->name);
+    else
+        y(null);
 
     if (con->type == CT_WORKSPACE) {
         ystr("num");
@@ -304,7 +367,8 @@ 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
@@ -313,12 +377,13 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
         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)
+#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);
@@ -329,6 +394,12 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
             ystr(i3string_as_utf8(con->window->name));
         }
 
+        ystr("transient_for");
+        if (con->window->transient_for == XCB_NONE)
+            y(null);
+        else
+            y(integer, con->window->transient_for);
+
         y(map_close);
     }
 
@@ -387,12 +458,13 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
             y(integer, match->insert_where);
         }
 
-#define DUMP_REGEX(re_name) do { \
-    if (match->re_name != NULL) { \
-        ystr(# re_name); \
-        ystr(match->re_name->pattern); \
-    } \
-} while (0)
+#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);
@@ -437,12 +509,12 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
         y(array_close);
     }
 
-#define YSTR_IF_SET(name) \
-    do { \
-        if (config->name) { \
-            ystr( # name); \
+#define YSTR_IF_SET(name)       \
+    do {                        \
+        if (config->name) {     \
+            ystr(#name);        \
             ystr(config->name); \
-        } \
+        }                       \
     } while (0)
 
     YSTR_IF_SET(tray_output);
@@ -490,7 +562,7 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
         case M_MOD3:
             ystr("Mod3");
             break;
-            /*
+        /*
                case M_MOD4:
                ystr("Mod4");
                break;
@@ -503,10 +575,21 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
             break;
     }
 
+    if (config->wheel_up_cmd) {
+        ystr("wheel_up_cmd");
+        ystr(config->wheel_up_cmd);
+    }
+
+    if (config->wheel_down_cmd) {
+        ystr("wheel_down_cmd");
+        ystr(config->wheel_down_cmd);
+    }
+
     ystr("position");
     if (config->position == P_BOTTOM)
         ystr("bottom");
-    else ystr("top");
+    else
+        ystr("top");
 
     YSTR_IF_SET(status_command);
     YSTR_IF_SET(font);
@@ -524,12 +607,12 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
     y(bool, config->verbose);
 
 #undef YSTR_IF_SET
-#define YSTR_IF_SET(name) \
-    do { \
-        if (config->colors.name) { \
-            ystr( # name); \
+#define YSTR_IF_SET(name)              \
+    do {                               \
+        if (config->colors.name) {     \
+            ystr(#name);               \
             ystr(config->colors.name); \
-        } \
+        }                              \
     } while (0)
 
     ystr("colors");
@@ -569,7 +652,6 @@ IPC_HANDLER(tree) {
     y(free);
 }
 
-
 /*
  * Formats the reply message for a GET_WORKSPACES request and sends it to the
  * client
@@ -591,9 +673,7 @@ IPC_HANDLER(get_workspaces) {
             y(map_open);
 
             ystr("num");
-            if (ws->num == -1)
-                y(null);
-            else y(integer, ws->num);
+            y(integer, ws->num);
 
             ystr("name");
             ystr(ws->name);
@@ -674,7 +754,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);
     }
@@ -700,8 +781,8 @@ IPC_HANDLER(get_marks) {
 
     Con *con;
     TAILQ_FOREACH(con, &all_cons, all_cons)
-        if (con->mark != NULL)
-            ystr(con->mark);
+    if (con->mark != NULL)
+        ystr(con->mark);
 
     y(array_close);
 
@@ -772,7 +853,7 @@ 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) {
@@ -816,10 +897,10 @@ static int add_subscription(void *extra, const unsigned char *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");
@@ -859,23 +940,23 @@ IPC_HANDLER(subscribe) {
         .yajl_string = add_subscription,
     };
 
-    p = yalloc(&callbacks, (void*)client);
-    stat = yajl_parse(p, (const unsigned char*)message, message_size);
+    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
@@ -963,10 +1044,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;
     }
 
@@ -1020,7 +1102,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;
@@ -1039,21 +1121,23 @@ int ipc_create_socket(const char *filename) {
 }
 
 /*
- * For the workspace "focus" event we send, along the usual "change" field,
- * also the current and previous workspace, in "current" and "old"
- * respectively.
+ * Generates a json workspace event. Returns a dynamically allocated yajl
+ * generator. Free with yajl_gen_free().
  */
-void ipc_send_workspace_focus_event(Con *current, Con *old) {
+yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old) {
     setlocale(LC_NUMERIC, "C");
     yajl_gen gen = ygenalloc();
 
     y(map_open);
 
     ystr("change");
-    ystr("focus");
+    ystr(change);
 
     ystr("current");
-    dump_node(gen, current, false);
+    if (current == NULL)
+        y(null);
+    else
+        dump_node(gen, current, false);
 
     ystr("old");
     if (old == NULL)
@@ -1063,13 +1147,26 @@ void ipc_send_workspace_focus_event(Con *current, Con *old) {
 
     y(map_close);
 
+    setlocale(LC_NUMERIC, "");
+
+    return gen;
+}
+
+/*
+ * For the workspace events we send, along with the usual "change" field, also
+ * the workspace container in "current". For focus events, we send the
+ * previously focused workspace in "old".
+ */
+void ipc_send_workspace_event(const char *change, Con *current, Con *old) {
+    yajl_gen gen = ipc_marshal_workspace_event(change, current, old);
+
     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, "");
 }
 
 /**
@@ -1078,7 +1175,7 @@ void ipc_send_workspace_focus_event(Con *current, Con *old) {
  */
 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));
+         property, con, (con->window ? con->window->id : XCB_WINDOW_NONE));
 
     setlocale(LC_NUMERIC, "C");
     yajl_gen gen = ygenalloc();
@@ -1120,3 +1217,33 @@ void ipc_send_barconfig_update_event(Barconfig *barconfig) {
     y(free);
     setlocale(LC_NUMERIC, "");
 }
+
+/*
+ * For the binding events, we send the serialized binding struct.
+ */
+void ipc_send_binding_event(const char *event_type, Binding *bind) {
+    DLOG("Issue IPC binding %s event (sym = %s, code = %d)\n", event_type, bind->symbol, bind->keycode);
+
+    setlocale(LC_NUMERIC, "C");
+
+    yajl_gen gen = ygenalloc();
+
+    y(map_open);
+
+    ystr("change");
+    ystr(event_type);
+
+    ystr("binding");
+    dump_binding(gen, bind);
+
+    y(map_close);
+
+    const unsigned char *payload;
+    ylength length;
+    y(get_buf, &payload, &length);
+
+    ipc_send_event("binding", I3_IPC_EVENT_BINDING, (const char *)payload);
+
+    y(free);
+    setlocale(LC_NUMERIC, "");
+}