X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Ftree.c;h=da73e14d76fc12b996a76ed3283cdb7410e62f28;hb=92000942039fa99d7334ca5099b467b0d3d17792;hp=068b05704e8981f6f932e0c1932b244686abae2c;hpb=de94f6da1a9f7d2b9701035b844154658d9d308f;p=i3%2Fi3 diff --git a/src/tree.c b/src/tree.c index 068b0570..da73e14d 100644 --- a/src/tree.c +++ b/src/tree.c @@ -1,3 +1,5 @@ +#undef I3__FILE__ +#define I3__FILE__ "tree.c" /* * vim:ts=4:sw=4:expandtab * @@ -29,7 +31,9 @@ static Con *_create___i3(void) { x_set_name(__i3, "[i3 con] pseudo-output __i3"); /* For retaining the correct position/size of a scratchpad window, the * dimensions of the real outputs should be multiples of the __i3 - * pseudo-output. */ + * pseudo-output. Ensuring that is the job of scratchpad_fix_resolution() + * which gets called after this function and after detecting all the + * outputs (or whenever an output changes). */ __i3->rect.width = 1280; __i3->rect.height = 1024; @@ -72,23 +76,22 @@ bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) { /* TODO: refactor the following */ croot = con_new(NULL, NULL); - croot->rect = (Rect){ + croot->rect = (Rect) { geometry->x, geometry->y, geometry->width, - geometry->height - }; + geometry->height}; focused = croot; - tree_append_json(globbed); + tree_append_json(focused, globbed, NULL); - printf("appended tree, using new root\n"); + DLOG("appended tree, using new root\n"); croot = TAILQ_FIRST(&(croot->nodes_head)); - printf("new root = %p\n", croot); + DLOG("new root = %p\n", croot); Con *out = TAILQ_FIRST(&(croot->nodes_head)); - printf("out = %p\n", out); + DLOG("out = %p\n", out); Con *ws = TAILQ_FIRST(&(out->nodes_head)); - printf("ws = %p\n", ws); + DLOG("ws = %p\n", ws); /* For in-place restarting into v4.2, we need to make sure the new * pseudo-output __i3 is present. */ @@ -115,12 +118,11 @@ void tree_init(xcb_get_geometry_reply_t *geometry) { croot->name = "root"; croot->type = CT_ROOT; croot->layout = L_SPLITH; - croot->rect = (Rect){ + croot->rect = (Rect) { geometry->x, geometry->y, geometry->width, - geometry->height - }; + geometry->height}; _create___i3(); } @@ -165,7 +167,7 @@ Con *tree_open_con(Con *con, i3Window *window) { static bool _is_con_mapped(Con *con) { Con *child; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) + TAILQ_FOREACH (child, &(con->nodes_head), nodes) if (_is_con_mapped(child)) return true; @@ -196,6 +198,13 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool was_mapped = _is_con_mapped(con); } + /* remove the urgency hint of the workspace (if set) */ + if (con->urgent) { + con->urgent = false; + con_update_parents_urgency(con); + workspace_update_urgent_flag(con_get_workspace(con)); + } + /* Get the container which is next focused */ Con *next = con_next_focused(con); DLOG("next = %p, focused = %p\n", next, focused); @@ -205,7 +214,7 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool bool abort_kill = false; /* We cannot use TAILQ_FOREACH because the children get deleted * in their parent’s nodes_head */ - for (child = TAILQ_FIRST(&(con->nodes_head)); child; ) { + for (child = TAILQ_FIRST(&(con->nodes_head)); child;) { nextchild = TAILQ_NEXT(child, nodes); DLOG("killing child=%p\n", child); if (!tree_close(child, kill_window, true, false)) @@ -224,7 +233,12 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool return false; } else { xcb_void_cookie_t cookie; - /* un-parent the window */ + /* Ignore any further events by clearing the event mask, + * unmap the window, + * then reparent it to the root window. */ + xcb_change_window_attributes(conn, con->window->id, + XCB_CW_EVENT_MASK, (uint32_t[]) {XCB_NONE}); + xcb_unmap_window(conn, con->window->id); cookie = xcb_reparent_window(conn, con->window->id, root, 0, 0); /* Ignore X11 errors for the ReparentWindow request. @@ -233,9 +247,9 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool /* We are no longer handling this window, thus set WM_STATE to * WM_STATE_WITHDRAWN (see ICCCM 4.1.3.1) */ - long data[] = { XCB_ICCCM_WM_STATE_WITHDRAWN, XCB_NONE }; + long data[] = {XCB_ICCCM_WM_STATE_WITHDRAWN, XCB_NONE}; cookie = xcb_change_property(conn, XCB_PROP_MODE_REPLACE, - con->window->id, A_WM_STATE, A_WM_STATE, 32, 2, data); + con->window->id, A_WM_STATE, A_WM_STATE, 32, 2, data); /* Ignore X11 errors for the ReparentWindow request. * X11 Errors are returned when the window was already destroyed */ @@ -243,36 +257,61 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool } FREE(con->window->class_class); FREE(con->window->class_instance); - FREE(con->window->name_x); - FREE(con->window->name_json); - free(con->window); + i3string_free(con->window->name); + FREE(con->window->ran_assignments); + FREE(con->window); } - /* kill the X11 part of this container */ - x_con_kill(con); + Con *ws = con_get_workspace(con); + + /* Figure out which container to focus next before detaching 'con'. */ + if (con_is_floating(con)) { + if (con == focused) { + DLOG("This is the focused container, i need to find another one to focus. I start looking at ws = %p\n", ws); + next = con_next_focused(parent); + dont_kill_parent = true; + DLOG("Alright, focusing %p\n", next); + } else { + next = NULL; + } + } + + /* Detach the container so that it will not be rendered anymore. */ con_detach(con); + + /* disable urgency timer, if needed */ + if (con->urgency_timer != NULL) { + DLOG("Removing urgency timer of con %p\n", con); + workspace_update_urgent_flag(ws); + ev_timer_stop(main_loop, con->urgency_timer); + FREE(con->urgency_timer); + } + if (con->type != CT_FLOATING_CON) { /* If the container is *not* floating, we might need to re-distribute * percentage values for the resized containers. */ con_fix_percent(parent); } + /* Render the tree so that the surrounding containers take up the space + * which 'con' does no longer occupy. If we don’t render here, there will + * be a gap in our containers and that could trigger an EnterNotify for an + * underlying container, see ticket #660. + * + * Rendering has to be avoided when dont_kill_parent is set (when + * tree_close calls itself recursively) because the tree is in a + * non-renderable state during that time. */ + if (!dont_kill_parent) + tree_render(); + + /* kill the X11 part of this container */ + x_con_kill(con); + if (con_is_floating(con)) { - Con *ws = con_get_workspace(con); DLOG("Container was floating, killing floating container\n"); tree_close(parent, DONT_KILL_WINDOW, false, (con == focused)); DLOG("parent container killed\n"); - if (con == focused) { - DLOG("This is the focused container, i need to find another one to focus. I start looking at ws = %p\n", ws); - /* go down the focus stack as far as possible */ - next = con_descend_focused(ws); - - dont_kill_parent = true; - DLOG("Alright, focusing %p\n", next); - } else { - next = NULL; - } } free(con->name); @@ -296,10 +335,10 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool } else { if (!force_set_focus && con != focused) DLOG("not changing focus, the container was not focused before\n"); - else con_focus(next); + else + con_focus(next); } - } - else { + } else { DLOG("not focusing because we're not killing anybody\n"); } } else { @@ -319,15 +358,24 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool */ void tree_close_con(kill_window_t kill_window) { assert(focused != NULL); - if (focused->type == CT_WORKSPACE) { - LOG("Cannot close workspace\n"); - return; - } /* There *should* be no possibility to focus outputs / root container */ assert(focused->type != CT_OUTPUT); assert(focused->type != CT_ROOT); + if (focused->type == CT_WORKSPACE) { + DLOG("Workspaces cannot be close, closing all children instead\n"); + Con *child, *nextchild; + for (child = TAILQ_FIRST(&(focused->focus_head)); child;) { + nextchild = TAILQ_NEXT(child, focused); + DLOG("killing child=%p\n", child); + tree_close(child, kill_window, false, false); + child = nextchild; + } + + return; + } + /* Kill con */ tree_close(focused, kill_window, false, false); } @@ -338,13 +386,23 @@ void tree_close_con(kill_window_t kill_window) { * */ void tree_split(Con *con, orientation_t orientation) { - /* for a workspace, we just need to change orientation */ - if (con->type == CT_WORKSPACE) { - DLOG("Workspace, simply changing orientation to %d\n", orientation); - con->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV; + if (con_is_floating(con)) { + DLOG("Floating containers can't be split.\n"); return; } + if (con->type == CT_WORKSPACE) { + if (con_num_children(con) < 2) { + DLOG("Just changing orientation of workspace\n"); + con->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV; + return; + } else { + /* if there is more than one container on the workspace + * move them into a new container and handle this instead */ + con = workspace_encapsulate(con); + } + } + Con *parent = con->parent; /* Force re-rendering to make the indicator border visible. */ @@ -370,7 +428,6 @@ void tree_split(Con *con, orientation_t orientation) { TAILQ_REPLACE(&(parent->focus_head), con, new, focused); new->parent = parent; new->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV; - new->split = true; /* 3: swap 'percent' (resize factor) */ new->percent = con->percent; @@ -385,11 +442,18 @@ void tree_split(Con *con, orientation_t orientation) { * */ bool level_up(void) { + /* Skip over floating containers and go directly to the grandparent + * (which should always be a workspace) */ + if (focused->parent->type == CT_FLOATING_CON) { + con_focus(focused->parent->parent); + return true; + } + /* We can focus up to the workspace, but not any higher in the tree */ if ((focused->parent->type != CT_CON && - focused->parent->type != CT_WORKSPACE) || + focused->parent->type != CT_WORKSPACE) || focused->type == CT_WORKSPACE) { - LOG("Cannot go up any further\n"); + ELOG("'focus parent': Focus is already on the workspace, cannot go higher than that.\n"); return false; } con_focus(focused->parent); @@ -404,9 +468,19 @@ bool level_down(void) { /* Go down the focus stack of the current node */ Con *next = TAILQ_FIRST(&(focused->focus_head)); if (next == TAILQ_END(&(focused->focus_head))) { - printf("cannot go down\n"); + DLOG("cannot go down\n"); return false; + } else if (next->type == CT_FLOATING_CON) { + /* Floating cons shouldn't be directly focused; try immediately + * going to the grandchild of the focused con. */ + Con *child = TAILQ_FIRST(&(next->focus_head)); + if (child == TAILQ_END(&(next->focus_head))) { + DLOG("cannot go down\n"); + return false; + } else + next = TAILQ_FIRST(&(next->focus_head)); } + con_focus(next); return true; } @@ -415,12 +489,12 @@ static void mark_unmapped(Con *con) { Con *current; con->mapped = false; - TAILQ_FOREACH(current, &(con->nodes_head), nodes) + TAILQ_FOREACH (current, &(con->nodes_head), nodes) mark_unmapped(current); if (con->type == CT_WORKSPACE) { /* We need to call mark_unmapped on floating nodes aswell since we can * make containers floating. */ - TAILQ_FOREACH(current, &(con->floating_head), floating_windows) + TAILQ_FOREACH (current, &(con->floating_head), floating_windows) mark_unmapped(current); } } @@ -451,9 +525,20 @@ void tree_render(void) { * */ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) { + /* When dealing with fullscreen containers, it's necessary to go up to the + * workspace level, because 'focus $dir' will start at the con's real + * position in the tree, and it may not be possible to get to the edge + * normally due to fullscreen focusing restrictions. */ + if (con->fullscreen_mode == CF_OUTPUT && con->type != CT_WORKSPACE) + con = con_get_workspace(con); + /* Stop recursing at workspaces after attempting to switch to next * workspace if possible. */ if (con->type == CT_WORKSPACE) { + if (con_get_fullscreen_con(con, CF_GLOBAL)) { + DLOG("Cannot change workspace while in global fullscreen mode.\n"); + return false; + } Output *current_output = get_output_containing(con->rect.x, con->rect.y); Output *next_output; @@ -474,7 +559,7 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) else return false; - next_output = get_output_next(direction, current_output); + next_output = get_output_next(direction, current_output, CLOSEST_OUTPUT); if (!next_output) return false; DLOG("Next output is %s\n", next_output->name); @@ -488,6 +573,13 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) return false; workspace_show(workspace); + + /* If a workspace has an active fullscreen container, one of its + * children should always be focused. The above workspace_show() + * should be adequate for that, so return. */ + if (con_get_fullscreen_con(workspace, CF_OUTPUT)) + return true; + Con *focus = con_descend_direction(workspace, direction); if (focus) { con_focus(focus); @@ -504,13 +596,15 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) Con *next; if (way == 'n') next = TAILQ_NEXT(con, floating_windows); - else next = TAILQ_PREV(con, floating_head, floating_windows); + else + next = TAILQ_PREV(con, floating_head, floating_windows); /* If there is no next/previous container, wrap */ if (!next) { if (way == 'n') next = TAILQ_FIRST(&(parent->floating_head)); - else next = TAILQ_LAST(&(parent->floating_head), floating_head); + else + next = TAILQ_LAST(&(parent->floating_head), floating_head); } /* Still no next/previous container? bail out */ @@ -543,7 +637,8 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) Con *next; if (way == 'n') next = TAILQ_NEXT(current, nodes); - else next = TAILQ_PREV(current, nodes_head, nodes); + else + next = TAILQ_PREV(current, nodes_head, nodes); if (!next) { if (!config.force_focus_wrapping) { @@ -559,7 +654,8 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) if (way == 'n') next = TAILQ_FIRST(&(parent->nodes_head)); - else next = TAILQ_LAST(&(parent->nodes_head), nodes_head); + else + next = TAILQ_LAST(&(parent->nodes_head), nodes_head); } /* Don't violate fullscreen focus restrictions. */ @@ -614,8 +710,10 @@ void tree_flatten(Con *con) { /* The child must have a different orientation than the con but the same as * the con’s parent to be redundant */ - if (con->split || - child->split || + if (!con_is_split(con) || + !con_is_split(child) || + (con->layout != L_SPLITH && con->layout != L_SPLITV) || + (child->layout != L_SPLITH && child->layout != L_SPLITV) || con_orientation(con) == con_orientation(child) || con_orientation(child) != con_orientation(parent)) goto recurse;