X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fipc.c;h=3733034dec5713ae6fabaef1988beadf83010088;hb=e114b3dba2485606cf5a3d044c0de8430c9f80b4;hp=031ee9ab5d78bea6369b740bdc84f760fb734c99;hpb=0482dc754681eb8bf0c05ed46943b066813dae0c;p=i3%2Fi3 diff --git a/src/ipc.c b/src/ipc.c index 031ee9ab..3733034d 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 */ @@ -72,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. @@ -120,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); } } @@ -132,9 +99,12 @@ void ipc_send_event(const char *event, uint32_t message_type, const char *payloa */ void ipc_shutdown() { 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); } } @@ -149,17 +119,18 @@ 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 (command_output->needs_tree_render) + tree_render(); + /* 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)); + ipc_send_message(fd, strlen(command_output->json_output), + I3_IPC_REPLY_TYPE_COMMAND, + (const uint8_t*)command_output->json_output); - FREE(save_reply); + free(command_output->json_output); } static void dump_rect(yajl_gen gen, const char *name, Rect r) { @@ -197,6 +168,19 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { break; } + ystr("scratchpad_state"); + switch (con->scratchpad_state) { + case SCRATCHPAD_NONE: + ystr("none"); + break; + case SCRATCHPAD_FRESH: + ystr("fresh"); + break; + case SCRATCHPAD_CHANGED: + ystr("changed"); + break; + } + ystr("percent"); if (con->percent == 0.0) y(null); @@ -292,6 +276,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; @@ -313,6 +313,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); } } @@ -339,7 +341,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); } @@ -361,6 +363,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); @@ -412,7 +416,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); } @@ -470,7 +474,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); } @@ -502,7 +506,178 @@ 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); +} + +/* + * 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); } @@ -580,26 +755,25 @@ 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[7] = { handle_command, handle_get_workspaces, handle_subscribe, handle_get_outputs, handle_tree, - handle_get_marks + handle_get_marks, + handle_get_bar_config, }; /* @@ -641,10 +815,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; @@ -716,6 +892,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));