X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;ds=sidebyside;f=src%2Fscratchpad.c;h=a4d2950519619bd563f804c36854c666f6cfcbad;hb=49979c9d15eb5095ce9dd47a757bf0cae26bc197;hp=b00d7f616e88b24f63047ec8735178cc4807035e;hpb=ed2bcc15e384be99225a247435f6cff678e64f0c;p=i3%2Fi3 diff --git a/src/scratchpad.c b/src/scratchpad.c index b00d7f61..a4d29505 100644 --- a/src/scratchpad.c +++ b/src/scratchpad.c @@ -1,8 +1,10 @@ +#undef I3__FILE__ +#define I3__FILE__ "scratchpad.c" /* * vim:ts=4:sw=4:expandtab * * i3 - an improved dynamic tiling window manager - * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) + * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE) * * scratchpad.c: Moving windows to the scratchpad and making them visible again. * @@ -37,15 +39,20 @@ void scratchpad_move(Con *con) { return; } - /* 1: Ensure the window is floating. From now on, we deal with the - * CT_FLOATING_CON. We use automatic == false because the user made the - * choice that this window should be a scratchpad (and floating). */ - floating_enable(con, false); - con = con->parent; + /* 1: Ensure the window or any parent is floating. From now on, we deal + * with the CT_FLOATING_CON. We use automatic == false because the user + * made the choice that this window should be a scratchpad (and floating). + */ + Con *maybe_floating_con = con_inside_floating(con); + if (maybe_floating_con == NULL) { + floating_enable(con, false); + con = con->parent; + } else { + con = maybe_floating_con; + } /* 2: Send the window to the __i3_scratch workspace, mainting its * coordinates and not warping the pointer. */ - Con *focus_next = con_next_focused(con); con_move_to_workspace(con, __i3_scratch, true, true); /* 3: If this is the first time this window is used as a scratchpad, we set @@ -55,10 +62,6 @@ void scratchpad_move(Con *con) { DLOG("This window was never used as a scratchpad before.\n"); con->scratchpad_state = SCRATCHPAD_FRESH; } - - /* 4: Fix focus. Normally, when moving a window to a different output, the - * destination output gets focused. In this case, we don’t want that. */ - con_focus(focus_next); } /* @@ -75,6 +78,51 @@ void scratchpad_show(Con *con) { Con *__i3_scratch = workspace_get("__i3_scratch", NULL); Con *floating; + /* If the current con or any of its parents are in fullscreen mode, we + * first need to disable it before showing the scratchpad con. */ + Con *fs = focused; + while (fs && fs->fullscreen_mode == CF_NONE) + fs = fs->parent; + + if (fs->type != CT_WORKSPACE) { + con_toggle_fullscreen(focused, CF_OUTPUT); + } + + /* If this was 'scratchpad show' without criteria, we check if there is a + * unfocused scratchpad on the current workspace and focus it */ + Con *walk_con; + Con *focused_ws = con_get_workspace(focused); + TAILQ_FOREACH(walk_con, &(focused_ws->floating_head), floating_windows) { + if ((floating = con_inside_floating(walk_con)) && + floating->scratchpad_state != SCRATCHPAD_NONE && + floating != con_inside_floating(focused)) { + DLOG("Found an unfocused scratchpad window on this workspace\n"); + DLOG("Focusing it: %p\n", walk_con); + /* use con_descend_tiling_focused to get the last focused + * window inside this scratch container in order to + * keep the focus the same within this container */ + con_focus(con_descend_tiling_focused(walk_con)); + return; + } + } + + /* If this was 'scratchpad show' without criteria, we check if there is a + * visible scratchpad window on another workspace. In this case we move it + * to the current workspace. */ + focused_ws = con_get_workspace(focused); + TAILQ_FOREACH(walk_con, &all_cons, all_cons) { + Con *walk_ws = con_get_workspace(walk_con); + if (walk_ws && + !con_is_internal(walk_ws) && focused_ws != walk_ws && + (floating = con_inside_floating(walk_con)) && + floating->scratchpad_state != SCRATCHPAD_NONE) { + DLOG("Found a visible scratchpad window on another workspace,\n"); + DLOG("moving it to this workspace: con = %p\n", walk_con); + con_move_to_workspace(walk_con, focused_ws, true, false); + return; + } + } + /* If this was 'scratchpad show' without criteria, we check if the * currently focused window is a scratchpad window and should be hidden * again. */ @@ -88,16 +136,21 @@ void scratchpad_show(Con *con) { /* If this was 'scratchpad show' with criteria, we check if it matches a * currently visible scratchpad window and hide it. */ + Con *active = con_get_workspace(focused); + Con *current = con_get_workspace(con); if (con && (floating = con_inside_floating(con)) && floating->scratchpad_state != SCRATCHPAD_NONE && - con_get_workspace(con) != __i3_scratch) { - DLOG("Window is a scratchpad window, hiding it.\n"); - scratchpad_move(con); - return; + current != __i3_scratch) { + /* If scratchpad window is on the active workspace, then we should hide + * it, otherwise we should move it to the active workspace. */ + if (current == active) { + DLOG("Window is a scratchpad window, hiding it.\n"); + scratchpad_move(con); + return; + } } - Con *ws = con_get_workspace(focused); if (con == NULL) { /* Use the container on __i3_scratch which is highest in the focus * stack. When moving windows to __i3_scratch, they get inserted at the @@ -112,7 +165,7 @@ void scratchpad_show(Con *con) { } /* 1: Move the window from __i3_scratch to the current workspace. */ - con_move_to_workspace(con, ws, true, false); + con_move_to_workspace(con, active, true, false); /* 2: Adjust the size if this window was not adjusted yet. */ if (con->scratchpad_state == SCRATCHPAD_FRESH) { @@ -120,12 +173,98 @@ void scratchpad_show(Con *con) { Con *output = con_get_output(con); con->rect.width = output->rect.width * 0.5; con->rect.height = output->rect.height * 0.75; + floating_check_size(con); con->rect.x = output->rect.x + ((output->rect.width / 2.0) - (con->rect.width / 2.0)); con->rect.y = output->rect.y + ((output->rect.height / 2.0) - (con->rect.height / 2.0)); - con->scratchpad_state = SCRATCHPAD_CHANGED; + } + + /* Activate active workspace if window is from another workspace to ensure + * proper focus. */ + if (current != active) { + workspace_show(active); } con_focus(con_descend_focused(con)); } + +/* + * Greatest common divisor, implemented only for the least common multiple + * below. + * + */ +static int _gcd(const int m, const int n) { + if (n == 0) + return m; + return _gcd(n, (m % n)); +} + +/* + * Least common multiple. We use it to determine the (ideally not too large) + * resolution for the __i3 pseudo-output on which the scratchpad is on (see + * below). We could just multiply the resolutions, but for some pathetic cases + * (many outputs), using the LCM will achieve better results. + * + * Man, when you were learning about these two algorithms for the first time, + * did you think you’d ever need them in a real-world software project of + * yours? I certainly didn’t until now. :-D + * + */ +static int _lcm(const int m, const int n) { + const int o = _gcd(m, n); + return ((m * n) / o); +} + +/* + * When starting i3 initially (and after each change to the connected outputs), + * this function fixes the resolution of the __i3 pseudo-output. When that + * resolution is not set to a function which shares a common divisor with every + * active output’s resolution, floating point calculation errors will lead to + * the scratchpad window moving when shown repeatedly. + * + */ +void scratchpad_fix_resolution(void) { + Con *__i3_scratch = workspace_get("__i3_scratch", NULL); + Con *__i3_output = con_get_output(__i3_scratch); + DLOG("Current resolution: (%d, %d) %d x %d\n", + __i3_output->rect.x, __i3_output->rect.y, + __i3_output->rect.width, __i3_output->rect.height); + Con *output; + int new_width = -1, + new_height = -1; + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + if (output == __i3_output) + continue; + DLOG("output %s's resolution: (%d, %d) %d x %d\n", + output->name, output->rect.x, output->rect.y, + output->rect.width, output->rect.height); + if (new_width == -1) { + new_width = output->rect.width; + new_height = output->rect.height; + } else { + new_width = _lcm(new_width, output->rect.width); + new_height = _lcm(new_height, output->rect.height); + } + } + + Rect old_rect = __i3_output->rect; + + DLOG("new width = %d, new height = %d\n", + new_width, new_height); + __i3_output->rect.width = new_width; + __i3_output->rect.height = new_height; + + Rect new_rect = __i3_output->rect; + + if (memcmp(&old_rect, &new_rect, sizeof(Rect)) == 0) { + DLOG("Scratchpad size unchanged.\n"); + return; + } + + DLOG("Fixing coordinates of scratchpad windows\n"); + Con *con; + TAILQ_FOREACH(con, &(__i3_scratch->floating_head), floating_windows) { + floating_fix_coordinates(con, &old_rect, &new_rect); + } +}