i3 - improved tiling WM


Extend the fullscreen command

Patch status: needinfo

Patch by Mats

Long description:

Rather than just toggling the fullscreen modes, allow to set them
directly with:

    fullscreen enable|toggle [global]
    fullscreen disable

For compatibility, retain the previous command and its toggling behavior:

    fullscreen [global]

fixes #1120

To apply this patch, use:
curl http://cr.i3wm.org/patch/663/raw.patch | git am

b/docs/userguide

33
@@ -91,7 +91,7 @@ To display a window in fullscreen mode or to go out of fullscreen mode again,
34
 press +$mod+f+.
35
 
36
 There is also a global fullscreen mode in i3 in which the client will span all
37
-available outputs (the command is +fullscreen global+).
38
+available outputs (the command is +fullscreen toggle global+).
39
 
40
 === Opening other applications
41
 
42
@@ -367,7 +367,7 @@ bindcode [--release] [Modifiers+]keycode command
43
 *Examples*:
44
 --------------------------------
45
 # Fullscreen
46
-bindsym $mod+f fullscreen
47
+bindsym $mod+f fullscreen toggle
48
 
49
 # Restart
50
 bindsym $mod+Shift+r restart
51
@@ -1502,9 +1502,13 @@ Use +layout toggle split+, +layout stacking+, +layout tabbed+, +layout splitv+
52
 or +layout splith+ to change the current container layout to splith/splitv,
53
 stacking, tabbed layout, splitv or splith, respectively.
54
 
55
-To make the current window (!) fullscreen, use +fullscreen+, to make
56
-it floating (or tiling again) use +floating enable+ respectively +floating disable+
57
-(or +floating toggle+):
58
+To make the current window (!) fullscreen, use +fullscreen enable+ (or
59
++fullscreen enable global+ for the global mode), to leave either fullscreen
60
+mode use +fullscreen disable+, and to toggle between these two states use
61
++fullscreen toggle+ (or +fullscreen toggle global+).
62
+
63
+Likewise, to make the current window floating (or tiling again) use +floating
64
+enable+ respectively +floating disable+ (or +floating toggle+):
65
 
66
 *Syntax*:
67
 --------------
68
@@ -1525,7 +1529,7 @@ bindsym $mod+x layout toggle
69
 bindsym $mod+x layout toggle all
70
 
71
 # Toggle fullscreen
72
-bindsym $mod+f fullscreen
73
+bindsym $mod+f fullscreen toggle
74
 
75
 # Toggle floating/tiling
76
 bindsym $mod+t floating toggle

b/i3.config

81
@@ -75,7 +75,7 @@ bindsym Mod1+h split h
82
 bindsym Mod1+v split v
83
 
84
 # enter fullscreen mode for the focused container
85
-bindsym Mod1+f fullscreen
86
+bindsym Mod1+f fullscreen toggle
87
 
88
 # change container layout (stacked, tabbed, toggle split)
89
 bindsym Mod1+s layout stacking

b/i3.config.keycodes

94
@@ -69,7 +69,7 @@ bindcode $mod+43 split h
95
 bindcode $mod+55 split v
96
 
97
 # enter fullscreen mode for the focused container
98
-bindcode $mod+41 fullscreen
99
+bindcode $mod+41 fullscreen toggle
100
 
101
 # change container layout (stacked, tabbed, toggle split)
102
 bindcode $mod+39 layout stacking

b/include/commands.h

107
@@ -187,10 +187,10 @@ void cmd_focus_level(I3_CMD, char *level);
108
 void cmd_focus(I3_CMD);
109
 
110
 /**
111
- * Implementation of 'fullscreen [global]'.
112
+ * Implementation of 'fullscreen [enable|disable|toggle] [global]'.
113
  *
114
  */
115
-void cmd_fullscreen(I3_CMD, char *fullscreen_mode);
116
+void cmd_fullscreen(I3_CMD, char *action, char *fullscreen_mode);
117
 
118
 /**
119
  * Implementation of 'move <direction> [<pixels> [px]]'.

b/include/con.h

124
@@ -173,6 +173,18 @@ void con_fix_percent(Con *con);
125
 void con_toggle_fullscreen(Con *con, int fullscreen_mode);
126
 
127
 /**
128
+ * Enables fullscreen mode for the given container, if necessary.
129
+ *
130
+ */
131
+void con_enable_fullscreen(Con *con, fullscreen_mode_t fullscreen_mode);
132
+
133
+/**
134
+ * Disables fullscreen mode for the given container, if necessary.
135
+ *
136
+ */
137
+void con_disable_fullscreen(Con *con);
138
+
139
+/**
140
  * Moves the given container to the currently focused container on the given
141
  * workspace.
142
  *

b/man/i3.man

147
@@ -230,7 +230,7 @@ bindsym Mod1+h split h
148
 bindsym Mod1+v split v
149
 
150
 # enter fullscreen mode for the focused container
151
-bindsym Mod1+f fullscreen
152
+bindsym Mod1+f fullscreen toggle
153
 
154
 # change container layout (stacked, tabbed, default)
155
 bindsym Mod1+s layout stacking

b/parser-specs/commands.spec

160
@@ -156,12 +156,28 @@ state KILL:
161
   end
162
       -> call cmd_kill($kill_mode)
163
 
164
+# fullscreen enable|toggle [global]
165
+# fullscreen disable
166
 # fullscreen [global]
167
 state FULLSCREEN:
168
-  fullscreen_mode = 'global'
169
-      -> call cmd_fullscreen($fullscreen_mode)
170
+  action = 'disable'
171
+      -> call cmd_fullscreen($action, "output")
172
+  action = 'enable', 'toggle'
173
+      -> FULLSCREEN_MODE
174
+  action = ''
175
+      -> FULLSCREEN_COMPAT
176
+
177
+state FULLSCREEN_MODE:
178
+  mode = 'global'
179
+      -> call cmd_fullscreen($action, $mode)
180
   end
181
-      -> call cmd_fullscreen($fullscreen_mode)
182
+      -> call cmd_fullscreen($action, "output")
183
+
184
+state FULLSCREEN_COMPAT:
185
+  mode = 'global'
186
+      -> call cmd_fullscreen("toggle", $mode)
187
+  end
188
+      -> call cmd_fullscreen("toggle", "output")
189
 
190
 # split v|h|vertical|horizontal
191
 state SPLIT:

b/src/commands.c

196
@@ -1574,20 +1574,27 @@ void cmd_focus(I3_CMD) {
197
 }
198
 
199
 /*
200
- * Implementation of 'fullscreen [global]'.
201
+ * Implementation of 'fullscreen (enabled|toggle [global])|disable'
202
  *
203
  */
204
-void cmd_fullscreen(I3_CMD, char *fullscreen_mode) {
205
-    if (fullscreen_mode == NULL)
206
-        fullscreen_mode = "output";
207
-    DLOG("toggling fullscreen, mode = %s\n", fullscreen_mode);
208
+void cmd_fullscreen(I3_CMD, char *action, char *fullscreen_mode) {
209
+    if (strcmp(action, "") == 0)
210
+        action = "toggle";
211
+    fullscreen_mode_t mode = strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT;
212
+    DLOG("%s fullscreen, mode = %s\n", action, fullscreen_mode);
213
     owindow *current;
214
 
215
     HANDLE_EMPTY_MATCH;
216
 
217
     TAILQ_FOREACH(current, &owindows, owindows) {
218
         DLOG("matching: %p / %s\n", current->con, current->con->name);
219
-        con_toggle_fullscreen(current->con, (strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT));
220
+        if (strcmp(action, "toggle") == 0) {
221
+            con_toggle_fullscreen(current->con, mode);
222
+        } else if (strcmp(action, "enable") == 0) {
223
+            con_enable_fullscreen(current->con, mode);
224
+        } else if (strcmp(action, "disable") == 0) {
225
+            con_disable_fullscreen(current->con);
226
+        }
227
     }
228
 
229
     cmd_output->needs_tree_render = true;

b/src/con.c

234
@@ -565,37 +565,27 @@ void con_fix_percent(Con *con) {
235
  *
236
  */
237
 void con_toggle_fullscreen(Con *con, int fullscreen_mode) {
238
-    Con *workspace, *fullscreen;
239
-
240
     if (con->type == CT_WORKSPACE) {
241
         DLOG("You cannot make a workspace fullscreen.\n");
242
         return;
243
     }
244
 
245
     DLOG("toggling fullscreen for %p / %s\n", con, con->name);
246
-    if (con->fullscreen_mode == CF_NONE) {
247
-        /* 1: check if there already is a fullscreen con */
248
-        if (fullscreen_mode == CF_GLOBAL)
249
-            fullscreen = con_get_fullscreen_con(croot, CF_GLOBAL);
250
-        else {
251
-            workspace = con_get_workspace(con);
252
-            fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT);
253
-        }
254
-        if (fullscreen != NULL) {
255
-            /* Disable fullscreen for the currently fullscreened
256
-             * container and enable it for the one the user wants
257
-             * to have in fullscreen mode. */
258
-            LOG("Disabling fullscreen for (%p/%s) upon user request\n",
259
-                fullscreen, fullscreen->name);
260
-            fullscreen->fullscreen_mode = CF_NONE;
261
-        }
262
 
263
-        /* 2: enable fullscreen */
264
-        con->fullscreen_mode = fullscreen_mode;
265
-    } else {
266
-        /* 1: disable fullscreen */
267
-        con->fullscreen_mode = CF_NONE;
268
-    }
269
+    if (con->fullscreen_mode == CF_NONE)
270
+        con_enable_fullscreen(con, fullscreen_mode);
271
+    else
272
+        con_disable_fullscreen(con);
273
+}
274
+
275
+/*
276
+ * Sets the specified fullscreen mode for the given container, sends the
277
+ * “fullscreen_mode” event and changes the XCB fullscreen property of the
278
+ * container’s window, if any.
279
+ *
280
+ */
281
+static void con_set_fullscreen_mode(Con *con, fullscreen_mode_t fullscreen_mode) {
282
+    con->fullscreen_mode = fullscreen_mode;
283
 
284
     DLOG("mode now: %d\n", con->fullscreen_mode);
285
 
286
@@ -619,6 +609,88 @@ void con_toggle_fullscreen(Con *con, int fullscreen_mode) {
287
 }
288
 
289
 /*
290
+ * Enables fullscreen mode for the given container, if necessary.
291
+ *
292
+ * If the container’s mode is already CF_OUTPUT or CF_GLOBAL, the container is
293
+ * kept fullscreen but its mode is set to CF_GLOBAL and CF_OUTPUT,
294
+ * respectively.
295
+ *
296
+ * Other fullscreen containers will be disabled first, if they hide the new
297
+ * one.
298
+ *
299
+ */
300
+void con_enable_fullscreen(Con *con, fullscreen_mode_t fullscreen_mode) {
301
+    if (con->type == CT_WORKSPACE) {
302
+        DLOG("You cannot make a workspace fullscreen.\n");
303
+        return;
304
+    }
305
+
306
+    assert(fullscreen_mode == CF_GLOBAL || fullscreen_mode == CF_OUTPUT);
307
+
308
+    if (fullscreen_mode == CF_GLOBAL)
309
+        DLOG("enabling global fullscreen for %p / %s\n", con, con->name);
310
+    else
311
+        DLOG("enabling fullscreen for %p / %s\n", con, con->name);
312
+
313
+    if (con->fullscreen_mode == fullscreen_mode) {
314
+        DLOG("fullscreen already enabled for %p / %s\n", con, con->name);
315
+        return;
316
+    }
317
+
318
+    /* For both fullscreen modes, disable global fullscreen, if any. */
319
+    Con *fullscreen;
320
+    if (fullscreen_mode == CF_GLOBAL || fullscreen_mode == CF_OUTPUT) {
321
+        fullscreen = con_get_fullscreen_con(croot, CF_GLOBAL);
322
+        if (fullscreen != NULL)
323
+            con_disable_fullscreen(fullscreen);
324
+    }
325
+    /* For output fullscreen, disable other output fullscreen, if any. */
326
+    Con *workspace = con_get_workspace(con);
327
+    if (fullscreen_mode == CF_OUTPUT) {
328
+        fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT);
329
+        if (fullscreen != NULL)
330
+            con_disable_fullscreen(fullscreen);
331
+    }
332
+
333
+    /* Set focus to new fullscreen container. If on another workspace restore
334
+     * focus afterwards. */
335
+    Con *old_focused = focused;
336
+    con_focus(con);
337
+    if (workspace != con_get_workspace(old_focused))
338
+        con_focus(old_focused);
339
+
340
+    con_set_fullscreen_mode(con, fullscreen_mode);
341
+}
342
+
343
+/*
344
+ * Disables fullscreen mode for the given container regardless of the mode, if
345
+ * necessary.
346
+ *
347
+ */
348
+void con_disable_fullscreen(Con *con) {
349
+    if (con->type == CT_WORKSPACE) {
350
+        DLOG("You cannot make a workspace fullscreen.\n");
351
+        return;
352
+    }
353
+
354
+    DLOG("disabling fullscreen for %p / %s\n", con, con->name);
355
+
356
+    if (con->fullscreen_mode == CF_NONE) {
357
+        DLOG("fullscreen already disabled for %p / %s\n", con, con->name);
358
+        return;
359
+    }
360
+
361
+    /* Restore focus to output-fullscreen container on workspace, when leaving
362
+     * global-fullscreen. */
363
+    Con *workspace = con_get_workspace(con);
364
+    Con *fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT);
365
+    if (fullscreen != NULL)
366
+        con_focus(con_descend_focused(fullscreen));
367
+
368
+    con_set_fullscreen_mode(con, CF_NONE);
369
+}
370
+
371
+/*
372
  * Moves the given container to the currently focused container on the given
373
  * workspace.
374
  *

b/testcases/i3-test.config

379
@@ -20,7 +20,7 @@ bindsym Mod1+h split h
380
 bindsym Mod1+v split v
381
 
382
 # Fullscreen (Mod1+f)
383
-bindsym Mod1+f fullscreen
384
+bindsym Mod1+f fullscreen toggle
385
 
386
 # Stacking (Mod1+s)
387
 bindsym Mod1+s layout stacking