]> git.sur5r.net Git - i3/i3/blob - testcases/t/185-scratchpad.t
Merge branch 'master' into next
[i3/i3] / testcases / t / 185-scratchpad.t
1 #!perl
2 # vim:ts=4:sw=4:expandtab
3 #
4 # Tests for the scratchpad functionality.
5 #
6 use i3test;
7 use List::Util qw(first);
8
9 my $i3 = i3(get_socket_path());
10 my $tmp = fresh_workspace;
11
12 ################################################################################
13 # 1: Verify that the __i3 output contains the __i3_scratch workspace and that
14 # it’s empty initially. Also, __i3 should not show up in GET_OUTPUTS so that
15 # tools like i3bar will not handle it. Similarly, __i3_scratch should not show
16 # up in GET_WORKSPACES. After all, you should not be able to switch to it.
17 ################################################################################
18
19 my $tree = $i3->get_tree->recv;
20 is($tree->{name}, 'root', 'root node is the first thing we get');
21
22 my @__i3 = grep { $_->{name} eq '__i3' } @{$tree->{nodes}};
23 is(scalar @__i3, 1, 'output __i3 found');
24
25 my $content = first { $_->{type} == 2 } @{$__i3[0]->{nodes}};
26 my @workspaces = @{$content->{nodes}};
27 my @workspace_names = map { $_->{name} } @workspaces;
28 ok('__i3_scratch' ~~ @workspace_names, '__i3_scratch workspace found');
29
30 my $get_outputs = $i3->get_outputs->recv;
31 my $get_ws = $i3->get_workspaces->recv;
32 my @output_names = map { $_->{name} } @$get_outputs;
33 my @ws_names = map { $_->{name} } @$get_ws;
34
35 ok(!('__i3' ~~ @output_names), '__i3 not in GET_OUTPUTS');
36 ok(!('__i3_scratch' ~~ @ws_names), '__i3_scratch ws not in GET_WORKSPACES');
37
38 ################################################################################
39 # 2: Verify that you cannot switch to the __i3_scratch workspace and moving
40 # windows to __i3_scratch does not work (users should be aware of the different
41 # behavior and acknowledge that by using the scratchpad commands).
42 ################################################################################
43
44 # Try focusing the workspace.
45 my $__i3_scratch = get_ws('__i3_scratch');
46 is($__i3_scratch->{focused}, 0, '__i3_scratch ws not focused');
47
48 cmd 'workspace __i3_scratch';
49
50 $__i3_scratch = get_ws('__i3_scratch');
51 is($__i3_scratch->{focused}, 0, '__i3_scratch ws still not focused');
52
53
54 # Try moving a window to it.
55 is(scalar @{$__i3_scratch->{floating_nodes}}, 0, '__i3_scratch ws empty');
56
57 my $window = open_window;
58 cmd 'move workspace __i3_scratch';
59
60 $__i3_scratch = get_ws('__i3_scratch');
61 is(scalar @{$__i3_scratch->{floating_nodes}}, 0, '__i3_scratch ws empty');
62
63
64 # Try moving the window with the 'output <direction>' command.
65 # We hardcode output left since the pseudo-output will be initialized before
66 # every other output, so it will always be the first one.
67 cmd 'move output left';
68
69 $__i3_scratch = get_ws('__i3_scratch');
70 is(scalar @{$__i3_scratch->{floating_nodes}}, 0, '__i3_scratch ws empty');
71
72
73 # Try moving the window with the 'output <name>' command.
74 cmd 'move output __i3';
75
76 $__i3_scratch = get_ws('__i3_scratch');
77 is(scalar @{$__i3_scratch->{floating_nodes}}, 0, '__i3_scratch ws empty');
78
79
80 ################################################################################
81 # 3: Verify that 'scratchpad toggle' sends a window to the __i3_scratch
82 # workspace and sets the scratchpad flag to SCRATCHPAD_FRESH. The window’s size
83 # and position will be changed (once!) on the next 'scratchpad show' and the
84 # flag will be changed to SCRATCHPAD_CHANGED.
85 ################################################################################
86
87 my ($nodes, $focus) = get_ws_content($tmp);
88 is(scalar @$nodes, 1, 'precisely one window on current ws');
89 is($nodes->[0]->{scratchpad_state}, 'none', 'scratchpad_state none');
90
91 cmd 'move scratchpad';
92
93 $__i3_scratch = get_ws('__i3_scratch');
94 my @scratch_nodes = @{$__i3_scratch->{floating_nodes}};
95 is(scalar @scratch_nodes, 1, '__i3_scratch contains our window');
96 ($nodes, $focus) = get_ws_content($tmp);
97 is(scalar @$nodes, 0, 'no window on current ws anymore');
98
99 is($scratch_nodes[0]->{scratchpad_state}, 'fresh', 'scratchpad_state fresh');
100
101 $tree = $i3->get_tree->recv;
102 my $__i3 = first { $_->{name} eq '__i3' } @{$tree->{nodes}};
103 isnt($tree->{focus}->[0], $__i3->{id}, '__i3 output not focused');
104
105 $get_outputs = $i3->get_outputs->recv;
106 $get_ws = $i3->get_workspaces->recv;
107 @output_names = map { $_->{name} } @$get_outputs;
108 @ws_names = map { $_->{name} } @$get_ws;
109
110 ok(!('__i3' ~~ @output_names), '__i3 not in GET_OUTPUTS');
111 ok(!('__i3_scratch' ~~ @ws_names), '__i3_scratch ws not in GET_WORKSPACES');
112
113 ################################################################################
114 # 4: Verify that 'scratchpad show' makes the window visible.
115 ################################################################################
116
117 # Open another window so that we can check if focus is on the scratchpad window
118 # after showing it.
119 my $second_window = open_window;
120 my $old_focus = get_focused($tmp);
121
122 cmd 'scratchpad show';
123
124 isnt(get_focused($tmp), $old_focus, 'focus changed');
125
126 $__i3_scratch = get_ws('__i3_scratch');
127 @scratch_nodes = @{$__i3_scratch->{floating_nodes}};
128 is(scalar @scratch_nodes, 0, '__i3_scratch is now empty');
129
130 my $ws = get_ws($tmp);
131 my $output = $tree->{nodes}->[1];
132 my $scratchrect = $ws->{floating_nodes}->[0]->{rect};
133 my $outputrect = $output->{rect};
134
135 is($scratchrect->{width}, $outputrect->{width} * 0.5, 'scratch width is 50%');
136 is($scratchrect->{height}, $outputrect->{height} * 0.75, 'scratch height is 75%');
137 is($scratchrect->{x},
138    ($outputrect->{width} / 2) - ($scratchrect->{width} / 2),
139    'scratch window centered horizontally');
140 is($scratchrect->{y},
141    ($outputrect->{height} / 2 ) - ($scratchrect->{height} / 2),
142    'scratch window centered vertically');
143
144 ################################################################################
145 # 5: Another 'scratchpad show' should make that window go to the scratchpad
146 # again.
147 ################################################################################
148
149 cmd 'scratchpad show';
150
151 $__i3_scratch = get_ws('__i3_scratch');
152 @scratch_nodes = @{$__i3_scratch->{floating_nodes}};
153 is(scalar @scratch_nodes, 1, '__i3_scratch contains our window');
154
155 is($scratch_nodes[0]->{scratchpad_state}, 'changed', 'scratchpad_state changed');
156
157 ################################################################################
158 # 6: Verify that repeated 'scratchpad show' cycle through the stack, that is,
159 # toggling a visible window should insert it at the bottom of the stack of the
160 # __i3_scratch workspace.
161 ################################################################################
162
163 my $third_window = open_window(name => 'scratch-match');
164 cmd 'move scratchpad';
165
166 $__i3_scratch = get_ws('__i3_scratch');
167 @scratch_nodes = @{$__i3_scratch->{floating_nodes}};
168 is(scalar @scratch_nodes, 2, '__i3_scratch contains both windows');
169
170 is($scratch_nodes[0]->{scratchpad_state}, 'changed', 'changed window first');
171 is($scratch_nodes[1]->{scratchpad_state}, 'fresh', 'fresh window is second');
172
173 my $changed_id = $scratch_nodes[0]->{nodes}->[0]->{id};
174 my $fresh_id = $scratch_nodes[1]->{nodes}->[0]->{id};
175 is($scratch_nodes[0]->{id}, $__i3_scratch->{focus}->[0], 'changed window first');
176 is($scratch_nodes[1]->{id}, $__i3_scratch->{focus}->[1], 'fresh window second');
177
178 # Repeatedly use 'scratchpad show' and check that the windows are different.
179 cmd 'scratchpad show';
180
181 is(get_focused($tmp), $changed_id, 'focus changed');
182
183 $ws = get_ws($tmp);
184 $scratchrect = $ws->{floating_nodes}->[0]->{rect};
185 is($scratchrect->{width}, $outputrect->{width} * 0.5, 'scratch width is 50%');
186 is($scratchrect->{height}, $outputrect->{height} * 0.75, 'scratch height is 75%');
187 is($scratchrect->{x},
188    ($outputrect->{width} / 2) - ($scratchrect->{width} / 2),
189    'scratch window centered horizontally');
190 is($scratchrect->{y},
191    ($outputrect->{height} / 2 ) - ($scratchrect->{height} / 2),
192    'scratch window centered vertically');
193
194 cmd 'scratchpad show';
195
196 isnt(get_focused($tmp), $changed_id, 'focus changed');
197
198 cmd 'scratchpad show';
199
200 is(get_focused($tmp), $fresh_id, 'focus changed');
201
202 cmd 'scratchpad show';
203
204 isnt(get_focused($tmp), $fresh_id, 'focus changed');
205
206 ################################################################################
207 # 7: Verify that using scratchpad show with criteria works as expected:
208 # When matching a scratchpad window which is visible, it should hide it.
209 # When matching a scratchpad window which is on __i3_scratch, it should show it.
210 # When matching a non-scratchpad window, it should be a no-op.
211 ################################################################################
212
213 # Verify that using 'scratchpad show' without any matching windows is a no-op.
214 $old_focus = get_focused($tmp);
215
216 cmd '[title="nomatch"] scratchpad show';
217
218 is(get_focused($tmp), $old_focus, 'non-matching criteria have no effect');
219
220 # Verify that we can use criteria to show a scratchpad window.
221 cmd '[title="scratch-match"] scratchpad show';
222
223 my $scratch_focus = get_focused($tmp);
224 isnt($scratch_focus, $old_focus, 'matching criteria works');
225
226 cmd '[title="scratch-match"] scratchpad show';
227
228 isnt(get_focused($tmp), $scratch_focus, 'matching criteria works');
229 is(get_focused($tmp), $old_focus, 'focus restored');
230
231 # Verify that we cannot use criteria to show a non-scratchpad window.
232 my $tmp2 = fresh_workspace;
233 my $non_scratch_window = open_window(name => 'non-scratch');
234 cmd "workspace $tmp";
235 is(get_focused($tmp), $old_focus, 'focus still ok');
236 cmd '[title="non-match"] scratchpad show';
237 is(get_focused($tmp), $old_focus, 'focus unchanged');
238
239 ################################################################################
240 # 8: Show it, move it around, hide it. Verify that the position is retained
241 # when showing it again.
242 ################################################################################
243
244 cmd '[title="scratch-match"] scratchpad show';
245
246 isnt(get_focused($tmp), $old_focus, 'scratchpad window shown');
247
248 my $oldrect = get_ws($tmp)->{floating_nodes}->[0]->{rect};
249
250 cmd 'move left';
251
252 $scratchrect = get_ws($tmp)->{floating_nodes}->[0]->{rect};
253 isnt($scratchrect->{x}, $oldrect->{x}, 'x position changed');
254 $oldrect = $scratchrect;
255
256 # hide it, then show it again
257 cmd '[title="scratch-match"] scratchpad show';
258 cmd '[title="scratch-match"] scratchpad show';
259
260 # verify the position is still the same
261 $scratchrect = get_ws($tmp)->{floating_nodes}->[0]->{rect};
262
263 is_deeply($scratchrect, $oldrect, 'position/size the same');
264
265 # hide it again for the next test
266 cmd '[title="scratch-match"] scratchpad show';
267
268 is(get_focused($tmp), $old_focus, 'scratchpad window hidden');
269
270 is(scalar @{get_ws($tmp)->{nodes}}, 1, 'precisely one window on current ws');
271
272 ################################################################################
273 # 9: restart i3 and verify that the scratchpad show still works
274 ################################################################################
275
276 $__i3_scratch = get_ws('__i3_scratch');
277 my $old_nodes = scalar @{$__i3_scratch->{nodes}};
278 my $old_floating_nodes = scalar @{$__i3_scratch->{floating_nodes}};
279
280 cmd 'restart';
281
282 does_i3_live;
283
284 $__i3_scratch = get_ws('__i3_scratch');
285 is(scalar @{$__i3_scratch->{nodes}}, $old_nodes, "number of nodes matches ($old_nodes)");
286 is(scalar @{$__i3_scratch->{floating_nodes}}, $old_floating_nodes, "number of floating nodes matches ($old_floating_nodes)");
287
288 is(scalar @{get_ws($tmp)->{nodes}}, 1, 'still precisely one window on current ws');
289 is(scalar @{get_ws($tmp)->{floating_nodes}}, 0, 'still no floating windows on current ws');
290
291 # verify that we can display the scratchpad window
292 cmd '[title="scratch-match"] scratchpad show';
293
294 $ws = get_ws($tmp);
295 is(scalar @{$ws->{nodes}}, 1, 'still precisely one window on current ws');
296 is(scalar @{$ws->{floating_nodes}}, 1, 'precisely one floating windows on current ws');
297 is($ws->{floating_nodes}->[0]->{scratchpad_state}, 'changed', 'scratchpad_state is "changed"');
298
299 ################################################################################
300 # 10: on an empty workspace, ensure the 'move scratchpad' command does nothing
301 ################################################################################
302
303 $tmp = fresh_workspace;
304
305 cmd 'move scratchpad';
306
307 does_i3_live;
308
309 ################################################################################
310 # 11: focus a workspace and move all of its children to the scratchpad area
311 ################################################################################
312
313 $tmp = fresh_workspace;
314
315 my $first = open_window;
316 my $second = open_window;
317
318 cmd 'focus parent';
319 cmd 'move scratchpad';
320
321 does_i3_live;
322
323 $ws = get_ws($tmp);
324 is(scalar @{$ws->{nodes}}, 0, 'no windows on ws');
325 is(scalar @{$ws->{floating_nodes}}, 0, 'no floating windows on ws');
326
327 # show the first window.
328 cmd 'scratchpad show';
329
330 $ws = get_ws($tmp);
331 is(scalar @{$ws->{nodes}}, 0, 'no windows on ws');
332 is(scalar @{$ws->{floating_nodes}}, 1, 'one floating windows on ws');
333
334 $old_focus = get_focused($tmp);
335
336 cmd 'scratchpad show';
337
338 # show the second window.
339 cmd 'scratchpad show';
340
341 $ws = get_ws($tmp);
342 is(scalar @{$ws->{nodes}}, 0, 'no windows on ws');
343 is(scalar @{$ws->{floating_nodes}}, 1, 'one floating windows on ws');
344
345 isnt(get_focused($tmp), $old_focus, 'focus changed');
346
347 # TODO: make i3bar display *something* when a window on the scratchpad has the urgency hint
348
349 done_testing;