aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sway/tree/container.h18
-rw-r--r--include/sway/tree/root.h2
-rw-r--r--sway/commands/focus.c26
-rw-r--r--sway/commands/fullscreen.c34
-rw-r--r--sway/commands/move.c44
-rw-r--r--sway/commands/split.c6
-rw-r--r--sway/commands/swap.c27
-rw-r--r--sway/commands/workspace.c5
-rw-r--r--sway/desktop/output.c30
-rw-r--r--sway/desktop/render.c10
-rw-r--r--sway/desktop/transaction.c2
-rw-r--r--sway/desktop/xdg_shell.c2
-rw-r--r--sway/desktop/xdg_shell_v6.c2
-rw-r--r--sway/desktop/xwayland.c2
-rw-r--r--sway/input/cursor.c12
-rw-r--r--sway/input/seat.c12
-rw-r--r--sway/ipc-json.c4
-rw-r--r--sway/sway.5.scd5
-rw-r--r--sway/tree/arrange.c16
-rw-r--r--sway/tree/container.c174
-rw-r--r--sway/tree/root.c5
-rw-r--r--sway/tree/view.c26
22 files changed, 332 insertions, 132 deletions
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 9a432cb2..f0d0b3ce 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -25,6 +25,12 @@ enum sway_container_border {
25 B_CSD, 25 B_CSD,
26}; 26};
27 27
28enum sway_fullscreen_mode {
29 FULLSCREEN_NONE,
30 FULLSCREEN_WORKSPACE,
31 FULLSCREEN_GLOBAL,
32};
33
28struct sway_root; 34struct sway_root;
29struct sway_output; 35struct sway_output;
30struct sway_workspace; 36struct sway_workspace;
@@ -38,7 +44,7 @@ struct sway_container_state {
38 double x, y; 44 double x, y;
39 double width, height; 45 double width, height;
40 46
41 bool is_fullscreen; 47 enum sway_fullscreen_mode fullscreen_mode;
42 48
43 struct sway_workspace *workspace; 49 struct sway_workspace *workspace;
44 struct sway_container *parent; 50 struct sway_container *parent;
@@ -85,7 +91,7 @@ struct sway_container {
85 double content_x, content_y; 91 double content_x, content_y;
86 int content_width, content_height; 92 int content_width, content_height;
87 93
88 bool is_fullscreen; 94 enum sway_fullscreen_mode fullscreen_mode;
89 95
90 enum sway_container_border border; 96 enum sway_container_border border;
91 97
@@ -249,7 +255,13 @@ bool container_has_urgent_child(struct sway_container *container);
249 */ 255 */
250void container_end_mouse_operation(struct sway_container *container); 256void container_end_mouse_operation(struct sway_container *container);
251 257
252void container_set_fullscreen(struct sway_container *container, bool enable); 258void container_set_fullscreen(struct sway_container *con,
259 enum sway_fullscreen_mode mode);
260
261/**
262 * Convenience function.
263 */
264void container_fullscreen_disable(struct sway_container *con);
253 265
254/** 266/**
255 * Return true if the container is floating, or a child of a floating split 267 * Return true if the container is floating, or a child of a floating split
diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h
index 8f4deaa7..9ff45eb5 100644
--- a/include/sway/tree/root.h
+++ b/include/sway/tree/root.h
@@ -35,6 +35,8 @@ struct sway_root {
35 // For when there's no connected outputs 35 // For when there's no connected outputs
36 struct sway_output *noop_output; 36 struct sway_output *noop_output;
37 37
38 struct sway_container *fullscreen_global;
39
38 struct { 40 struct {
39 struct wl_signal new_node; 41 struct wl_signal new_node;
40 } events; 42 } events;
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index 0622f2e8..87fe6cf3 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -89,19 +89,23 @@ static struct sway_node *get_node_in_output_direction(
89 89
90static struct sway_node *node_get_in_direction(struct sway_container *container, 90static struct sway_node *node_get_in_direction(struct sway_container *container,
91 struct sway_seat *seat, enum wlr_direction dir) { 91 struct sway_seat *seat, enum wlr_direction dir) {
92 if (container->is_fullscreen) {
93 // Fullscreen container with a direction - go straight to outputs
94 struct sway_output *output = container->workspace->output;
95 struct sway_output *new_output = output_get_in_direction(output, dir);
96 if (!new_output) {
97 return NULL;
98 }
99 return get_node_in_output_direction(new_output, dir);
100 }
101
102 struct sway_container *wrap_candidate = NULL; 92 struct sway_container *wrap_candidate = NULL;
103 struct sway_container *current = container; 93 struct sway_container *current = container;
104 while (current) { 94 while (current) {
95 if (current->fullscreen_mode == FULLSCREEN_WORKSPACE) {
96 // Fullscreen container with a direction - go straight to outputs
97 struct sway_output *output = current->workspace->output;
98 struct sway_output *new_output =
99 output_get_in_direction(output, dir);
100 if (!new_output) {
101 return NULL;
102 }
103 return get_node_in_output_direction(new_output, dir);
104 }
105 if (current->fullscreen_mode == FULLSCREEN_GLOBAL) {
106 return NULL;
107 }
108
105 bool can_move = false; 109 bool can_move = false;
106 int desired; 110 int desired;
107 int idx = container_sibling_index(current); 111 int idx = container_sibling_index(current);
@@ -227,7 +231,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
227static struct cmd_results *focus_parent(void) { 231static struct cmd_results *focus_parent(void) {
228 struct sway_seat *seat = config->handler_context.seat; 232 struct sway_seat *seat = config->handler_context.seat;
229 struct sway_container *con = config->handler_context.container; 233 struct sway_container *con = config->handler_context.container;
230 if (!con || con->is_fullscreen) { 234 if (!con || con->fullscreen_mode) {
231 return cmd_results_new(CMD_SUCCESS, NULL); 235 return cmd_results_new(CMD_SUCCESS, NULL);
232 } 236 }
233 struct sway_node *parent = node_get_parent(&con->node); 237 struct sway_node *parent = node_get_parent(&con->node);
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c
index 920b9bd0..52248ce4 100644
--- a/sway/commands/fullscreen.c
+++ b/sway/commands/fullscreen.c
@@ -1,3 +1,4 @@
1#include <strings.h>
1#include "log.h" 2#include "log.h"
2#include "sway/commands.h" 3#include "sway/commands.h"
3#include "sway/config.h" 4#include "sway/config.h"
@@ -7,9 +8,10 @@
7#include "sway/tree/workspace.h" 8#include "sway/tree/workspace.h"
8#include "util.h" 9#include "util.h"
9 10
11// fullscreen [enable|disable|toggle] [global]
10struct cmd_results *cmd_fullscreen(int argc, char **argv) { 12struct cmd_results *cmd_fullscreen(int argc, char **argv) {
11 struct cmd_results *error = NULL; 13 struct cmd_results *error = NULL;
12 if ((error = checkarg(argc, "fullscreen", EXPECTED_AT_MOST, 1))) { 14 if ((error = checkarg(argc, "fullscreen", EXPECTED_AT_MOST, 2))) {
13 return error; 15 return error;
14 } 16 }
15 if (!root->outputs->length) { 17 if (!root->outputs->length) {
@@ -23,20 +25,38 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
23 return cmd_results_new(CMD_FAILURE, 25 return cmd_results_new(CMD_FAILURE,
24 "Can't fullscreen an empty workspace"); 26 "Can't fullscreen an empty workspace");
25 } 27 }
26 if (node->type == N_WORKSPACE) { 28
29 bool is_fullscreen = container &&
30 container->fullscreen_mode != FULLSCREEN_NONE;
31 bool global = false;
32 bool enable = !is_fullscreen;
33
34 if (argc >= 1) {
35 if (strcasecmp(argv[0], "global") == 0) {
36 global = true;
37 } else {
38 enable = parse_boolean(argv[0], is_fullscreen);
39 }
40 }
41
42 if (argc >= 2) {
43 global = strcasecmp(argv[1], "global") == 0;
44 }
45
46 if (enable && node->type == N_WORKSPACE) {
27 // Wrap the workspace's children in a container so we can fullscreen it 47 // Wrap the workspace's children in a container so we can fullscreen it
28 container = workspace_wrap_children(workspace); 48 container = workspace_wrap_children(workspace);
29 workspace->layout = L_HORIZ; 49 workspace->layout = L_HORIZ;
30 seat_set_focus_container(config->handler_context.seat, container); 50 seat_set_focus_container(config->handler_context.seat, container);
31 } 51 }
32 bool enable = !container->is_fullscreen;
33 52
34 if (argc) { 53 enum sway_fullscreen_mode mode = FULLSCREEN_NONE;
35 enable = parse_boolean(argv[0], container->is_fullscreen); 54 if (enable) {
55 mode = global ? FULLSCREEN_GLOBAL : FULLSCREEN_WORKSPACE;
36 } 56 }
37 57
38 container_set_fullscreen(container, enable); 58 container_set_fullscreen(container, mode);
39 arrange_workspace(workspace); 59 arrange_root();
40 60
41 return cmd_results_new(CMD_SUCCESS, NULL); 61 return cmd_results_new(CMD_SUCCESS, NULL);
42} 62}
diff --git a/sway/commands/move.c b/sway/commands/move.c
index acb5f44f..aa06b168 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -191,7 +191,7 @@ static void container_move_to_workspace(struct sway_container *container,
191 workspace_add_floating(workspace, container); 191 workspace_add_floating(workspace, container);
192 container_handle_fullscreen_reparent(container); 192 container_handle_fullscreen_reparent(container);
193 // If changing output, center it within the workspace 193 // If changing output, center it within the workspace
194 if (old_output != workspace->output && !container->is_fullscreen) { 194 if (old_output != workspace->output && !container->fullscreen_mode) {
195 container_floating_move_to_center(container); 195 container_floating_move_to_center(container);
196 } 196 }
197 } else { 197 } else {
@@ -276,7 +276,7 @@ static void workspace_rejigger(struct sway_workspace *ws,
276static bool container_move_in_direction(struct sway_container *container, 276static bool container_move_in_direction(struct sway_container *container,
277 enum wlr_direction move_dir) { 277 enum wlr_direction move_dir) {
278 // If moving a fullscreen view, only consider outputs 278 // If moving a fullscreen view, only consider outputs
279 if (container->is_fullscreen) { 279 if (container->fullscreen_mode == FULLSCREEN_WORKSPACE) {
280 struct sway_output *new_output = 280 struct sway_output *new_output =
281 output_get_in_direction(container->workspace->output, move_dir); 281 output_get_in_direction(container->workspace->output, move_dir);
282 if (!new_output) { 282 if (!new_output) {
@@ -286,6 +286,9 @@ static bool container_move_in_direction(struct sway_container *container,
286 container_move_to_workspace(container, ws); 286 container_move_to_workspace(container, ws);
287 return true; 287 return true;
288 } 288 }
289 if (container->fullscreen_mode == FULLSCREEN_GLOBAL) {
290 return false;
291 }
289 292
290 // If container is in a split container by itself, move out of the split 293 // If container is in a split container by itself, move out of the split
291 if (container->parent) { 294 if (container->parent) {
@@ -309,13 +312,19 @@ static bool container_move_in_direction(struct sway_container *container,
309 int index = list_find(siblings, current); 312 int index = list_find(siblings, current);
310 int desired = index + offs; 313 int desired = index + offs;
311 314
315 // Don't allow containers to move out of their
316 // fullscreen or floating parent
317 if (current->fullscreen_mode || container_is_floating(current)) {
318 return false;
319 }
320
312 if (is_parallel(layout, move_dir)) { 321 if (is_parallel(layout, move_dir)) {
313 if (desired == -1 || desired == siblings->length) { 322 if (desired == -1 || desired == siblings->length) {
314 if (current->parent == container->parent) { 323 if (current->parent == container->parent) {
315 current = current->parent; 324 current = current->parent;
316 continue; 325 continue;
317 } else { 326 } else {
318 // Special case 327 // Reparenting
319 if (current->parent) { 328 if (current->parent) {
320 container_insert_child(current->parent, container, 329 container_insert_child(current->parent, container,
321 index + (offs < 0 ? 0 : 1)); 330 index + (offs < 0 ? 0 : 1));
@@ -334,13 +343,6 @@ static bool container_move_in_direction(struct sway_container *container,
334 } 343 }
335 344
336 current = current->parent; 345 current = current->parent;
337
338 // Don't allow containers to move out of their
339 // fullscreen or floating parent
340 if (current &&
341 (current->is_fullscreen || container_is_floating(current))) {
342 return false;
343 }
344 } 346 }
345 347
346 // Maybe rejigger the workspace 348 // Maybe rejigger the workspace
@@ -563,10 +565,14 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) {
563 } 565 }
564 566
565 // arrange windows 567 // arrange windows
566 if (old_ws && !old_ws->node.destroying) { 568 if (root->fullscreen_global) {
567 arrange_workspace(old_ws); 569 arrange_root();
570 } else {
571 if (old_ws && !old_ws->node.destroying) {
572 arrange_workspace(old_ws);
573 }
574 arrange_node(node_get_parent(destination));
568 } 575 }
569 arrange_node(node_get_parent(destination));
570 576
571 return cmd_results_new(CMD_SUCCESS, NULL); 577 return cmd_results_new(CMD_SUCCESS, NULL);
572} 578}
@@ -658,7 +664,7 @@ static struct cmd_results *cmd_move_in_direction(
658 "Cannot move a hidden scratchpad container"); 664 "Cannot move a hidden scratchpad container");
659 } 665 }
660 if (container_is_floating(container)) { 666 if (container_is_floating(container)) {
661 if (container->is_fullscreen) { 667 if (container->fullscreen_mode) {
662 return cmd_results_new(CMD_FAILURE, 668 return cmd_results_new(CMD_FAILURE,
663 "Cannot move fullscreen floating container"); 669 "Cannot move fullscreen floating container");
664 } 670 }
@@ -690,9 +696,13 @@ static struct cmd_results *cmd_move_in_direction(
690 696
691 struct sway_workspace *new_ws = container->workspace; 697 struct sway_workspace *new_ws = container->workspace;
692 698
693 arrange_workspace(old_ws); 699 if (root->fullscreen_global) {
694 if (new_ws != old_ws) { 700 arrange_root();
695 arrange_workspace(new_ws); 701 } else {
702 arrange_workspace(old_ws);
703 if (new_ws != old_ws) {
704 arrange_workspace(new_ws);
705 }
696 } 706 }
697 707
698 if (container->view) { 708 if (container->view) {
diff --git a/sway/commands/split.c b/sway/commands/split.c
index 06b7df5e..b7ab7b79 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -26,7 +26,11 @@ static struct cmd_results *do_split(int layout) {
26 container_flatten(con->parent->parent); 26 container_flatten(con->parent->parent);
27 } 27 }
28 28
29 arrange_workspace(ws); 29 if (root->fullscreen_global) {
30 arrange_root();
31 } else {
32 arrange_workspace(ws);
33 }
30 34
31 return cmd_results_new(CMD_SUCCESS, NULL); 35 return cmd_results_new(CMD_SUCCESS, NULL);
32} 36}
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index 0d8d1116..0e2c2d10 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -77,6 +77,11 @@ static void swap_focus(struct sway_container *con1,
77 } else { 77 } else {
78 seat_set_focus_container(seat, focus); 78 seat_set_focus_container(seat, focus);
79 } 79 }
80
81 if (root->fullscreen_global) {
82 seat_set_focus(seat,
83 seat_get_focus_inactive(seat, &root->fullscreen_global->node));
84 }
80} 85}
81 86
82static void container_swap(struct sway_container *con1, 87static void container_swap(struct sway_container *con1,
@@ -98,13 +103,13 @@ static void container_swap(struct sway_container *con1,
98 sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu", 103 sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu",
99 con1->node.id, con2->node.id); 104 con1->node.id, con2->node.id);
100 105
101 bool fs1 = con1->is_fullscreen; 106 enum sway_fullscreen_mode fs1 = con1->fullscreen_mode;
102 bool fs2 = con2->is_fullscreen; 107 enum sway_fullscreen_mode fs2 = con2->fullscreen_mode;
103 if (fs1) { 108 if (fs1) {
104 container_set_fullscreen(con1, false); 109 container_fullscreen_disable(con1);
105 } 110 }
106 if (fs2) { 111 if (fs2) {
107 container_set_fullscreen(con2, false); 112 container_fullscreen_disable(con2);
108 } 113 }
109 114
110 struct sway_seat *seat = config->handler_context.seat; 115 struct sway_seat *seat = config->handler_context.seat;
@@ -136,10 +141,10 @@ static void container_swap(struct sway_container *con1,
136 } 141 }
137 142
138 if (fs1) { 143 if (fs1) {
139 container_set_fullscreen(con2, true); 144 container_set_fullscreen(con2, fs1);
140 } 145 }
141 if (fs2) { 146 if (fs2) {
142 container_set_fullscreen(con1, true); 147 container_set_fullscreen(con1, fs2);
143 } 148 }
144} 149}
145 150
@@ -220,9 +225,13 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
220 225
221 container_swap(current, other); 226 container_swap(current, other);
222 227
223 arrange_node(node_get_parent(&current->node)); 228 if (root->fullscreen_global) {
224 if (node_get_parent(&other->node) != node_get_parent(&current->node)) { 229 arrange_root();
225 arrange_node(node_get_parent(&other->node)); 230 } else {
231 arrange_node(node_get_parent(&current->node));
232 if (node_get_parent(&other->node) != node_get_parent(&current->node)) {
233 arrange_node(node_get_parent(&other->node));
234 }
226 } 235 }
227 236
228 return cmd_results_new(CMD_SUCCESS, NULL); 237 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index 6bfca506..c5d6435a 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -168,6 +168,11 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
168 "Can't run this command while there's no outputs connected."); 168 "Can't run this command while there's no outputs connected.");
169 } 169 }
170 170
171 if (root->fullscreen_global) {
172 return cmd_results_new(CMD_FAILURE, "workspace",
173 "Can't switch workspaces while fullscreen global");
174 }
175
171 bool no_auto_back_and_forth = false; 176 bool no_auto_back_and_forth = false;
172 while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { 177 while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) {
173 no_auto_back_and_forth = true; 178 no_auto_back_and_forth = true;
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index f18a118f..b5f164cb 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -251,17 +251,27 @@ static void output_for_each_surface(struct sway_output *output,
251 }; 251 };
252 252
253 struct sway_workspace *workspace = output_get_active_workspace(output); 253 struct sway_workspace *workspace = output_get_active_workspace(output);
254 if (workspace->current.fullscreen) { 254 struct sway_container *fullscreen_con = root->fullscreen_global;
255 for_each_surface_container_iterator( 255 if (fullscreen_con && fullscreen_con->scratchpad &&
256 workspace->current.fullscreen, &data); 256 !fullscreen_con->workspace) {
257 container_for_each_child(workspace->current.fullscreen, 257 fullscreen_con = NULL;
258 }
259 if (!fullscreen_con) {
260 fullscreen_con = workspace->current.fullscreen;
261 }
262 if (fullscreen_con) {
263 for_each_surface_container_iterator(fullscreen_con, &data);
264 container_for_each_child(fullscreen_con,
258 for_each_surface_container_iterator, &data); 265 for_each_surface_container_iterator, &data);
259 for (int i = 0; i < workspace->current.floating->length; ++i) { 266
260 struct sway_container *floater = 267 // TODO: Show transient containers for fullscreen global
261 workspace->current.floating->items[i]; 268 if (fullscreen_con == workspace->current.fullscreen) {
262 if (container_is_transient_for(floater, 269 for (int i = 0; i < workspace->current.floating->length; ++i) {
263 workspace->current.fullscreen)) { 270 struct sway_container *floater =
264 for_each_surface_container_iterator(floater, &data); 271 workspace->current.floating->items[i];
272 if (container_is_transient_for(floater, fullscreen_con)) {
273 for_each_surface_container_iterator(floater, &data);
274 }
265 } 275 }
266 } 276 }
267#if HAVE_XWAYLAND 277#if HAVE_XWAYLAND
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index a38c6a07..fa27500e 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -985,7 +985,15 @@ void output_render(struct sway_output *output, struct timespec *when,
985 goto render_overlay; 985 goto render_overlay;
986 } 986 }
987 987
988 struct sway_container *fullscreen_con = workspace->current.fullscreen; 988 struct sway_container *fullscreen_con = root->fullscreen_global;
989 if (fullscreen_con && fullscreen_con->scratchpad &&
990 !fullscreen_con->workspace) {
991 fullscreen_con = NULL;
992 }
993 if (!fullscreen_con) {
994 fullscreen_con = workspace->current.fullscreen;
995 }
996
989 if (fullscreen_con) { 997 if (fullscreen_con) {
990 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; 998 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
991 999
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index 55cf1c5e..b06ac9cc 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -134,7 +134,7 @@ static void copy_container_state(struct sway_container *container,
134 state->y = container->y; 134 state->y = container->y;
135 state->width = container->width; 135 state->width = container->width;
136 state->height = container->height; 136 state->height = container->height;
137 state->is_fullscreen = container->is_fullscreen; 137 state->fullscreen_mode = container->fullscreen_mode;
138 state->parent = container->parent; 138 state->parent = container->parent;
139 state->workspace = container->workspace; 139 state->workspace = container->workspace;
140 state->border = container->border; 140 state->border = container->border;
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index e2c614b3..007b0a94 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -349,7 +349,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
349 349
350 container_set_fullscreen(view->container, e->fullscreen); 350 container_set_fullscreen(view->container, e->fullscreen);
351 351
352 arrange_workspace(view->container->workspace); 352 arrange_root();
353 transaction_commit_dirty(); 353 transaction_commit_dirty();
354} 354}
355 355
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index f11c00b1..386e350e 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -342,7 +342,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
342 342
343 container_set_fullscreen(view->container, e->fullscreen); 343 container_set_fullscreen(view->container, e->fullscreen);
344 344
345 arrange_workspace(view->container->workspace); 345 arrange_root();
346 transaction_commit_dirty(); 346 transaction_commit_dirty();
347} 347}
348 348
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 4ea661f8..d9e1b0a9 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -453,7 +453,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
453 } 453 }
454 container_set_fullscreen(view->container, xsurface->fullscreen); 454 container_set_fullscreen(view->container, xsurface->fullscreen);
455 455
456 arrange_workspace(view->container->workspace); 456 arrange_root();
457 transaction_commit_dirty(); 457 transaction_commit_dirty();
458} 458}
459 459
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index bf9bf2da..4b2d99e6 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -90,6 +90,16 @@ struct sway_node *node_at_coords(
90 double ox = lx, oy = ly; 90 double ox = lx, oy = ly;
91 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); 91 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
92 92
93 if (root->fullscreen_global) {
94 // Try fullscreen container
95 struct sway_container *con = tiling_container_at(
96 &root->fullscreen_global->node, lx, ly, surface, sx, sy);
97 if (con) {
98 return &con->node;
99 }
100 return NULL;
101 }
102
93 // find the focused workspace on the output for this seat 103 // find the focused workspace on the output for this seat
94 struct sway_workspace *ws = output_get_active_workspace(output); 104 struct sway_workspace *ws = output_get_active_workspace(output);
95 105
@@ -659,7 +669,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
659 // Handle moving a tiling container 669 // Handle moving a tiling container
660 if (config->tiling_drag && (mod_pressed || on_titlebar) && 670 if (config->tiling_drag && (mod_pressed || on_titlebar) &&
661 state == WLR_BUTTON_PRESSED && !is_floating_or_child && 671 state == WLR_BUTTON_PRESSED && !is_floating_or_child &&
662 cont && !cont->is_fullscreen) { 672 cont && cont->fullscreen_mode == FULLSCREEN_NONE) {
663 struct sway_container *focus = seat_get_focused_container(seat); 673 struct sway_container *focus = seat_get_focused_container(seat);
664 bool focused = focus == cont || container_has_ancestor(focus, cont); 674 bool focused = focus == cont || container_has_ancestor(focus, cont);
665 if (on_titlebar && !focused) { 675 if (on_titlebar && !focused) {
diff --git a/sway/input/seat.c b/sway/input/seat.c
index fd5eda2d..f216810b 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -753,6 +753,18 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
753 return; 753 return;
754 } 754 }
755 } 755 }
756 // Deny setting focus to a workspace node when using fullscreen global
757 if (root->fullscreen_global && !container && new_workspace) {
758 return;
759 }
760 // Deny setting focus to a view which is hidden by a fullscreen global
761 if (root->fullscreen_global && container != root->fullscreen_global &&
762 !container_has_ancestor(container, root->fullscreen_global)) {
763 // Unless it's a transient container
764 if (!container_is_transient_for(container, root->fullscreen_global)) {
765 return;
766 }
767 }
756 768
757 struct sway_output *new_output = new_workspace->output; 769 struct sway_output *new_output = new_workspace->output;
758 770
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 778bf773..87d2c1ec 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -425,7 +425,9 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
425 view_is_urgent(c->view) : container_has_urgent_child(c); 425 view_is_urgent(c->view) : container_has_urgent_child(c);
426 json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); 426 json_object_object_add(object, "urgent", json_object_new_boolean(urgent));
427 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); 427 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky));
428 json_object_object_add(object, "fullscreen_mode", json_object_new_int(c->is_fullscreen)); 428
429 json_object_object_add(object, "fullscreen_mode",
430 json_object_new_int(c->fullscreen_mode));
429 431
430 struct sway_node *parent = node_get_parent(&c->node); 432 struct sway_node *parent = node_get_parent(&c->node);
431 struct wlr_box parent_box = {0, 0, 0, 0}; 433 struct wlr_box parent_box = {0, 0, 0, 0};
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 06bc0dbf..c2539804 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -134,9 +134,10 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
134*focus* mode\_toggle 134*focus* mode\_toggle
135 Moves focus between the floating and tiled layers. 135 Moves focus between the floating and tiled layers.
136 136
137*fullscreen* [enable|disable|toggle] 137*fullscreen* [enable|disable|toggle] [global]
138 Makes focused view fullscreen, non-fullscreen, or the opposite of what it 138 Makes focused view fullscreen, non-fullscreen, or the opposite of what it
139 is now. If no argument is given, it does the same as _toggle_. 139 is now. If no argument is given, it does the same as _toggle_. If _global_
140 is specified, the view will be fullscreen across all outputs.
140 141
141*gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current 142*gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current
142set|plus|minus <amount> 143set|plus|minus <amount>
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index f78d95a4..da372aa4 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -261,9 +261,19 @@ void arrange_root(void) {
261 root->y = layout_box->y; 261 root->y = layout_box->y;
262 root->width = layout_box->width; 262 root->width = layout_box->width;
263 root->height = layout_box->height; 263 root->height = layout_box->height;
264 for (int i = 0; i < root->outputs->length; ++i) { 264
265 struct sway_output *output = root->outputs->items[i]; 265 if (root->fullscreen_global) {
266 arrange_output(output); 266 struct sway_container *fs = root->fullscreen_global;
267 fs->x = root->x;
268 fs->y = root->y;
269 fs->width = root->width;
270 fs->height = root->height;
271 arrange_container(fs);
272 } else {
273 for (int i = 0; i < root->outputs->length; ++i) {
274 struct sway_output *output = root->outputs->items[i];
275 arrange_output(output);
276 }
267 } 277 }
268} 278}
269 279
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 0c0684c0..d8ad3bc0 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -92,7 +92,7 @@ void container_begin_destroy(struct sway_container *con) {
92 } 92 }
93 // The workspace must have the fullscreen pointer cleared so that the 93 // The workspace must have the fullscreen pointer cleared so that the
94 // seat code can find an appropriate new focus. 94 // seat code can find an appropriate new focus.
95 if (con->is_fullscreen && con->workspace) { 95 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE && con->workspace) {
96 con->workspace->fullscreen = NULL; 96 con->workspace->fullscreen = NULL;
97 } 97 }
98 wl_signal_emit(&con->node.events.destroy, &con->node); 98 wl_signal_emit(&con->node.events.destroy, &con->node);
@@ -106,6 +106,10 @@ void container_begin_destroy(struct sway_container *con) {
106 root_scratchpad_remove_container(con); 106 root_scratchpad_remove_container(con);
107 } 107 }
108 108
109 if (con->fullscreen_mode == FULLSCREEN_GLOBAL) {
110 container_fullscreen_disable(con);
111 }
112
109 if (con->parent || con->workspace) { 113 if (con->parent || con->workspace) {
110 container_detach(con); 114 container_detach(con);
111 } 115 }
@@ -840,15 +844,15 @@ void container_floating_move_to_center(struct sway_container *con) {
840 return; 844 return;
841 } 845 }
842 struct sway_workspace *ws = con->workspace; 846 struct sway_workspace *ws = con->workspace;
843 bool full = con->is_fullscreen; 847 enum sway_fullscreen_mode fullscreen_mode = con->fullscreen_mode;
844 if (full) { 848 if (fullscreen_mode) {
845 container_set_fullscreen(con, false); 849 container_fullscreen_disable(con);
846 } 850 }
847 double new_lx = ws->x + (ws->width - con->width) / 2; 851 double new_lx = ws->x + (ws->width - con->width) / 2;
848 double new_ly = ws->y + (ws->height - con->height) / 2; 852 double new_ly = ws->y + (ws->height - con->height) / 2;
849 container_floating_translate(con, new_lx - con->x, new_ly - con->y); 853 container_floating_translate(con, new_lx - con->x, new_ly - con->y);
850 if (full) { 854 if (fullscreen_mode) {
851 container_set_fullscreen(con, true); 855 container_set_fullscreen(con, fullscreen_mode);
852 } 856 }
853} 857}
854 858
@@ -877,59 +881,125 @@ static void set_fullscreen_iterator(struct sway_container *con, void *data) {
877 } 881 }
878} 882}
879 883
880void container_set_fullscreen(struct sway_container *container, bool enable) { 884static void container_fullscreen_workspace(struct sway_container *con) {
881 if (container->is_fullscreen == enable) { 885 if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE,
886 "Expected a non-fullscreen container")) {
882 return; 887 return;
883 } 888 }
889 bool enable = true;
890 set_fullscreen_iterator(con, &enable);
891 container_for_each_child(con, set_fullscreen_iterator, &enable);
884 892
885 struct sway_workspace *workspace = container->workspace; 893 con->workspace->fullscreen = con;
886 if (enable && workspace->fullscreen) { 894 con->saved_x = con->x;
887 container_set_fullscreen(workspace->fullscreen, false); 895 con->saved_y = con->y;
896 con->saved_width = con->width;
897 con->saved_height = con->height;
898
899 struct sway_seat *seat;
900 struct sway_workspace *focus_ws;
901 wl_list_for_each(seat, &server.input->seats, link) {
902 focus_ws = seat_get_focused_workspace(seat);
903 if (focus_ws == con->workspace) {
904 seat_set_focus_container(seat, con);
905 }
888 } 906 }
889 907
890 set_fullscreen_iterator(container, &enable); 908 con->fullscreen_mode = FULLSCREEN_WORKSPACE;
891 container_for_each_child(container, set_fullscreen_iterator, &enable); 909 container_end_mouse_operation(con);
910 ipc_event_window(con, "fullscreen_mode");
911}
912
913static void container_fullscreen_global(struct sway_container *con) {
914 if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE,
915 "Expected a non-fullscreen container")) {
916 return;
917 }
918 bool enable = true;
919 set_fullscreen_iterator(con, &enable);
920 container_for_each_child(con, set_fullscreen_iterator, &enable);
892 921
893 container->is_fullscreen = enable; 922 root->fullscreen_global = con;
923 con->saved_x = con->x;
924 con->saved_y = con->y;
925 con->saved_width = con->width;
926 con->saved_height = con->height;
894 927
895 if (enable) { 928 struct sway_seat *seat;
896 workspace->fullscreen = container; 929 wl_list_for_each(seat, &server.input->seats, link) {
897 container->saved_x = container->x; 930 struct sway_container *focus = seat_get_focused_container(seat);
898 container->saved_y = container->y; 931 if (focus && focus != con) {
899 container->saved_width = container->width; 932 seat_set_focus_container(seat, con);
900 container->saved_height = container->height;
901
902 struct sway_seat *seat;
903 struct sway_workspace *focus_ws;
904 wl_list_for_each(seat, &server.input->seats, link) {
905 focus_ws = seat_get_focused_workspace(seat);
906 if (focus_ws) {
907 if (focus_ws == workspace) {
908 seat_set_focus_container(seat, container);
909 }
910 }
911 } 933 }
912 } else { 934 }
913 workspace->fullscreen = NULL; 935
914 if (container_is_floating(container)) { 936 con->fullscreen_mode = FULLSCREEN_GLOBAL;
915 container->x = container->saved_x; 937 container_end_mouse_operation(con);
916 container->y = container->saved_y; 938 ipc_event_window(con, "fullscreen_mode");
917 container->width = container->saved_width; 939}
918 container->height = container->saved_height; 940
919 struct sway_output *output = 941void container_fullscreen_disable(struct sway_container *con) {
920 container_floating_find_output(container); 942 if (!sway_assert(con->fullscreen_mode != FULLSCREEN_NONE,
921 if (workspace->output != output) { 943 "Expected a fullscreen container")) {
922 container_floating_move_to_center(container); 944 return;
945 }
946 bool enable = false;
947 set_fullscreen_iterator(con, &enable);
948 container_for_each_child(con, set_fullscreen_iterator, &enable);
949
950 if (container_is_floating(con)) {
951 con->x = con->saved_x;
952 con->y = con->saved_y;
953 }
954 con->width = con->saved_width;
955 con->height = con->saved_height;
956
957 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) {
958 con->workspace->fullscreen = NULL;
959 if (container_is_floating(con)) {
960 struct sway_output *output = container_floating_find_output(con);
961 if (con->workspace->output != output) {
962 container_floating_move_to_center(con);
923 } 963 }
924 } else {
925 container->width = container->saved_width;
926 container->height = container->saved_height;
927 } 964 }
965 } else {
966 root->fullscreen_global = NULL;
928 } 967 }
929 968
930 container_end_mouse_operation(container); 969 con->fullscreen_mode = FULLSCREEN_NONE;
970 container_end_mouse_operation(con);
971 ipc_event_window(con, "fullscreen_mode");
972}
973
974void container_set_fullscreen(struct sway_container *con,
975 enum sway_fullscreen_mode mode) {
976 if (con->fullscreen_mode == mode) {
977 return;
978 }
931 979
932 ipc_event_window(container, "fullscreen_mode"); 980 switch (mode) {
981 case FULLSCREEN_NONE:
982 container_fullscreen_disable(con);
983 break;
984 case FULLSCREEN_WORKSPACE:
985 if (root->fullscreen_global) {
986 container_fullscreen_disable(root->fullscreen_global);
987 }
988 if (con->workspace->fullscreen) {
989 container_fullscreen_disable(con->workspace->fullscreen);
990 }
991 container_fullscreen_workspace(con);
992 break;
993 case FULLSCREEN_GLOBAL:
994 if (root->fullscreen_global) {
995 container_fullscreen_disable(root->fullscreen_global);
996 }
997 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) {
998 container_fullscreen_disable(con);
999 }
1000 container_fullscreen_global(con);
1001 break;
1002 }
933} 1003}
934 1004
935bool container_is_floating_or_child(struct sway_container *container) { 1005bool container_is_floating_or_child(struct sway_container *container) {
@@ -941,7 +1011,7 @@ bool container_is_floating_or_child(struct sway_container *container) {
941 1011
942bool container_is_fullscreen_or_child(struct sway_container *container) { 1012bool container_is_fullscreen_or_child(struct sway_container *container) {
943 do { 1013 do {
944 if (container->is_fullscreen) { 1014 if (container->fullscreen_mode) {
945 return true; 1015 return true;
946 } 1016 }
947 container = container->parent; 1017 container = container->parent;
@@ -1111,12 +1181,12 @@ list_t *container_get_current_siblings(struct sway_container *container) {
1111} 1181}
1112 1182
1113void container_handle_fullscreen_reparent(struct sway_container *con) { 1183void container_handle_fullscreen_reparent(struct sway_container *con) {
1114 if (!con->is_fullscreen || !con->workspace || 1184 if (con->fullscreen_mode != FULLSCREEN_WORKSPACE || !con->workspace ||
1115 con->workspace->fullscreen == con) { 1185 con->workspace->fullscreen == con) {
1116 return; 1186 return;
1117 } 1187 }
1118 if (con->workspace->fullscreen) { 1188 if (con->workspace->fullscreen) {
1119 container_set_fullscreen(con->workspace->fullscreen, false); 1189 container_fullscreen_disable(con->workspace->fullscreen);
1120 } 1190 }
1121 con->workspace->fullscreen = con; 1191 con->workspace->fullscreen = con;
1122 1192
@@ -1171,9 +1241,12 @@ void container_add_child(struct sway_container *parent,
1171} 1241}
1172 1242
1173void container_detach(struct sway_container *child) { 1243void container_detach(struct sway_container *child) {
1174 if (child->is_fullscreen) { 1244 if (child->fullscreen_mode == FULLSCREEN_WORKSPACE) {
1175 child->workspace->fullscreen = NULL; 1245 child->workspace->fullscreen = NULL;
1176 } 1246 }
1247 if (child->fullscreen_mode == FULLSCREEN_GLOBAL) {
1248 root->fullscreen_global = NULL;
1249 }
1177 1250
1178 struct sway_container *old_parent = child->parent; 1251 struct sway_container *old_parent = child->parent;
1179 struct sway_workspace *old_workspace = child->workspace; 1252 struct sway_workspace *old_workspace = child->workspace;
@@ -1387,4 +1460,3 @@ void container_raise_floating(struct sway_container *con) {
1387 node_set_dirty(&floater->workspace->node); 1460 node_set_dirty(&floater->workspace->node);
1388 } 1461 }
1389} 1462}
1390
diff --git a/sway/tree/root.c b/sway/tree/root.c
index 99cf91a7..476e47a3 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -101,7 +101,10 @@ void root_scratchpad_show(struct sway_container *con) {
101 // If the current con or any of its parents are in fullscreen mode, we 101 // If the current con or any of its parents are in fullscreen mode, we
102 // first need to disable it before showing the scratchpad con. 102 // first need to disable it before showing the scratchpad con.
103 if (new_ws->fullscreen) { 103 if (new_ws->fullscreen) {
104 container_set_fullscreen(new_ws->fullscreen, false); 104 container_fullscreen_disable(new_ws->fullscreen);
105 }
106 if (root->fullscreen_global) {
107 container_fullscreen_disable(root->fullscreen_global);
105 } 108 }
106 109
107 // Show the container 110 // Show the container
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 8795e04f..561c6ef1 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -203,12 +203,18 @@ void view_autoconfigure(struct sway_view *view) {
203 } 203 }
204 struct sway_output *output = con->workspace->output; 204 struct sway_output *output = con->workspace->output;
205 205
206 if (con->is_fullscreen) { 206 if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) {
207 con->content_x = output->lx; 207 con->content_x = output->lx;
208 con->content_y = output->ly; 208 con->content_y = output->ly;
209 con->content_width = output->width; 209 con->content_width = output->width;
210 con->content_height = output->height; 210 con->content_height = output->height;
211 return; 211 return;
212 } else if (con->fullscreen_mode == FULLSCREEN_GLOBAL) {
213 con->content_x = root->x;
214 con->content_y = root->y;
215 con->content_width = root->width;
216 con->content_height = root->height;
217 return;
212 } 218 }
213 219
214 struct sway_workspace *ws = view->container->workspace; 220 struct sway_workspace *ws = view->container->workspace;
@@ -648,7 +654,10 @@ void view_unmap(struct sway_view *view) {
648 workspace_consider_destroy(ws); 654 workspace_consider_destroy(ws);
649 } 655 }
650 656
651 if (ws && !ws->node.destroying) { 657 if (root->fullscreen_global) {
658 // Container may have been a child of the root fullscreen container
659 arrange_root();
660 } else if (ws && !ws->node.destroying) {
652 arrange_workspace(ws); 661 arrange_workspace(ws);
653 workspace_detect_urgent(ws); 662 workspace_detect_urgent(ws);
654 } 663 }
@@ -1008,14 +1017,11 @@ bool view_is_visible(struct sway_view *view) {
1008 con = con->parent; 1017 con = con->parent;
1009 } 1018 }
1010 // Check view isn't hidden by another fullscreen view 1019 // Check view isn't hidden by another fullscreen view
1011 if (workspace->fullscreen && 1020 struct sway_container *fs = root->fullscreen_global ?
1012 !container_is_fullscreen_or_child(view->container)) { 1021 root->fullscreen_global : workspace->fullscreen;
1013 // However, if we're transient for the fullscreen view and we allow 1022 if (fs && !container_is_fullscreen_or_child(view->container) &&
1014 // "popups" during fullscreen then it might be visible 1023 !container_is_transient_for(view->container, fs)) {
1015 if (!container_is_transient_for(view->container, 1024 return false;
1016 workspace->fullscreen)) {
1017 return false;
1018 }
1019 } 1025 }
1020 return true; 1026 return true;
1021} 1027}