X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fipc.c;h=17c15b9e91e723a128794e218694b8196e3475e9;hb=7be5ece6636f7a4c800ab0c5dd6289b38db7b435;hp=d7b5ac701a8414f7c94599872d3908e70e5bb12e;hpb=eb0a56fad1a9048e3905972f5db0d61c5bd13516;p=i3%2Fi3 diff --git a/src/ipc.c b/src/ipc.c index d7b5ac70..17c15b9e 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -1,15 +1,17 @@ +#undef I3__FILE__ +#define I3__FILE__ "ipc.c" /* * vim:ts=4:sw=4:expandtab * * i3 - an improved dynamic tiling window manager + * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) * - * © 2009-2011 Michael Stapelberg and contributors - * - * See file LICENSE for license information. - * - * ipc.c: Everything about the UNIX domain sockets for IPC + * ipc.c: UNIX domain socket IPC (initialization, client handling, protocol). * */ +#include "all.h" +#include "yajl_utils.h" + #include #include #include @@ -17,16 +19,9 @@ #include #include #include -#include - -#include "all.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); /* @@ -53,14 +48,16 @@ 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'; char *sep = strrchr(copy, '/'); - if (sep == NULL) + if (sep == NULL) { + FREE(copy); return false; + } *sep = '\0'; bool result = false; if (mkdirp(copy)) @@ -70,35 +67,6 @@ static bool mkdirp(const char *path) { return result; } -static void ipc_send_message(int fd, const unsigned char *payload, - int message_type, int message_size) { - int buffer_size = strlen("i3-ipc") + sizeof(uint32_t) + - sizeof(uint32_t) + message_size; - char msg[buffer_size]; - char *walk = msg; - - strncpy(walk, "i3-ipc", buffer_size - 1); - walk += strlen("i3-ipc"); - memcpy(walk, &message_size, sizeof(uint32_t)); - walk += sizeof(uint32_t); - memcpy(walk, &message_type, sizeof(uint32_t)); - walk += sizeof(uint32_t); - memcpy(walk, payload, message_size); - - int sent_bytes = 0; - int bytes_to_go = buffer_size; - while (sent_bytes < bytes_to_go) { - int n = write(fd, msg + sent_bytes, bytes_to_go); - if (n == -1) { - DLOG("write() failed: %s\n", strerror(errno)); - return; - } - - sent_bytes += n; - bytes_to_go -= n; - } -} - /* * Sends the specified event to all IPC clients which are currently connected * and subscribed to this kind of event. @@ -118,8 +86,7 @@ void ipc_send_event(const char *event, uint32_t message_type, const char *payloa if (!interested) continue; - ipc_send_message(current->fd, (const unsigned char*)payload, - message_type, strlen(payload)); + ipc_send_message(current->fd, strlen(payload), message_type, (const uint8_t*)payload); } } @@ -128,11 +95,14 @@ void ipc_send_event(const char *event, uint32_t message_type, const char *payloa * when exiting or restarting only! * */ -void ipc_shutdown() { +void ipc_shutdown(void) { ipc_client *current; - TAILQ_FOREACH(current, &all_clients, clients) { + while (!TAILQ_EMPTY(&all_clients)) { + current = TAILQ_FIRST(&all_clients); shutdown(current->fd, SHUT_RDWR); close(current->fd); + TAILQ_REMOVE(&all_clients, current, clients); + free(current); } } @@ -147,17 +117,20 @@ IPC_HANDLER(command) { char *command = scalloc(message_size + 1); strncpy(command, (const char*)message, message_size); LOG("IPC: received: *%s*\n", command); - char *reply = parse_cmd((const char*)command); - char *save_reply = reply; + struct CommandResult *command_output = parse_command((const char*)command); free(command); - /* If no reply was provided, we just use the default success message */ - if (reply == NULL) - reply = "{\"success\":true}"; - ipc_send_message(fd, (const unsigned char*)reply, - I3_IPC_REPLY_TYPE_COMMAND, strlen(reply)); + if (command_output->needs_tree_render) + tree_render(); - FREE(save_reply); + const unsigned char *reply; + ylength length; + yajl_gen_get_buf(command_output->json_gen, &reply, &length); + + ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_COMMAND, + (const uint8_t*)reply); + + yajl_gen_free(command_output->json_gen); } static void dump_rect(yajl_gen gen, const char *name, Rect r) { @@ -182,16 +155,26 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("type"); y(integer, con->type); + /* provided for backwards compatibility only. */ ystr("orientation"); - switch (con->orientation) { - case NO_ORIENTATION: + if (!con_is_split(con)) + ystr("none"); + else { + if (con_orientation(con) == HORIZ) + ystr("horizontal"); + else ystr("vertical"); + } + + ystr("scratchpad_state"); + switch (con->scratchpad_state) { + case SCRATCHPAD_NONE: ystr("none"); break; - case HORIZ: - ystr("horizontal"); + case SCRATCHPAD_FRESH: + ystr("fresh"); break; - case VERT: - ystr("vertical"); + case SCRATCHPAD_CHANGED: + ystr("changed"); break; } @@ -214,7 +197,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"); @@ -230,6 +220,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: @@ -238,17 +255,23 @@ 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"); - ystr(con->name); + if (con->window && con->window->name) + ystr(i3string_as_utf8(con->window->name)); + else + ystr(con->name); if (con->type == CT_WORKSPACE) { ystr("num"); @@ -279,7 +302,7 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("focus"); y(array_open); - TAILQ_FOREACH(node, &(con->focus_head), nodes) { + TAILQ_FOREACH(node, &(con->focus_head), focused) { y(integer, (long int)node); } y(array_close); @@ -287,6 +310,22 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("fullscreen_mode"); y(integer, con->fullscreen_mode); + ystr("floating"); + switch (con->floating) { + case FLOATING_AUTO_OFF: + ystr("auto_off"); + break; + case FLOATING_AUTO_ON: + ystr("auto_on"); + break; + case FLOATING_USER_OFF: + ystr("user_off"); + break; + case FLOATING_USER_ON: + ystr("user_on"); + break; + } + ystr("swallows"); y(array_open); Match *match; @@ -308,6 +347,8 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { y(map_open); ystr("id"); y(integer, con->window->id); + ystr("restart_mode"); + y(bool, true); y(map_close); } } @@ -318,23 +359,15 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { 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, payload, I3_IPC_REPLY_TYPE_TREE, length); + ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_TREE, payload); y(free); } @@ -345,17 +378,15 @@ IPC_HANDLER(tree) { * */ 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 (con_is_internal(output)) + continue; Con *ws; TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) { assert(ws->type == CT_WORKSPACE); @@ -400,14 +431,10 @@ 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, payload, I3_IPC_REPLY_TYPE_WORKSPACES, length); + ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_WORKSPACES, payload); y(free); } @@ -417,11 +444,7 @@ 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; @@ -434,6 +457,9 @@ IPC_HANDLER(get_outputs) { ystr("active"); y(bool, output->active); + ystr("primary"); + y(bool, output->primary); + ystr("rect"); y(map_open); ystr("x"); @@ -458,14 +484,10 @@ 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, payload, I3_IPC_REPLY_TYPE_OUTPUTS, length); + ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_OUTPUTS, payload); y(free); } @@ -475,11 +497,7 @@ 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; @@ -490,10 +508,199 @@ IPC_HANDLER(get_marks) { y(array_close); const unsigned char *payload; - unsigned int length; + 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) { + 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) { + ystr(current->id); + } + y(array_close); + + const unsigned char *payload; + ylength length; + y(get_buf, &payload, &length); + + ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload); + y(free); + return; + } + + /* 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); + LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id); + Barconfig *current, *config = NULL; + TAILQ_FOREACH(current, &barconfigs, configs) { + if (strcmp(current->id, bar_id) != 0) + continue; + + config = current; + 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. */ + 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 + } + + y(map_close); + + const unsigned char *payload; + ylength length; y(get_buf, &payload, &length); - ipc_send_message(fd, payload, I3_IPC_REPLY_TYPE_MARKS, length); + ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload); y(free); } @@ -501,16 +708,11 @@ IPC_HANDLER(get_marks) { * 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++; @@ -557,11 +759,7 @@ IPC_HANDLER(subscribe) { 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 + p = yalloc(&callbacks, (void*)client); stat = yajl_parse(p, (const unsigned char*)message, message_size); if (stat != yajl_status_ok) { unsigned char *err; @@ -571,26 +769,26 @@ IPC_HANDLER(subscribe) { yajl_free_error(p, err); const char *reply = "{\"success\":false}"; - ipc_send_message(fd, (const unsigned char*)reply, - I3_IPC_REPLY_TYPE_SUBSCRIBE, strlen(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, (const unsigned char*)reply, - I3_IPC_REPLY_TYPE_SUBSCRIBE, strlen(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[6] = { +handler_t handlers[8] = { handle_command, handle_get_workspaces, handle_subscribe, handle_get_outputs, handle_tree, - handle_get_marks + handle_get_marks, + handle_get_bar_config, + handle_get_version, }; /* @@ -632,10 +830,12 @@ static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) { /* We can call TAILQ_REMOVE because we break out of the * TAILQ_FOREACH afterwards */ TAILQ_REMOVE(&all_clients, current, clients); + free(current); break; } ev_io_stop(EV_A_ w); + free(w); DLOG("IPC: client disconnected\n"); return; @@ -707,13 +907,16 @@ void ipc_new_client(EV_P_ struct ev_io *w, int revents) { return; } + /* Close this file descriptor on exec() */ + (void)fcntl(client, F_SETFD, FD_CLOEXEC); + set_nonblock(client); struct ev_io *package = scalloc(sizeof(struct ev_io)); ev_io_init(package, ipc_receive_message, client, EV_READ); ev_io_start(EV_A_ package); - DLOG("IPC: new client connected\n"); + DLOG("IPC: new client connected on fd %d\n", w->fd); ipc_client *new = scalloc(sizeof(ipc_client)); new->fd = client;