X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fipc.c;h=73f8d8cb99eb0c7132b09b4a35f8deee52da5519;hb=f354f534357798eb3ba497b7143132f41ff090f6;hp=c70ec323da334dddf146dfd0bf9006e106ef997b;hpb=58c65a64fe279258d374aa83ca3871aeacd66adb;p=i3%2Fi3 diff --git a/src/ipc.c b/src/ipc.c index c70ec323..73f8d8cb 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -1,17 +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 Michael Stapelberg and contributors (see also: LICENSE) * * ipc.c: UNIX domain socket IPC (initialization, client handling, protocol). * */ #include "all.h" + #include "yajl_utils.h" +#include #include #include #include @@ -37,36 +37,6 @@ static void set_nonblock(int sockfd) { err(-1, "Could not set O_NONBLOCK"); } -/* - * Emulates mkdir -p (creates any missing folders) - * - */ -bool mkdirp(const char *path) { - if (mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0) - return true; - if (errno != ENOENT) { - ELOG("mkdir(%s) failed: %s\n", path, strerror(errno)); - return false; - } - 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) { - FREE(copy); - return false; - } - *sep = '\0'; - bool result = false; - if (mkdirp(copy)) - result = mkdirp(path); - free(copy); - - return result; -} - /* * Sends the specified event to all IPC clients which are currently connected * and subscribed to this kind of event. @@ -101,6 +71,9 @@ void ipc_shutdown(void) { current = TAILQ_FIRST(&all_clients); shutdown(current->fd, SHUT_RDWR); close(current->fd); + for (int i = 0; i < current->num_events; i++) + free(current->events[i]); + free(current->events); TAILQ_REMOVE(&all_clients, current, clients); free(current); } @@ -114,7 +87,7 @@ void ipc_shutdown(void) { IPC_HANDLER(command) { /* To get a properly terminated buffer, we copy * message_size bytes out of the buffer */ - char *command = scalloc(message_size + 1); + char *command = scalloc(message_size + 1, 1); strncpy(command, (const char *)message, message_size); LOG("IPC: received: *%s*\n", command); yajl_gen gen = yajl_gen_alloc(NULL); @@ -151,56 +124,92 @@ 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"); +static void dump_event_state_mask(yajl_gen gen, Binding *bind) { y(array_open); - for (int i = 0; i < 8; i++) { - if (bind->mods & (1 << i)) { + for (int i = 0; i < 20; i++) { + if (bind->event_state_mask & (1 << i)) { switch (1 << i) { - case XCB_MOD_MASK_SHIFT: + case XCB_KEY_BUT_MASK_SHIFT: ystr("shift"); break; - case XCB_MOD_MASK_LOCK: + case XCB_KEY_BUT_MASK_LOCK: ystr("lock"); break; - case XCB_MOD_MASK_CONTROL: + case XCB_KEY_BUT_MASK_CONTROL: ystr("ctrl"); break; - case XCB_MOD_MASK_1: + case XCB_KEY_BUT_MASK_MOD_1: ystr("Mod1"); break; - case XCB_MOD_MASK_2: + case XCB_KEY_BUT_MASK_MOD_2: ystr("Mod2"); break; - case XCB_MOD_MASK_3: + case XCB_KEY_BUT_MASK_MOD_3: ystr("Mod3"); break; - case XCB_MOD_MASK_4: + case XCB_KEY_BUT_MASK_MOD_4: ystr("Mod4"); break; - case XCB_MOD_MASK_5: + case XCB_KEY_BUT_MASK_MOD_5: ystr("Mod5"); break; + case XCB_KEY_BUT_MASK_BUTTON_1: + ystr("Button1"); + break; + case XCB_KEY_BUT_MASK_BUTTON_2: + ystr("Button2"); + break; + case XCB_KEY_BUT_MASK_BUTTON_3: + ystr("Button3"); + break; + case XCB_KEY_BUT_MASK_BUTTON_4: + ystr("Button4"); + break; + case XCB_KEY_BUT_MASK_BUTTON_5: + ystr("Button5"); + break; + case (I3_XKB_GROUP_MASK_1 << 16): + ystr("Group1"); + break; + case (I3_XKB_GROUP_MASK_2 << 16): + ystr("Group2"); + break; + case (I3_XKB_GROUP_MASK_3 << 16): + ystr("Group3"); + break; + case (I3_XKB_GROUP_MASK_4 << 16): + ystr("Group4"); + break; } } } y(array_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); + + // This key is only provided for compatibility, new programs should use + // event_state_mask instead. + ystr("mods"); + dump_event_state_mask(gen, bind); + + ystr("event_state_mask"); + dump_event_state_mask(gen, bind); y(map_close); } @@ -208,7 +217,7 @@ static void dump_binding(yajl_gen gen, Binding *bind) { void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { y(map_open); ystr("id"); - y(integer, (long int)con); + y(integer, (uintptr_t)con); ystr("type"); switch (con->type) { @@ -269,9 +278,16 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("urgent"); y(bool, con->urgent); - if (con->mark != NULL) { - ystr("mark"); - ystr(con->mark); + if (!TAILQ_EMPTY(&(con->marks_head))) { + ystr("marks"); + y(array_open); + + mark_t *mark; + TAILQ_FOREACH(mark, &(con->marks_head), marks) { + ystr(mark->name); + } + + y(array_close); } ystr("focused"); @@ -359,6 +375,11 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { else y(null); + if (con->title_format != NULL) { + ystr("title_format"); + ystr(con->title_format); + } + if (con->type == CT_WORKSPACE) { ystr("num"); y(integer, con->num); @@ -397,7 +418,8 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("transient_for"); if (con->window->transient_for == XCB_NONE) y(null); - else y(integer, con->window->transient_for); + else + y(integer, con->window->transient_for); y(map_close); } @@ -422,13 +444,16 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("focus"); y(array_open); TAILQ_FOREACH(node, &(con->focus_head), focused) { - y(integer, (long int)node); + y(integer, (uintptr_t)node); } y(array_close); ystr("fullscreen_mode"); y(integer, con->fullscreen_mode); + ystr("sticky"); + y(bool, con->sticky); + ystr("floating"); switch (con->floating) { case FLOATING_AUTO_OFF: @@ -449,8 +474,12 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { y(array_open); Match *match; TAILQ_FOREACH(match, &(con->swallow_head), matches) { + /* We will generate a new restart_mode match specification after this + * loop, so skip this one. */ + if (match->restart_mode) + continue; y(map_open); - if (match->dock != -1) { + if (match->dock != M_DONTCHECK) { ystr("dock"); y(integer, match->dock); ystr("insert_where"); @@ -494,6 +523,28 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { y(map_close); } +static void dump_bar_bindings(yajl_gen gen, Barconfig *config) { + if (TAILQ_EMPTY(&(config->bar_bindings))) + return; + + ystr("bindings"); + y(array_open); + + struct Barbinding *current; + TAILQ_FOREACH(current, &(config->bar_bindings), bindings) { + y(map_open); + + ystr("input_code"); + y(integer, current->input_code); + ystr("command"); + ystr(current->command); + + y(map_close); + } + + y(array_close); +} + static void dump_bar_config(yajl_gen gen, Barconfig *config) { y(map_open); @@ -508,6 +559,18 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { y(array_close); } + if (!TAILQ_EMPTY(&(config->tray_outputs))) { + ystr("tray_outputs"); + y(array_open); + + struct tray_output_t *tray_output; + TAILQ_FOREACH(tray_output, &(config->tray_outputs), tray_outputs) { + ystr(tray_output->output); + } + + y(array_close); + } + #define YSTR_IF_SET(name) \ do { \ if (config->name) { \ @@ -516,7 +579,9 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { } \ } while (0) - YSTR_IF_SET(tray_output); + ystr("tray_padding"); + y(integer, config->tray_padding); + YSTR_IF_SET(socket_path); ystr("mode"); @@ -546,6 +611,9 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { ystr("modifier"); switch (config->modifier) { + case M_NONE: + ystr("none"); + break; case M_CONTROL: ystr("ctrl"); break; @@ -561,11 +629,6 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { case M_MOD3: ystr("Mod3"); break; - /* - case M_MOD4: - ystr("Mod4"); - break; - */ case M_MOD5: ystr("Mod5"); break; @@ -574,15 +637,7 @@ 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); - } + dump_bar_bindings(gen, config); ystr("position"); if (config->position == P_BOTTOM) @@ -593,6 +648,11 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { YSTR_IF_SET(status_command); YSTR_IF_SET(font); + if (config->separator_symbol) { + ystr("separator_symbol"); + ystr(config->separator_symbol); + } + ystr("workspace_buttons"); y(bool, !config->hide_workspace_buttons); @@ -619,6 +679,9 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { YSTR_IF_SET(background); YSTR_IF_SET(statusline); YSTR_IF_SET(separator); + YSTR_IF_SET(focused_background); + YSTR_IF_SET(focused_statusline); + YSTR_IF_SET(focused_separator); YSTR_IF_SET(focused_workspace_border); YSTR_IF_SET(focused_workspace_bg); YSTR_IF_SET(focused_workspace_text); @@ -631,6 +694,9 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { YSTR_IF_SET(urgent_workspace_border); YSTR_IF_SET(urgent_workspace_bg); YSTR_IF_SET(urgent_workspace_text); + YSTR_IF_SET(binding_mode_border); + YSTR_IF_SET(binding_mode_bg); + YSTR_IF_SET(binding_mode_text); y(map_close); y(map_close); @@ -779,9 +845,12 @@ IPC_HANDLER(get_marks) { y(array_open); Con *con; - TAILQ_FOREACH(con, &all_cons, all_cons) - if (con->mark != NULL) - ystr(con->mark); + TAILQ_FOREACH(con, &all_cons, all_cons) { + mark_t *mark; + TAILQ_FOREACH(mark, &(con->marks_head), marks) { + ystr(mark->name); + } + } y(array_close); @@ -811,7 +880,10 @@ IPC_HANDLER(get_version) { y(integer, PATCH_VERSION); ystr("human_readable"); - ystr(I3_VERSION); + ystr(i3_version); + + ystr("loaded_config_file_name"); + ystr(current_configpath); y(map_close); @@ -851,8 +923,8 @@ 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); + char *bar_id = NULL; + sasprintf(&bar_id, "%.*s", message_size, message); LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id); Barconfig *current, *config = NULL; TAILQ_FOREACH(current, &barconfigs, configs) { @@ -862,6 +934,7 @@ IPC_HANDLER(get_bar_config) { config = current; break; } + free(bar_id); if (!config) { /* If we did not find a config for the given ID, the reply will contain @@ -884,6 +957,28 @@ IPC_HANDLER(get_bar_config) { y(free); } +/* + * Returns a list of configured binding modes + * + */ +IPC_HANDLER(get_binding_modes) { + yajl_gen gen = ygenalloc(); + + y(array_open); + struct Mode *mode; + SLIST_FOREACH(mode, &modes, modes) { + ystr(mode->name); + } + y(array_close); + + const unsigned char *payload; + ylength length; + y(get_buf, &payload, &length); + + ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BINDING_MODES, payload); + y(free); +} + /* * Callback for the YAJL parser (will be called when a string is parsed). * @@ -896,10 +991,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 = srealloc(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, 1); memcpy(client->events[event], s, len); DLOG("client is now subscribed to:\n"); @@ -960,7 +1055,7 @@ IPC_HANDLER(subscribe) { /* The index of each callback function corresponds to the numeric * value of the message type (see include/i3/ipc.h) */ -handler_t handlers[8] = { +handler_t handlers[9] = { handle_command, handle_get_workspaces, handle_subscribe, @@ -969,6 +1064,7 @@ handler_t handlers[8] = { handle_get_marks, handle_get_bar_config, handle_get_version, + handle_get_binding_modes, }; /* @@ -1007,6 +1103,7 @@ static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) { for (int i = 0; i < current->num_events; i++) free(current->events[i]); + free(current->events); /* We can call TAILQ_REMOVE because we break out of the * TAILQ_FOREACH afterwards */ TAILQ_REMOVE(&all_clients, current, clients); @@ -1056,13 +1153,13 @@ void ipc_new_client(EV_P_ struct ev_io *w, int revents) { set_nonblock(client); - struct ev_io *package = scalloc(sizeof(struct ev_io)); + struct ev_io *package = scalloc(1, 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 on fd %d\n", w->fd); - ipc_client *new = scalloc(sizeof(ipc_client)); + ipc_client *new = scalloc(1, sizeof(ipc_client)); new->fd = client; TAILQ_INSERT_TAIL(&all_clients, new, clients); @@ -1083,7 +1180,7 @@ int ipc_create_socket(const char *filename) { char *copy = sstrdup(resolved); const char *dir = dirname(copy); if (!path_exists(dir)) - mkdirp(dir); + mkdirp(dir, DEFAULT_DIR_MODE); free(copy); /* Unlink the unix domain socket before */ @@ -1120,21 +1217,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) @@ -1144,13 +1243,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, ""); } /**