X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;ds=sidebyside;f=src%2Fipc.c;h=77b8dbb391101eb50c32a80e8b69d669f7aff7a7;hb=78f5f2204d3b25ce0bac948eb7c0ed8d35808262;hp=a7b46f62c978c69e96025bd7f1588649e4ab72dc;hpb=abafb2b235102ea561c632391ad525f7e1c9e148;p=i3%2Fi3 diff --git a/src/ipc.c b/src/ipc.c index a7b46f62..77b8dbb3 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -2,14 +2,13 @@ * 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 #include #include @@ -19,8 +18,6 @@ #include #include -#include "all.h" - char *current_socketpath = NULL; /* Shorter names for all those yajl_gen_* functions */ @@ -59,8 +56,10 @@ static bool mkdirp(const char *path) { 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 +69,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 +88,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 +97,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 +119,24 @@ 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(); + + const unsigned char *reply; +#if YAJL_MAJOR >= 2 + size_t length; +#else + unsigned int length; +#endif + yajl_gen_get_buf(command_output->json_gen, &reply, &length); + + ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_COMMAND, + (const uint8_t*)reply); - FREE(save_reply); + yajl_gen_free(command_output->json_gen); } static void dump_rect(yajl_gen gen, const char *name, Rect r) { @@ -182,16 +161,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->split) + 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; } @@ -211,10 +200,20 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("focused"); y(bool, (con == focused)); + ystr("split"); + y(bool, con->split); + 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 +229,16 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { 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: @@ -282,7 +291,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); @@ -290,6 +299,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; @@ -311,6 +336,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); } } @@ -337,7 +364,7 @@ IPC_HANDLER(tree) { #endif 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); } @@ -359,6 +386,8 @@ IPC_HANDLER(get_workspaces) { Con *output; TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + if (output->name[0] == '_' && output->name[1] == '_') + continue; Con *ws; TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) { assert(ws->type == CT_WORKSPACE); @@ -410,7 +439,7 @@ IPC_HANDLER(get_workspaces) { #endif 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); } @@ -437,6 +466,9 @@ IPC_HANDLER(get_outputs) { ystr("active"); y(bool, output->active); + ystr("primary"); + y(bool, output->primary); + ystr("rect"); y(map_open); ystr("x"); @@ -468,7 +500,7 @@ IPC_HANDLER(get_outputs) { #endif 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); } @@ -500,7 +532,216 @@ IPC_HANDLER(get_marks) { #endif 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_MARKS, payload); + y(free); +} + +/* + * Returns the version of i3 + * + */ +IPC_HANDLER(get_version) { +#if YAJL_MAJOR >= 2 + yajl_gen gen = yajl_gen_alloc(NULL); +#else + yajl_gen gen = yajl_gen_alloc(NULL, NULL); +#endif + 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; +#if YAJL_MAJOR >= 2 + size_t length; +#else + unsigned int length; +#endif + 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 + + /* 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; +#if YAJL_MAJOR >= 2 + size_t length; +#else + unsigned int length; +#endif + 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; +#if YAJL_MAJOR >= 2 + size_t length; +#else + unsigned int length; +#endif + y(get_buf, &payload, &length); + + ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload); y(free); } @@ -578,26 +819,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, }; /* @@ -639,10 +880,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; @@ -714,6 +957,9 @@ 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));