X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fscratchpad.c;h=7b30909554040360233317dd7f70bdeedb9ff28b;hb=da2b47c1e5d145c5029421129264dfaa986d94f5;hp=b00d7f616e88b24f63047ec8735178cc4807035e;hpb=5edacf49435023ab5a876d3425693ea530872918;p=i3%2Fi3 diff --git a/src/scratchpad.c b/src/scratchpad.c index b00d7f61..7b309095 100644 --- a/src/scratchpad.c +++ b/src/scratchpad.c @@ -1,3 +1,5 @@ +#undef I3__FILE__ +#define I3__FILE__ "scratchpad.c" /* * vim:ts=4:sw=4:expandtab * @@ -37,11 +39,17 @@ 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. */ @@ -58,7 +66,8 @@ void scratchpad_move(Con *con) { /* 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); + if (con_get_workspace(focus_next) == con_get_workspace(focused)) + con_focus(focus_next); } /* @@ -75,6 +84,16 @@ 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 the * currently focused window is a scratchpad window and should be hidden * again. */ @@ -88,16 +107,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 +136,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) { @@ -127,5 +151,75 @@ void scratchpad_show(Con *con) { 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); + } + } + DLOG("new width = %d, new height = %d\n", + new_width, new_height); + __i3_output->rect.width = new_width; + __i3_output->rect.height = new_height; +}