X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Ftree.c;h=9c8f56026f7baf95a1d2e0405ca5efb8a4d3bc82;hb=617afc67a25c9bb7bf91a7659319e07fbe32d758;hp=5559908f0738d20afbbed8e9b5f048a7609a4e65;hpb=d4523de6c8f7d930d52ce69d94ff186a29c5e803;p=i3%2Fi3 diff --git a/src/tree.c b/src/tree.c index 5559908f..9c8f5602 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 * @@ -19,7 +21,7 @@ struct all_cons_head all_cons = TAILQ_HEAD_INITIALIZER(all_cons); * __i3_scratch will live there. * */ -static Con *_create___i3() { +static Con *_create___i3(void) { Con *__i3 = con_new(croot, NULL); FREE(__i3->name); __i3->name = sstrdup("__i3"); @@ -29,7 +31,9 @@ static Con *_create___i3() { 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; @@ -39,6 +43,7 @@ static Con *_create___i3() { content->type = CT_CON; FREE(content->name); content->name = sstrdup("content"); + content->layout = L_SPLITH; x_set_name(content, "[i3 con] content __i3"); con_attach(content, __i3, false); @@ -48,6 +53,7 @@ static Con *_create___i3() { ws->type = CT_WORKSPACE; ws->num = -1; ws->name = sstrdup("__i3_scratch"); + ws->layout = L_SPLITH; con_attach(ws, content, false); x_set_name(ws, "[i3 con] workspace __i3_scratch"); ws->fullscreen_mode = CF_OUTPUT; @@ -78,15 +84,15 @@ bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) { }; 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. */ @@ -112,6 +118,7 @@ void tree_init(xcb_get_geometry_reply_t *geometry) { FREE(croot->name); croot->name = "root"; croot->type = CT_ROOT; + croot->layout = L_SPLITH; croot->rect = (Rect){ geometry->x, geometry->y, @@ -151,6 +158,7 @@ Con *tree_open_con(Con *con, i3Window *window) { /* 3. create the container and attach it to its parent */ Con *new = con_new(con, window); + new->layout = L_SPLITH; /* 4: re-calculate child->percent for each child */ con_fix_percent(con); @@ -192,6 +200,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); @@ -220,7 +235,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. @@ -239,36 +259,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,7 +341,7 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool } } else { - DLOG("not focusing because we're not killing anybody"); + DLOG("not focusing because we're not killing anybody\n"); } } else { DLOG("not focusing, was not mapped\n"); @@ -315,15 +360,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); } @@ -334,13 +388,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->orientation = orientation; + 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. */ @@ -351,8 +415,9 @@ void tree_split(Con *con, orientation_t orientation) { * child (its split functionality is unused so far), we just change the * orientation (more intuitive than splitting again) */ if (con_num_children(parent) == 1 && - parent->layout == L_DEFAULT) { - parent->orientation = orientation; + (parent->layout == L_SPLITH || + parent->layout == L_SPLITV)) { + parent->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV; DLOG("Just changing orientation of existing container\n"); return; } @@ -364,7 +429,7 @@ void tree_split(Con *con, orientation_t orientation) { TAILQ_REPLACE(&(parent->nodes_head), con, new, nodes); TAILQ_REPLACE(&(parent->focus_head), con, new, focused); new->parent = parent; - new->orientation = orientation; + new->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV; /* 3: swap 'percent' (resize factor) */ new->percent = con->percent; @@ -375,38 +440,53 @@ void tree_split(Con *con, orientation_t orientation) { } /* - * Moves focus one level up. + * Moves focus one level up. Returns true if focus changed. * */ -void level_up() { - /* We cannot go up when we are in fullscreen mode at the moment, that would - * be totally not intuitive */ - if (focused->fullscreen_mode != CF_NONE) { - LOG("Currently in fullscreen, not going up\n"); - return; +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"); - return; + ELOG("'focus parent': Focus is already on the workspace, cannot go higher than that.\n"); + return false; } con_focus(focused->parent); + return true; } /* - * Moves focus one level down. + * Moves focus one level down. Returns true if focus changed. * */ -void level_down() { +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"); - return; + 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; } static void mark_unmapped(Con *con) { @@ -428,7 +508,7 @@ static void mark_unmapped(Con *con) { * pushing the changes to X11 using x_push_changes(). * */ -void tree_render() { +void tree_render(void) { if (croot == NULL) return; @@ -449,9 +529,20 @@ void tree_render() { * */ 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; @@ -472,7 +563,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); @@ -486,6 +577,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); @@ -560,6 +658,10 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) else next = TAILQ_LAST(&(parent->nodes_head), nodes_head); } + /* Don't violate fullscreen focus restrictions. */ + if (!con_fullscreen_permits_focusing(next)) + return false; + /* 3: focus choice comes in here. at the moment we will go down * until we find a window */ /* TODO: check for window, atm we only go down as far as possible */ @@ -594,7 +696,9 @@ void tree_flatten(Con *con) { DLOG("Checking if I can flatten con = %p / %s\n", con, con->name); /* We only consider normal containers without windows */ - if (con->type != CT_CON || con->window != NULL) + if (con->type != CT_CON || + parent->layout == L_OUTPUT || /* con == "content" */ + con->window != NULL) goto recurse; /* Ensure it got only one child */ @@ -602,12 +706,16 @@ void tree_flatten(Con *con) { if (child == NULL || TAILQ_NEXT(child, nodes) != NULL) goto recurse; + DLOG("child = %p, con = %p, parent = %p\n", child, con, parent); + /* The child must have a different orientation than the con but the same as * the con’s parent to be redundant */ - if (con->orientation == NO_ORIENTATION || - child->orientation == NO_ORIENTATION || - con->orientation == child->orientation || - child->orientation != parent->orientation) + 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; DLOG("Alright, I have to flatten this situation now. Stay calm.\n");