i3
scratchpad.c
Go to the documentation of this file.
1 #undef I3__FILE__
2 #define I3__FILE__ "scratchpad.c"
3 /*
4  * vim:ts=4:sw=4:expandtab
5  *
6  * i3 - an improved dynamic tiling window manager
7  * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
8  *
9  * scratchpad.c: Moving windows to the scratchpad and making them visible again.
10  *
11  */
12 #include "all.h"
13 
14 /*
15  * Moves the specified window to the __i3_scratch workspace, making it floating
16  * and setting the appropriate scratchpad_state.
17  *
18  * Gets called upon the command 'move scratchpad'.
19  *
20  */
21 void scratchpad_move(Con *con) {
22  if (con->type == CT_WORKSPACE) {
23  LOG("'move scratchpad' used on a workspace \"%s\". Calling it "
24  "recursively on all windows on this workspace.\n",
25  con->name);
26  Con *current;
27  current = TAILQ_FIRST(&(con->focus_head));
28  while (current) {
29  Con *next = TAILQ_NEXT(current, focused);
30  scratchpad_move(current);
31  current = next;
32  }
33  return;
34  }
35  DLOG("should move con %p to __i3_scratch\n", con);
36 
37  Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
38  if (con_get_workspace(con) == __i3_scratch) {
39  DLOG("This window is already on __i3_scratch.\n");
40  return;
41  }
42 
43  /* If the current con is in fullscreen mode, we need to disable that,
44  * as a scratchpad window should never be in fullscreen mode */
45  if (focused && focused->type != CT_WORKSPACE && focused->fullscreen_mode != CF_NONE) {
47  }
48 
49  /* 1: Ensure the window or any parent is floating. From now on, we deal
50  * with the CT_FLOATING_CON. We use automatic == false because the user
51  * made the choice that this window should be a scratchpad (and floating).
52  */
53  Con *maybe_floating_con = con_inside_floating(con);
54  if (maybe_floating_con == NULL) {
55  floating_enable(con, false);
56  con = con->parent;
57  } else {
58  con = maybe_floating_con;
59  }
60 
61  /* 2: Send the window to the __i3_scratch workspace, mainting its
62  * coordinates and not warping the pointer. */
63  con_move_to_workspace(con, __i3_scratch, true, true);
64 
65  /* 3: If this is the first time this window is used as a scratchpad, we set
66  * the scratchpad_state to SCRATCHPAD_FRESH. The window will then be
67  * adjusted in size according to what the user specifies. */
68  if (con->scratchpad_state == SCRATCHPAD_NONE) {
69  DLOG("This window was never used as a scratchpad before.\n");
70  if (con == maybe_floating_con) {
71  DLOG("It was in floating mode before, set scratchpad state to changed.\n");
72  con->scratchpad_state = SCRATCHPAD_CHANGED;
73  } else {
74  DLOG("It was in tiling mode before, set scratchpad state to fresh.\n");
75  con->scratchpad_state = SCRATCHPAD_FRESH;
76  }
77  }
78 }
79 
80 /*
81  * Either shows the top-most scratchpad window (con == NULL) or shows the
82  * specified con (if it is scratchpad window).
83  *
84  * When called with con == NULL and the currently focused window is a
85  * scratchpad window, this serves as a shortcut to hide it again (so the user
86  * can press the same key to quickly look something up).
87  *
88  */
89 void scratchpad_show(Con *con) {
90  DLOG("should show scratchpad window %p\n", con);
91  Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
92  Con *floating;
93 
94  /* If this was 'scratchpad show' without criteria, we check if the
95  * currently focused window is a scratchpad window and should be hidden
96  * again. */
97  if (!con &&
98  (floating = con_inside_floating(focused)) &&
99  floating->scratchpad_state != SCRATCHPAD_NONE) {
100  DLOG("Focused window is a scratchpad window, hiding it.\n");
102  return;
103  }
104 
105  /* If the current con or any of its parents are in fullscreen mode, we
106  * first need to disable it before showing the scratchpad con. */
107  Con *fs = focused;
108  while (fs && fs->fullscreen_mode == CF_NONE)
109  fs = fs->parent;
110 
111  if (fs && fs->type != CT_WORKSPACE) {
113  }
114 
115  /* If this was 'scratchpad show' without criteria, we check if there is a
116  * unfocused scratchpad on the current workspace and focus it */
117  Con *walk_con;
118  Con *focused_ws = con_get_workspace(focused);
119  TAILQ_FOREACH (walk_con, &(focused_ws->floating_head), floating_windows) {
120  if (!con && (floating = con_inside_floating(walk_con)) &&
121  floating->scratchpad_state != SCRATCHPAD_NONE &&
122  floating != con_inside_floating(focused)) {
123  DLOG("Found an unfocused scratchpad window on this workspace\n");
124  DLOG("Focusing it: %p\n", walk_con);
125  /* use con_descend_tiling_focused to get the last focused
126  * window inside this scratch container in order to
127  * keep the focus the same within this container */
129  return;
130  }
131  }
132 
133  /* If this was 'scratchpad show' without criteria, we check if there is a
134  * visible scratchpad window on another workspace. In this case we move it
135  * to the current workspace. */
136  focused_ws = con_get_workspace(focused);
137  TAILQ_FOREACH (walk_con, &all_cons, all_cons) {
138  Con *walk_ws = con_get_workspace(walk_con);
139  if (!con && walk_ws &&
140  !con_is_internal(walk_ws) && focused_ws != walk_ws &&
141  (floating = con_inside_floating(walk_con)) &&
142  floating->scratchpad_state != SCRATCHPAD_NONE) {
143  DLOG("Found a visible scratchpad window on another workspace,\n");
144  DLOG("moving it to this workspace: con = %p\n", walk_con);
145  con_move_to_workspace(walk_con, focused_ws, true, false);
146  return;
147  }
148  }
149 
150  /* If this was 'scratchpad show' with criteria, we check if the window
151  * is actually in the scratchpad */
152  if (con && con->parent->scratchpad_state == SCRATCHPAD_NONE) {
153  DLOG("Window is not in the scratchpad, doing nothing.\n");
154  return;
155  }
156 
157  /* If this was 'scratchpad show' with criteria, we check if it matches a
158  * currently visible scratchpad window and hide it. */
159  Con *active = con_get_workspace(focused);
160  Con *current = con_get_workspace(con);
161  if (con &&
162  (floating = con_inside_floating(con)) &&
163  floating->scratchpad_state != SCRATCHPAD_NONE &&
164  current != __i3_scratch) {
165  /* If scratchpad window is on the active workspace, then we should hide
166  * it, otherwise we should move it to the active workspace. */
167  if (current == active) {
168  DLOG("Window is a scratchpad window, hiding it.\n");
169  scratchpad_move(con);
170  return;
171  }
172  }
173 
174  if (con == NULL) {
175  /* Use the container on __i3_scratch which is highest in the focus
176  * stack. When moving windows to __i3_scratch, they get inserted at the
177  * bottom of the stack. */
178  con = TAILQ_FIRST(&(__i3_scratch->floating_head));
179 
180  if (!con) {
181  LOG("You don't have any scratchpad windows yet.\n");
182  LOG("Use 'move scratchpad' to move a window to the scratchpad.\n");
183  return;
184  }
185  } else {
186  /* We used a criterion, so we need to do what follows (moving,
187  * resizing) on the floating parent. */
188  con = con_inside_floating(con);
189  }
190 
191  /* 1: Move the window from __i3_scratch to the current workspace. */
192  con_move_to_workspace(con, active, true, false);
193 
194  /* 2: Adjust the size if this window was not adjusted yet. */
195  if (con->scratchpad_state == SCRATCHPAD_FRESH) {
196  DLOG("Adjusting size of this window.\n");
197  Con *output = con_get_output(con);
198  con->rect.width = output->rect.width * 0.5;
199  con->rect.height = output->rect.height * 0.75;
200  floating_check_size(con);
201  con->rect.x = output->rect.x +
202  ((output->rect.width / 2.0) - (con->rect.width / 2.0));
203  con->rect.y = output->rect.y +
204  ((output->rect.height / 2.0) - (con->rect.height / 2.0));
205  }
206 
207  /* Activate active workspace if window is from another workspace to ensure
208  * proper focus. */
209  if (current != active) {
210  workspace_show(active);
211  }
212 
214 }
215 
216 /*
217  * Greatest common divisor, implemented only for the least common multiple
218  * below.
219  *
220  */
221 static int _gcd(const int m, const int n) {
222  if (n == 0)
223  return m;
224  return _gcd(n, (m % n));
225 }
226 
227 /*
228  * Least common multiple. We use it to determine the (ideally not too large)
229  * resolution for the __i3 pseudo-output on which the scratchpad is on (see
230  * below). We could just multiply the resolutions, but for some pathetic cases
231  * (many outputs), using the LCM will achieve better results.
232  *
233  * Man, when you were learning about these two algorithms for the first time,
234  * did you think you’d ever need them in a real-world software project of
235  * yours? I certainly didn’t until now. :-D
236  *
237  */
238 static int _lcm(const int m, const int n) {
239  const int o = _gcd(m, n);
240  return ((m * n) / o);
241 }
242 
243 /*
244  * When starting i3 initially (and after each change to the connected outputs),
245  * this function fixes the resolution of the __i3 pseudo-output. When that
246  * resolution is not set to a function which shares a common divisor with every
247  * active output’s resolution, floating point calculation errors will lead to
248  * the scratchpad window moving when shown repeatedly.
249  *
250  */
252  Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
253  Con *__i3_output = con_get_output(__i3_scratch);
254  DLOG("Current resolution: (%d, %d) %d x %d\n",
255  __i3_output->rect.x, __i3_output->rect.y,
256  __i3_output->rect.width, __i3_output->rect.height);
257  Con *output;
258  int new_width = -1,
259  new_height = -1;
260  TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
261  if (output == __i3_output)
262  continue;
263  DLOG("output %s's resolution: (%d, %d) %d x %d\n",
264  output->name, output->rect.x, output->rect.y,
265  output->rect.width, output->rect.height);
266  if (new_width == -1) {
267  new_width = output->rect.width;
268  new_height = output->rect.height;
269  } else {
270  new_width = _lcm(new_width, output->rect.width);
271  new_height = _lcm(new_height, output->rect.height);
272  }
273  }
274 
275  Rect old_rect = __i3_output->rect;
276 
277  DLOG("new width = %d, new height = %d\n",
278  new_width, new_height);
279  __i3_output->rect.width = new_width;
280  __i3_output->rect.height = new_height;
281 
282  Rect new_rect = __i3_output->rect;
283 
284  if (memcmp(&old_rect, &new_rect, sizeof(Rect)) == 0) {
285  DLOG("Scratchpad size unchanged.\n");
286  return;
287  }
288 
289  DLOG("Fixing coordinates of scratchpad windows\n");
290  Con *con;
291  TAILQ_FOREACH (con, &(__i3_scratch->floating_head), floating_windows) {
292  floating_fix_coordinates(con, &old_rect, &new_rect);
293  }
294 }
enum Con::@20 scratchpad_state
struct Con * parent
Definition: data.h:512
void scratchpad_move(Con *con)
Moves the specified window to the __i3_scratch workspace, making it floating and setting the appropri...
Definition: scratchpad.c:21
uint32_t y
Definition: data.h:124
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
Definition: con.c:317
Stores a rectangle, for example the size of a window, the child window etc.
Definition: data.h:122
#define LOG(fmt,...)
Definition: libi3.h:76
void workspace_show(Con *workspace)
Switches to the given workspace.
Definition: workspace.c:447
Con * con_descend_focused(Con *con)
Returns the focused con inside this client, descending the tree as far as possible.
Definition: con.c:983
struct Rect rect
Definition: data.h:514
struct all_cons_head all_cons
Definition: tree.c:17
void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool dont_warp)
Moves the given container to the currently focused container on the given workspace.
Definition: con.c:658
Con * con_descend_tiling_focused(Con *con)
Returns the focused con inside this client, descending the tree as far as possible.
Definition: con.c:998
#define TAILQ_FIRST(head)
Definition: queue.h:323
#define TAILQ_NEXT(elm, field)
Definition: queue.h:325
bool con_is_internal(Con *con)
Returns true if the container is internal, such as __i3_scratch.
Definition: con.c:411
enum Con::@18 type
#define DLOG(fmt,...)
Definition: libi3.h:86
static int _lcm(const int m, const int n)
Definition: scratchpad.c:238
void floating_check_size(Con *floating_con)
Called when a floating window is created or resized.
Definition: floating.c:37
Definition: data.h:473
uint32_t height
Definition: data.h:126
Con * focused
Definition: tree.c:15
char * name
Definition: data.h:520
fullscreen_mode_t fullscreen_mode
Definition: data.h:563
void con_focus(Con *con)
Sets input focus to the given container.
Definition: con.c:213
void scratchpad_show(Con *con)
Either shows the top-most scratchpad window (con == NULL) or shows the specified con (if it is scratc...
Definition: scratchpad.c:89
A 'Con' represents everything from the X11 root window down to a single X11 window.
Definition: data.h:479
uint32_t x
Definition: data.h:123
Con * workspace_get(const char *num, bool *created)
Returns a pointer to the workspace with the given number (starting at 0), creating the workspace if n...
Definition: workspace.c:44
void scratchpad_fix_resolution(void)
When starting i3 initially (and after each change to the connected outputs), this function fixes the ...
Definition: scratchpad.c:251
struct Con * croot
Definition: tree.c:14
Con * con_get_output(Con *con)
Gets the output container (first container with CT_OUTPUT in hierarchy) this node is on...
Definition: con.c:303
Con * con_inside_floating(Con *con)
Checks if the given container is either floating or inside some floating container.
Definition: con.c:430
void floating_enable(Con *con, bool automatic)
Enables floating mode for the given container by detaching it from its parent, creating a new contain...
Definition: floating.c:105
static int _gcd(const int m, const int n)
Definition: scratchpad.c:221
void floating_fix_coordinates(Con *con, Rect *old_rect, Rect *new_rect)
Fixes the coordinates of the floating window whenever the window gets reassigned to a different outpu...
Definition: floating.c:783
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:334
void con_toggle_fullscreen(Con *con, int fullscreen_mode)
Toggles fullscreen mode for the given container.
Definition: con.c:587
uint32_t width
Definition: data.h:125