X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcommands.c;h=03bb10a622be654530d1f7fae4cca1b2f14b4ca1;hb=92000942039fa99d7334ca5099b467b0d3d17792;hp=2404a811785591fd996c8ed37b815e0bdc252a37;hpb=a1691a08ba33bba654d45e80afab0dcf03cc0cfa;p=i3%2Fi3 diff --git a/src/commands.c b/src/commands.c index 2404a811..03bb10a6 100644 --- a/src/commands.c +++ b/src/commands.c @@ -13,38 +13,53 @@ #include #include "all.h" +#include "shmlog.h" // Macros to make the YAJL API a bit easier to use. -#define y(x, ...) yajl_gen_ ## x (cmd_output->json_gen, ##__VA_ARGS__) -#define ystr(str) yajl_gen_string(cmd_output->json_gen, (unsigned char*)str, strlen(str)) -#define ysuccess(success) do { \ - y(map_open); \ - ystr("success"); \ - y(bool, success); \ - y(map_close); \ -} while (0) +#define y(x, ...) (cmd_output->json_gen != NULL ? yajl_gen_##x(cmd_output->json_gen, ##__VA_ARGS__) : 0) +#define ystr(str) (cmd_output->json_gen != NULL ? yajl_gen_string(cmd_output->json_gen, (unsigned char *)str, strlen(str)) : 0) +#define ysuccess(success) \ + do { \ + if (cmd_output->json_gen != NULL) { \ + y(map_open); \ + ystr("success"); \ + y(bool, success); \ + y(map_close); \ + } \ + } while (0) +#define yerror(message) \ + do { \ + if (cmd_output->json_gen != NULL) { \ + y(map_open); \ + ystr("success"); \ + y(bool, false); \ + ystr("error"); \ + ystr(message); \ + y(map_close); \ + } \ + } while (0) /** When the command did not include match criteria (!), we use the currently * focused container. Do not confuse this case with a command which included * criteria but which did not match any windows. This macro has to be called in * every command. */ -#define HANDLE_EMPTY_MATCH do { \ - if (match_is_empty(current_match)) { \ - owindow *ow = smalloc(sizeof(owindow)); \ - ow->con = focused; \ - TAILQ_INIT(&owindows); \ - TAILQ_INSERT_TAIL(&owindows, ow, owindows); \ - } \ -} while (0) - +#define HANDLE_EMPTY_MATCH \ + do { \ + if (match_is_empty(current_match)) { \ + owindow *ow = smalloc(sizeof(owindow)); \ + ow->con = focused; \ + TAILQ_INIT(&owindows); \ + TAILQ_INSERT_TAIL(&owindows, ow, owindows); \ + } \ + } while (0) /* * Returns true if a is definitely greater than b (using the given epsilon) * */ static bool definitelyGreaterThan(float a, float b, float epsilon) { - return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon); + return (a - b) > ((fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon); } /* @@ -63,7 +78,19 @@ static Output *get_output_from_string(Output *current_output, const char *output output = get_output_next_wrap(D_UP, current_output); else if (strcasecmp(output_str, "down") == 0) output = get_output_next_wrap(D_DOWN, current_output); - else output = get_output_by_name(output_str); + else + output = get_output_by_name(output_str); + + return output; +} + +/* + * Returns the output containing the given container. + */ +static Output *get_output_of_con(Con *con) { + Con *output_con = con_get_output(con); + Output *output = get_output_by_name(output_con->name); + assert(output != NULL); return output; } @@ -75,7 +102,7 @@ static Output *get_output_from_string(Output *current_output, const char *output * and return true, signaling that no further workspace switching should occur in the calling function. * */ -static bool maybe_back_and_forth(struct CommandResult *cmd_output, char *name) { +static bool maybe_back_and_forth(struct CommandResultIR *cmd_output, char *name) { Con *ws = con_get_workspace(focused); /* If we switched to a different workspace, do nothing */ @@ -237,6 +264,7 @@ void cmd_criteria_init(I3_CMD) { owindow *ow; DLOG("Initializing criteria, current_match = %p\n", current_match); + match_free(current_match); match_init(current_match); while (!TAILQ_EMPTY(&owindows)) { ow = TAILQ_FIRST(&owindows); @@ -245,7 +273,7 @@ void cmd_criteria_init(I3_CMD) { } TAILQ_INIT(&owindows); /* copy all_cons */ - TAILQ_FOREACH(con, &all_cons, all_cons) { + TAILQ_FOREACH (con, &all_cons, all_cons) { ow = smalloc(sizeof(owindow)); ow->con = con; TAILQ_INSERT_TAIL(&owindows, ow, owindows); @@ -277,15 +305,16 @@ void cmd_criteria_match_windows(I3_CMD) { if (current_match->con_id == current->con) { DLOG("matches container!\n"); TAILQ_INSERT_TAIL(&owindows, current, owindows); + } else { + DLOG("doesnt match\n"); + free(current); } } else if (current_match->mark != NULL && current->con->mark != NULL && regex_matches(current_match->mark, current->con->mark)) { DLOG("match by mark\n"); TAILQ_INSERT_TAIL(&owindows, current, owindows); } else { - if (current->con->window == NULL) - continue; - if (match_matches_window(current_match, current->con->window)) { + if (current->con->window && match_matches_window(current_match, current->con->window)) { DLOG("matches window!\n"); TAILQ_INSERT_TAIL(&owindows, current, owindows); } else { @@ -295,7 +324,7 @@ void cmd_criteria_match_windows(I3_CMD) { } } - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); } } @@ -319,7 +348,7 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) { } if (strcmp(ctype, "window_role") == 0) { - current_match->role = regex_new(cvalue); + current_match->window_role = regex_new(cvalue); return; } @@ -332,8 +361,8 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) { (end && *end != '\0')) { ELOG("Could not parse con id \"%s\"\n", cvalue); } else { - current_match->con_id = (Con*)parsed; - printf("id as int = %p\n", current_match->con_id); + current_match->con_id = (Con *)parsed; + DLOG("id as int = %p\n", current_match->con_id); } return; } @@ -348,7 +377,7 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) { ELOG("Could not parse window id \"%s\"\n", cvalue); } else { current_match->id = parsed; - printf("window id as int = %d\n", current_match->id); + DLOG("window id as int = %d\n", current_match->id); } return; } @@ -394,7 +423,7 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) { * when criteria wasn't specified and we don't have any window focused. */ if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) || (match_is_empty(current_match) && focused->type == CT_WORKSPACE && - !con_has_children(focused))) { + !con_has_children(focused))) { ysuccess(false); return; } @@ -419,7 +448,7 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) { return; } - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_move_to_workspace(current->con, ws, true, false); } @@ -440,18 +469,13 @@ void cmd_move_con_to_workspace_back_and_forth(I3_CMD) { ws = workspace_back_and_forth_get(); if (ws == NULL) { - y(map_open); - ystr("success"); - y(bool, false); - ystr("error"); - ystr("No workspace was previously active."); - y(map_close); + yerror("No workspace was previously active."); return; } HANDLE_EMPTY_MATCH; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_move_to_workspace(current->con, ws, true, false); } @@ -466,8 +490,8 @@ void cmd_move_con_to_workspace_back_and_forth(I3_CMD) { * */ void cmd_move_con_to_workspace_name(I3_CMD, char *name) { - if (strncasecmp(name, "__i3_", strlen("__i3_")) == 0) { - LOG("You cannot switch to the i3 internal workspaces.\n"); + if (strncasecmp(name, "__", strlen("__")) == 0) { + LOG("You cannot move containers to i3-internal workspaces (\"%s\").\n", name); ysuccess(false); return; } @@ -481,9 +505,8 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) { ELOG("No windows match your criteria, cannot move.\n"); ysuccess(false); return; - } - else if (match_is_empty(current_match) && focused->type == CT_WORKSPACE && - !con_has_children(focused)) { + } else if (match_is_empty(current_match) && focused->type == CT_WORKSPACE && + !con_has_children(focused)) { ysuccess(false); return; } @@ -496,7 +519,7 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_move_to_workspace(current->con, ws, true, false); } @@ -518,7 +541,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) { * when criteria wasn't specified and we don't have any window focused. */ if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) || (match_is_empty(current_match) && focused->type == CT_WORKSPACE && - !con_has_children(focused))) { + !con_has_children(focused))) { ysuccess(false); return; } @@ -534,19 +557,14 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) { parsed_num < 0 || endptr == which) { LOG("Could not parse initial part of \"%s\" as a number.\n", which); - y(map_open); - ystr("success"); - y(bool, false); - ystr("error"); // TODO: better error message - ystr("Could not parse number"); - y(map_close); + yerror("Could not parse number"); return; } - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) + TAILQ_FOREACH (output, &(croot->nodes_head), nodes) GREP_FIRST(workspace, output_get_content(output), - child->num == parsed_num); + child->num == parsed_num); if (!workspace) { workspace = workspace_get(which, NULL); @@ -556,7 +574,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_move_to_workspace(current->con, workspace, true, false); } @@ -569,6 +587,23 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) { static void cmd_resize_floating(I3_CMD, char *way, char *direction, Con *floating_con, int px) { LOG("floating resize\n"); Rect old_rect = floating_con->rect; + Con *focused_con = con_descend_focused(floating_con); + + /* ensure that resize will take place even if pixel increment is smaller than + * height increment or width increment. + * fixes #1011 */ + if (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0 || + strcmp(direction, "height") == 0) { + if (px < 0) + px = (-px < focused_con->height_increment) ? -focused_con->height_increment : px; + else + px = (px < focused_con->height_increment) ? focused_con->height_increment : px; + } else if (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0) { + if (px < 0) + px = (-px < focused_con->width_increment) ? -focused_con->width_increment : px; + else + px = (px < focused_con->width_increment) ? focused_con->width_increment : px; + } if (strcmp(direction, "up") == 0) { floating_con->rect.height += px; @@ -600,79 +635,50 @@ static void cmd_resize_floating(I3_CMD, char *way, char *direction, Con *floatin static bool cmd_resize_tiling_direction(I3_CMD, Con *current, char *way, char *direction, int ppt) { LOG("tiling resize\n"); - /* get the appropriate current container (skip stacked/tabbed cons) */ - Con *other = NULL; - double percentage = 0; - while (current->parent->layout == L_STACKED || - current->parent->layout == L_TABBED) - current = current->parent; - - /* Then further go up until we find one with the matching orientation. */ - orientation_t search_orientation = - (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0 ? HORIZ : VERT); - - do { - if (con_orientation(current->parent) != search_orientation) { - current = current->parent; - continue; - } - - /* get the default percentage */ - int children = con_num_children(current->parent); - LOG("ins. %d children\n", children); - percentage = 1.0 / children; - LOG("default percentage = %f\n", percentage); - - orientation_t orientation = con_orientation(current->parent); - - if ((orientation == HORIZ && - (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) || - (orientation == VERT && - (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) { - LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n", - (orientation == HORIZ ? "horizontal" : "vertical")); - ysuccess(false); - return false; - } - - if (strcmp(direction, "up") == 0 || strcmp(direction, "left") == 0) { - other = TAILQ_PREV(current, nodes_head, nodes); - } else { - other = TAILQ_NEXT(current, nodes); - } - if (other == TAILQ_END(workspaces)) { - LOG("No other container in this direction found, trying to look further up in the tree...\n"); - current = current->parent; - continue; - } - break; - } while (current->type != CT_WORKSPACE && - current->type != CT_FLOATING_CON); + Con *second = NULL; + Con *first = current; + direction_t search_direction; + if (!strcmp(direction, "left")) + search_direction = D_LEFT; + else if (!strcmp(direction, "right")) + search_direction = D_RIGHT; + else if (!strcmp(direction, "up")) + search_direction = D_UP; + else + search_direction = D_DOWN; - if (other == NULL) { - LOG("No other container in this direction found, trying to look further up in the tree...\n"); + bool res = resize_find_tiling_participants(&first, &second, search_direction); + if (!res) { + LOG("No second container in this direction found.\n"); ysuccess(false); return false; } - LOG("other->percent = %f\n", other->percent); - LOG("current->percent before = %f\n", current->percent); - if (current->percent == 0.0) - current->percent = percentage; - if (other->percent == 0.0) - other->percent = percentage; - double new_current_percent = current->percent + ((double)ppt / 100.0); - double new_other_percent = other->percent - ((double)ppt / 100.0); - LOG("new_current_percent = %f\n", new_current_percent); - LOG("new_other_percent = %f\n", new_other_percent); + /* get the default percentage */ + int children = con_num_children(first->parent); + LOG("ins. %d children\n", children); + double percentage = 1.0 / children; + LOG("default percentage = %f\n", percentage); + + /* resize */ + LOG("second->percent = %f\n", second->percent); + LOG("first->percent before = %f\n", first->percent); + if (first->percent == 0.0) + first->percent = percentage; + if (second->percent == 0.0) + second->percent = percentage; + double new_first_percent = first->percent + ((double)ppt / 100.0); + double new_second_percent = second->percent - ((double)ppt / 100.0); + LOG("new_first_percent = %f\n", new_first_percent); + LOG("new_second_percent = %f\n", new_second_percent); /* Ensure that the new percentages are positive and greater than * 0.05 to have a reasonable minimum size. */ - if (definitelyGreaterThan(new_current_percent, 0.05, DBL_EPSILON) && - definitelyGreaterThan(new_other_percent, 0.05, DBL_EPSILON)) { - current->percent += ((double)ppt / 100.0); - other->percent -= ((double)ppt / 100.0); - LOG("current->percent after = %f\n", current->percent); - LOG("other->percent after = %f\n", other->percent); + if (definitelyGreaterThan(new_first_percent, 0.05, DBL_EPSILON) && + definitelyGreaterThan(new_second_percent, 0.05, DBL_EPSILON)) { + first->percent += ((double)ppt / 100.0); + second->percent -= ((double)ppt / 100.0); + LOG("first->percent after = %f\n", first->percent); + LOG("second->percent after = %f\n", second->percent); } else { LOG("Not resizing, already at minimum size\n"); } @@ -722,7 +728,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char /* Ensure all the other children have a percentage set. */ Con *child; - TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) { + TAILQ_FOREACH (child, &(current->parent->nodes_head), nodes) { LOG("child->percent = %f (child %p)\n", child->percent, child); if (child->percent == 0.0) child->percent = percentage; @@ -734,7 +740,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char LOG("subtract_percent = %f\n", subtract_percent); /* Ensure that the new percentages are positive and greater than * 0.05 to have a reasonable minimum size. */ - TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) { + TAILQ_FOREACH (child, &(current->parent->nodes_head), nodes) { if (child == current) continue; if (!definitelyGreaterThan(child->percent - subtract_percent, 0.05, DBL_EPSILON)) { @@ -752,7 +758,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char current->percent += ((double)ppt / 100.0); LOG("current->percent after = %f\n", current->percent); - TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) { + TAILQ_FOREACH (child, &(current->parent->nodes_head), nodes) { if (child == current) continue; child->percent -= subtract_percent; @@ -780,7 +786,13 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { + /* Don't handle dock windows (issue #1201) */ + if (current->con->window && current->con->window->dock) { + DLOG("This is a dock window. Not resizing (con = %p)\n)", current->con); + continue; + } + Con *floating_con; if ((floating_con = con_inside_floating(current->con))) { cmd_resize_floating(current_match, cmd_output, way, direction, floating_con, px); @@ -805,13 +817,13 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz * Implementation of 'border normal|none|1pixel|toggle|pixel'. * */ -void cmd_border(I3_CMD, char *border_style_str, char *border_width ) { +void cmd_border(I3_CMD, char *border_style_str, char *border_width) { DLOG("border style should be changed to %s with border width %s\n", border_style_str, border_width); owindow *current; HANDLE_EMPTY_MATCH; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); int border_style = current->con->border_style; char *end; @@ -835,7 +847,7 @@ void cmd_border(I3_CMD, char *border_style_str, char *border_width ) { border_style = BS_NORMAL; else if (strcmp(border_style_str, "pixel") == 0) border_style = BS_PIXEL; - else if (strcmp(border_style_str, "1pixel") == 0){ + else if (strcmp(border_style_str, "1pixel") == 0) { border_style = BS_PIXEL; tmp_border_width = 1; } else if (strcmp(border_style_str, "none") == 0) @@ -870,11 +882,39 @@ void cmd_nop(I3_CMD, char *comment) { */ void cmd_append_layout(I3_CMD, char *path) { LOG("Appending layout \"%s\"\n", path); - tree_append_json(path); + Con *parent = focused; + /* We need to append the layout to a split container, since a leaf + * container must not have any children (by definition). + * Note that we explicitly check for workspaces, since they are okay for + * this purpose, but con_accepts_window() returns false for workspaces. */ + while (parent->type != CT_WORKSPACE && !con_accepts_window(parent)) + parent = parent->parent; + DLOG("Appending to parent=%p instead of focused=%p\n", + parent, focused); + char *errormsg = NULL; + tree_append_json(parent, path, &errormsg); + if (errormsg != NULL) { + yerror(errormsg); + free(errormsg); + /* Note that we continue executing since tree_append_json() has + * side-effects — user-provided layouts can be partly valid, partly + * invalid, leading to half of the placeholder containers being + * created. */ + } else { + ysuccess(true); + } + + // XXX: This is a bit of a kludge. Theoretically, render_con(parent, + // false); should be enough, but when sending 'workspace 4; append_layout + // /tmp/foo.json', the needs_tree_render == true of the workspace command + // is not executed yet and will be batched with append_layout’s + // needs_tree_render after the parser finished. We should check if that is + // necessary at all. + render_con(croot, false); + + restore_open_placeholder_windows(parent); cmd_output->needs_tree_render = true; - // XXX: default reply for now, make this a better reply - ysuccess(true); } /* @@ -921,20 +961,15 @@ void cmd_workspace_number(I3_CMD, char *which) { parsed_num < 0 || endptr == which) { LOG("Could not parse initial part of \"%s\" as a number.\n", which); - y(map_open); - ystr("success"); - y(bool, false); - ystr("error"); // TODO: better error message - ystr("Could not parse number"); - y(map_close); + yerror("Could not parse number"); return; } - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) + TAILQ_FOREACH (output, &(croot->nodes_head), nodes) GREP_FIRST(workspace, output_get_content(output), - child->num == parsed_num); + child->num == parsed_num); if (!workspace) { LOG("There is no workspace with number %ld, creating a new one.\n", parsed_num); @@ -969,15 +1004,15 @@ void cmd_workspace_back_and_forth(I3_CMD) { * */ void cmd_workspace_name(I3_CMD, char *name) { - if (strncasecmp(name, "__i3_", strlen("__i3_")) == 0) { - LOG("You cannot switch to the i3 internal workspaces.\n"); + if (strncasecmp(name, "__", strlen("__")) == 0) { + LOG("You cannot switch to the i3-internal workspaces (\"%s\").\n", name); ysuccess(false); return; } DLOG("should switch to workspace %s\n", name); if (maybe_back_and_forth(cmd_output, name)) - return; + return; workspace_show_by_name(name); cmd_output->needs_tree_render = true; @@ -993,7 +1028,7 @@ void cmd_mark(I3_CMD, char *mark) { DLOG("Clearing all windows which have that mark first\n"); Con *con; - TAILQ_FOREACH(con, &all_cons, all_cons) { + TAILQ_FOREACH (con, &all_cons, all_cons) { if (con->mark && strcmp(con->mark, mark) == 0) FREE(con->mark); } @@ -1003,7 +1038,7 @@ void cmd_mark(I3_CMD, char *mark) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); current->con->mark = sstrdup(mark); } @@ -1013,6 +1048,31 @@ void cmd_mark(I3_CMD, char *mark) { ysuccess(true); } +/* + * Implementation of 'unmark [mark]' + * + */ +void cmd_unmark(I3_CMD, char *mark) { + if (mark == NULL) { + Con *con; + TAILQ_FOREACH (con, &all_cons, all_cons) { + FREE(con->mark); + } + DLOG("removed all window marks"); + } else { + Con *con; + TAILQ_FOREACH (con, &all_cons, all_cons) { + if (con->mark && strcmp(con->mark, mark) == 0) + FREE(con->mark); + } + DLOG("removed window mark %s\n", mark); + } + + cmd_output->needs_tree_render = true; + // XXX: default reply for now, make this a better reply + ysuccess(true); +} + /* * Implementation of 'mode '. * @@ -1041,8 +1101,8 @@ void cmd_move_con_to_output(I3_CMD, char *name) { Output *output; // TODO: fix the handling of criteria - TAILQ_FOREACH(current, &owindows, owindows) - current_output = get_output_containing(current->con->rect.x, current->con->rect.y); + TAILQ_FOREACH (current, &owindows, owindows) + current_output = get_output_of_con(current->con); assert(current_output != NULL); @@ -1072,7 +1132,7 @@ void cmd_move_con_to_output(I3_CMD, char *name) { return; } - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_move_to_workspace(current->con, ws, true, false); } @@ -1093,7 +1153,7 @@ void cmd_floating(I3_CMD, char *floating_mode) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); if (strcmp(floating_mode, "toggle") == 0) { DLOG("should toggle mode\n"); @@ -1123,9 +1183,8 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { - Output *current_output = get_output_containing(current->con->rect.x, - current->con->rect.y); + TAILQ_FOREACH (current, &owindows, owindows) { + Output *current_output = get_output_of_con(current->con); if (!current_output) { ELOG("Cannot get current output. This is a bug in i3.\n"); ysuccess(false); @@ -1154,13 +1213,13 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { /* check if we can find a workspace assigned to this output */ bool used_assignment = false; struct Workspace_Assignment *assignment; - TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { + TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) { if (strcmp(assignment->output, current_output->name) != 0) continue; /* check if this workspace is already attached to the tree */ Con *workspace = NULL, *out; - TAILQ_FOREACH(out, &(croot->nodes_head), nodes) + TAILQ_FOREACH (out, &(croot->nodes_head), nodes) GREP_FIRST(workspace, output_get_content(out), !strcasecmp(child->name, assignment->name)); if (workspace != NULL) @@ -1197,7 +1256,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { /* fix the coordinates of the floating containers */ Con *floating_con; - TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows) + TAILQ_FOREACH (floating_con, &(ws->floating_head), floating_windows) floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect)); ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"move\"}"); @@ -1211,7 +1270,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { * focus order/number of other workspaces on the output. * Instead, we loop through the available workspaces and only work with * previously_visible_ws if we still find it. */ - TAILQ_FOREACH(ws, &(content->nodes_head), nodes) { + TAILQ_FOREACH (ws, &(content->nodes_head), nodes) { if (ws != previously_visible_ws) continue; @@ -1239,7 +1298,7 @@ void cmd_split(I3_CMD, char *direction) { if (match_is_empty(current_match)) tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ)); else { - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ)); } @@ -1276,7 +1335,7 @@ void cmd_kill(I3_CMD, char *kill_mode_str) { if (match_is_empty(current_match)) tree_close_con(kill_mode); else { - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); tree_close(current->con, kill_mode, false, false); } @@ -1341,9 +1400,10 @@ void cmd_focus_window_mode(I3_CMD, char *window_mode) { current = TAILQ_FIRST(&(ws->focus_head)); if (current != NULL && current->type == CT_FLOATING_CON) window_mode = "tiling"; - else window_mode = "floating"; + else + window_mode = "floating"; } - TAILQ_FOREACH(current, &(ws->focus_head), focused) { + TAILQ_FOREACH (current, &(ws->focus_head), focused) { if ((strcmp(window_mode, "floating") == 0 && current->type != CT_FLOATING_CON) || (strcmp(window_mode, "tiling") == 0 && current->type == CT_FLOATING_CON)) continue; @@ -1378,7 +1438,8 @@ void cmd_focus_level(I3_CMD, char *level) { } /* Focusing a child should always be allowed. */ - else success = level_down(); + else + success = level_down(); cmd_output->needs_tree_render = success; // XXX: default reply for now, make this a better reply @@ -1396,12 +1457,7 @@ void cmd_focus(I3_CMD) { ELOG("You have to specify which window/container should be focused.\n"); ELOG("Example: [class=\"urxvt\" title=\"irssi\"] focus\n"); - y(map_open); - ystr("success"); - y(bool, false); - ystr("error"); - ystr("You have to specify which window/container should be focused"); - y(map_close); + yerror("You have to specify which window/container should be focused"); return; } @@ -1409,7 +1465,7 @@ void cmd_focus(I3_CMD) { Con *__i3_scratch = workspace_get("__i3_scratch", NULL); int count = 0; owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { Con *ws = con_get_workspace(current->con); /* If no workspace could be found, this was a dock window. * Just skip it, you cannot focus dock windows. */ @@ -1458,7 +1514,8 @@ void cmd_focus(I3_CMD) { if (count > 1) LOG("WARNING: Your criteria for the focus command matches %d containers, " - "while only exactly one container can be focused at a time.\n", count); + "while only exactly one container can be focused at a time.\n", + count); cmd_output->needs_tree_render = true; // XXX: default reply for now, make this a better reply @@ -1477,8 +1534,8 @@ void cmd_fullscreen(I3_CMD, char *fullscreen_mode) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH(current, &owindows, owindows) { - printf("matching: %p / %s\n", current->con, current->con->name); + TAILQ_FOREACH (current, &owindows, owindows) { + DLOG("matching: %p / %s\n", current->con, current->con->name); con_toggle_fullscreen(current->con, (strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT)); } @@ -1511,10 +1568,7 @@ void cmd_move_direction(I3_CMD, char *direction, char *move_px) { } floating_reposition(focused->parent, newrect); } else { - tree_move((strcmp(direction, "right") == 0 ? D_RIGHT : - (strcmp(direction, "left") == 0 ? D_LEFT : - (strcmp(direction, "up") == 0 ? D_UP : - D_DOWN)))); + tree_move((strcmp(direction, "right") == 0 ? D_RIGHT : (strcmp(direction, "left") == 0 ? D_LEFT : (strcmp(direction, "up") == 0 ? D_UP : D_DOWN)))); cmd_output->needs_tree_render = true; } @@ -1530,7 +1584,7 @@ void cmd_layout(I3_CMD, char *layout_str) { if (strcmp(layout_str, "stacking") == 0) layout_str = "stacked"; owindow *current; - int layout; + layout_t layout; /* default is a special case which will be handled in con_set_layout(). */ if (strcmp(layout_str, "default") == 0) layout = L_DEFAULT; @@ -1553,7 +1607,7 @@ void cmd_layout(I3_CMD, char *layout_str) { if (match_is_empty(current_match)) con_set_layout(focused, layout); else { - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_set_layout(current->con, layout); } @@ -1580,7 +1634,7 @@ void cmd_layout_toggle(I3_CMD, char *toggle_mode) { if (match_is_empty(current_match)) con_toggle_layout(focused, toggle_mode); else { - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_toggle_layout(current->con, toggle_mode); } @@ -1597,6 +1651,8 @@ void cmd_layout_toggle(I3_CMD, char *toggle_mode) { */ void cmd_exit(I3_CMD) { LOG("Exiting due to user command.\n"); + ipc_shutdown(); + unlink(config.ipc_socket_path); xcb_disconnect(conn); exit(0); @@ -1615,6 +1671,8 @@ void cmd_reload(I3_CMD) { x_set_i3_atoms(); /* Send an IPC event just in case the ws names have changed */ ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}"); + /* Send an update event for the barconfig just in case it has changed */ + update_barconfig(); // XXX: default reply for now, make this a better reply ysuccess(true); @@ -1626,6 +1684,14 @@ void cmd_reload(I3_CMD) { */ void cmd_restart(I3_CMD) { LOG("restarting i3\n"); + ipc_shutdown(); + /* We need to call this manually since atexit handlers don’t get called + * when exec()ing */ + purge_zerobyte_logfile(); + /* The unlink call is intentionally after the purge_zerobyte_logfile() so + * that the latter does not remove the directory yet. We need to store the + * restart layout state in there. */ + unlink(config.ipc_socket_path); i3_restart(false); // XXX: default reply for now, make this a better reply @@ -1667,8 +1733,8 @@ void cmd_focus_output(I3_CMD, char *name) { Output *current_output = NULL; Output *output; - TAILQ_FOREACH(current, &owindows, owindows) - current_output = get_output_containing(current->con->rect.x, current->con->rect.y); + TAILQ_FOREACH (current, &owindows, owindows) + current_output = get_output_of_con(current->con); assert(current_output != NULL); output = get_output_from_string(current_output, name); @@ -1699,18 +1765,12 @@ void cmd_focus_output(I3_CMD, char *name) { * */ void cmd_move_window_to_position(I3_CMD, char *method, char *cx, char *cy) { - int x = atoi(cx); int y = atoi(cy); if (!con_is_floating(focused)) { ELOG("Cannot change position. The window/container is not floating\n"); - y(map_open); - ystr("success"); - y(bool, false); - ystr("error"); - ystr("Cannot change position. The window/container is not floating."); - y(map_close); + yerror("Cannot change position. The window/container is not floating."); return; } @@ -1742,23 +1802,18 @@ void cmd_move_window_to_position(I3_CMD, char *method, char *cx, char *cy) { * */ void cmd_move_window_to_center(I3_CMD, char *method) { - if (!con_is_floating(focused)) { ELOG("Cannot change position. The window/container is not floating\n"); - y(map_open); - ystr("success"); - y(bool, false); - ystr("error"); - ystr("Cannot change position. The window/container is not floating."); - y(map_close); + yerror("Cannot change position. The window/container is not floating."); + return; } if (strcmp(method, "absolute") == 0) { Rect *rect = &focused->parent->rect; DLOG("moving to absolute center\n"); - rect->x = croot->rect.width/2 - rect->width/2; - rect->y = croot->rect.height/2 - rect->height/2; + rect->x = croot->rect.width / 2 - rect->width / 2; + rect->y = croot->rect.height / 2 - rect->height / 2; floating_maybe_reassign_ws(focused->parent); cmd_output->needs_tree_render = true; @@ -1769,8 +1824,8 @@ void cmd_move_window_to_center(I3_CMD, char *method) { Rect newrect = focused->parent->rect; DLOG("moving to center\n"); - newrect.x = wsrect->width/2 - newrect.width/2; - newrect.y = wsrect->height/2 - newrect.height/2; + newrect.x = wsrect->width / 2 - newrect.width / 2; + newrect.y = wsrect->height / 2 - newrect.height / 2; floating_reposition(focused->parent, newrect); } @@ -1789,7 +1844,7 @@ void cmd_move_scratchpad(I3_CMD) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); scratchpad_move(current->con); } @@ -1810,7 +1865,7 @@ void cmd_scratchpad_show(I3_CMD) { if (match_is_empty(current_match)) { scratchpad_show(NULL); } else { - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); scratchpad_show(current->con); } @@ -1826,6 +1881,11 @@ void cmd_scratchpad_show(I3_CMD) { * */ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { + if (strncasecmp(new_name, "__", strlen("__")) == 0) { + LOG("Cannot rename workspace to \"%s\": names starting with __ are i3-internal.", new_name); + ysuccess(false); + return; + } if (old_name) { LOG("Renaming workspace \"%s\" to \"%s\"\n", old_name, new_name); } else { @@ -1834,9 +1894,9 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { Con *output, *workspace = NULL; if (old_name) { - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) + TAILQ_FOREACH (output, &(croot->nodes_head), nodes) GREP_FIRST(workspace, output_get_content(output), - !strcasecmp(child->name, old_name)); + !strcasecmp(child->name, old_name)); } else { workspace = con_get_workspace(focused); } @@ -1844,31 +1904,21 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { if (!workspace) { // TODO: we should include the old workspace name here and use yajl for // generating the reply. - y(map_open); - ystr("success"); - y(bool, false); - ystr("error"); // TODO: better error message - ystr("Old workspace not found"); - y(map_close); + yerror("Old workspace not found"); return; } Con *check_dest = NULL; - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) + TAILQ_FOREACH (output, &(croot->nodes_head), nodes) GREP_FIRST(check_dest, output_get_content(output), - !strcasecmp(child->name, new_name)); + !strcasecmp(child->name, new_name)); if (check_dest != NULL) { // TODO: we should include the new workspace name here and use yajl for // generating the reply. - y(map_open); - ystr("success"); - y(bool, false); - ystr("error"); // TODO: better error message - ystr("New workspace already exists"); - y(map_close); + yerror("New workspace already exists"); return; } @@ -1882,7 +1932,8 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { parsed_num < 0 || endptr == new_name) workspace->num = -1; - else workspace->num = parsed_num; + else + workspace->num = parsed_num; LOG("num = %d\n", workspace->num); /* By re-attaching, the sort order will be correct afterwards. */ @@ -1898,3 +1949,164 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}"); } + +/* + * Implementation of 'bar mode dock|hide|invisible|toggle []' + * + */ +bool cmd_bar_mode(char *bar_mode, char *bar_id) { + int mode = M_DOCK; + bool toggle = false; + if (strcmp(bar_mode, "dock") == 0) + mode = M_DOCK; + else if (strcmp(bar_mode, "hide") == 0) + mode = M_HIDE; + else if (strcmp(bar_mode, "invisible") == 0) + mode = M_INVISIBLE; + else if (strcmp(bar_mode, "toggle") == 0) + toggle = true; + else { + ELOG("Unknown bar mode \"%s\", this is a mismatch between code and parser spec.\n", bar_mode); + return false; + } + + bool changed_sth = false; + Barconfig *current = NULL; + TAILQ_FOREACH (current, &barconfigs, configs) { + if (bar_id && strcmp(current->id, bar_id) != 0) + continue; + + if (toggle) + mode = (current->mode + 1) % 2; + + DLOG("Changing bar mode of bar_id '%s' to '%s (%d)'\n", current->id, bar_mode, mode); + current->mode = mode; + changed_sth = true; + + if (bar_id) + break; + } + + if (bar_id && !changed_sth) { + DLOG("Changing bar mode of bar_id %s failed, bar_id not found.\n", bar_id); + return false; + } + + return true; +} + +/* + * Implementation of 'bar hidden_state hide|show|toggle []' + * + */ +bool cmd_bar_hidden_state(char *bar_hidden_state, char *bar_id) { + int hidden_state = S_SHOW; + bool toggle = false; + if (strcmp(bar_hidden_state, "hide") == 0) + hidden_state = S_HIDE; + else if (strcmp(bar_hidden_state, "show") == 0) + hidden_state = S_SHOW; + else if (strcmp(bar_hidden_state, "toggle") == 0) + toggle = true; + else { + ELOG("Unknown bar state \"%s\", this is a mismatch between code and parser spec.\n", bar_hidden_state); + return false; + } + + bool changed_sth = false; + Barconfig *current = NULL; + TAILQ_FOREACH (current, &barconfigs, configs) { + if (bar_id && strcmp(current->id, bar_id) != 0) + continue; + + if (toggle) + hidden_state = (current->hidden_state + 1) % 2; + + DLOG("Changing bar hidden_state of bar_id '%s' to '%s (%d)'\n", current->id, bar_hidden_state, hidden_state); + current->hidden_state = hidden_state; + changed_sth = true; + + if (bar_id) + break; + } + + if (bar_id && !changed_sth) { + DLOG("Changing bar hidden_state of bar_id %s failed, bar_id not found.\n", bar_id); + return false; + } + + return true; +} + +/* + * Implementation of 'bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) []' + * + */ +void cmd_bar(I3_CMD, char *bar_type, char *bar_value, char *bar_id) { + bool ret; + if (strcmp(bar_type, "mode") == 0) + ret = cmd_bar_mode(bar_value, bar_id); + else if (strcmp(bar_type, "hidden_state") == 0) + ret = cmd_bar_hidden_state(bar_value, bar_id); + else { + ELOG("Unknown bar option type \"%s\", this is a mismatch between code and parser spec.\n", bar_type); + ret = false; + } + + ysuccess(ret); + if (!ret) + return; + + update_barconfig(); +} + +/* + * Implementation of 'shmlog |toggle|on|off' + * + */ +void cmd_shmlog(I3_CMD, char *argument) { + if (!strcmp(argument, "toggle")) + /* Toggle shm log, if size is not 0. If it is 0, set it to default. */ + shmlog_size = shmlog_size ? -shmlog_size : default_shmlog_size; + else if (!strcmp(argument, "on")) + shmlog_size = default_shmlog_size; + else if (!strcmp(argument, "off")) + shmlog_size = 0; + else { + /* If shm logging now, restart logging with the new size. */ + if (shmlog_size > 0) { + shmlog_size = 0; + LOG("Restarting shm logging...\n"); + init_logging(); + } + shmlog_size = atoi(argument); + /* Make a weakly attempt at ensuring the argument is valid. */ + if (shmlog_size <= 0) + shmlog_size = default_shmlog_size; + } + LOG("%s shm logging\n", shmlog_size > 0 ? "Enabling" : "Disabling"); + init_logging(); + update_shmlog_atom(); + // XXX: default reply for now, make this a better reply + ysuccess(true); +} + +/* + * Implementation of 'debuglog toggle|on|off' + * + */ +void cmd_debuglog(I3_CMD, char *argument) { + bool logging = get_debug_logging(); + if (!strcmp(argument, "toggle")) { + LOG("%s debug logging\n", logging ? "Disabling" : "Enabling"); + set_debug_logging(!logging); + } else if (!strcmp(argument, "on") && !logging) { + LOG("Enabling debug logging\n"); + set_debug_logging(true); + } else if (!strcmp(argument, "off") && logging) { + LOG("Disabling debug logging\n"); + set_debug_logging(false); + } + // XXX: default reply for now, make this a better reply + ysuccess(true); +}