aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2018-06-01 15:41:49 -0700
committerLibravatar GitHub <noreply@github.com>2018-06-01 15:41:49 -0700
commit96446fdbf748acfdbd4c60fbc0d12e45a27199fe (patch)
tree6d46cc61a1e7c74efe36565796ccbf8b47e7e4a7
parentMerge pull request #2083 from RedSoxFan/fix-1976 (diff)
parentFix changing borders on floating views (diff)
downloadsway-96446fdbf748acfdbd4c60fbc0d12e45a27199fe.tar.gz
sway-96446fdbf748acfdbd4c60fbc0d12e45a27199fe.tar.zst
sway-96446fdbf748acfdbd4c60fbc0d12e45a27199fe.zip
Merge pull request #2027 from RyanDwyer/implement-floating
Implement floating
-rw-r--r--include/sway/tree/container.h23
-rw-r--r--include/sway/tree/view.h15
-rw-r--r--include/sway/tree/workspace.h3
-rw-r--r--sway/commands.c4
-rw-r--r--sway/commands/border.c8
-rw-r--r--sway/commands/floating.c40
-rw-r--r--sway/commands/layout.c10
-rw-r--r--sway/commands/move.c6
-rw-r--r--sway/commands/split.c15
-rw-r--r--sway/commands/sticky.c40
-rw-r--r--sway/config.c11
-rw-r--r--sway/criteria.c9
-rw-r--r--sway/debug-tree.c2
-rw-r--r--sway/desktop/output.c112
-rw-r--r--sway/desktop/xdg_shell.c35
-rw-r--r--sway/desktop/xdg_shell_v6.c35
-rw-r--r--sway/desktop/xwayland.c52
-rw-r--r--sway/input/cursor.c15
-rw-r--r--sway/input/seat.c22
-rw-r--r--sway/ipc-json.c10
-rw-r--r--sway/meson.build2
-rw-r--r--sway/tree/arrange.c16
-rw-r--r--sway/tree/container.c169
-rw-r--r--sway/tree/layout.c54
-rw-r--r--sway/tree/view.c126
-rw-r--r--sway/tree/workspace.c48
26 files changed, 662 insertions, 220 deletions
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index bb6c04a6..7ed6aab1 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -76,12 +76,14 @@ struct sway_container {
76 enum sway_container_layout layout; 76 enum sway_container_layout layout;
77 enum sway_container_layout prev_layout; 77 enum sway_container_layout prev_layout;
78 78
79 bool is_sticky;
80
79 // For C_ROOT, this has no meaning 81 // For C_ROOT, this has no meaning
80 // For C_OUTPUT, this is the output position in layout coordinates 82 // For other types, this is the position in layout coordinates
81 // For other types, this is the position in output-local coordinates
82 // Includes borders 83 // Includes borders
83 double x, y; 84 double x, y;
84 double width, height; 85 double width, height;
86 double saved_x, saved_y;
85 double saved_width, saved_height; 87 double saved_width, saved_height;
86 88
87 list_t *children; 89 list_t *children;
@@ -172,6 +174,13 @@ struct sway_container *container_at(struct sway_container *container,
172 double *sx, double *sy); 174 double *sx, double *sy);
173 175
174/** 176/**
177 * Same as container_at, but only checks floating views and expects coordinates
178 * to be layout coordinates, as that's what floating views use.
179 */
180struct sway_container *floating_container_at(double lx, double ly,
181 struct wlr_surface **surface, double *sx, double *sy);
182
183/**
175 * Apply the function for each descendant of the container breadth first. 184 * Apply the function for each descendant of the container breadth first.
176 */ 185 */
177void container_for_each_descendant_bfs(struct sway_container *container, 186void container_for_each_descendant_bfs(struct sway_container *container,
@@ -227,4 +236,14 @@ void container_notify_subtree_changed(struct sway_container *container);
227 */ 236 */
228size_t container_titlebar_height(void); 237size_t container_titlebar_height(void);
229 238
239void container_set_floating(struct sway_container *container, bool enable);
240
241void container_set_geometry_from_floating_view(struct sway_container *con);
242
243/**
244 * Determine if the given container is itself floating.
245 * This will return false for any descendants of a floating container.
246 */
247bool container_is_floating(struct sway_container *container);
248
230#endif 249#endif
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index a8bf4955..3df38e2d 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -29,10 +29,11 @@ struct sway_view_impl {
29 const char *(*get_string_prop)(struct sway_view *view, 29 const char *(*get_string_prop)(struct sway_view *view,
30 enum sway_view_prop prop); 30 enum sway_view_prop prop);
31 uint32_t (*get_int_prop)(struct sway_view *view, enum sway_view_prop prop); 31 uint32_t (*get_int_prop)(struct sway_view *view, enum sway_view_prop prop);
32 void (*configure)(struct sway_view *view, double ox, double oy, int width, 32 void (*configure)(struct sway_view *view, double lx, double ly, int width,
33 int height); 33 int height);
34 void (*set_activated)(struct sway_view *view, bool activated); 34 void (*set_activated)(struct sway_view *view, bool activated);
35 void (*set_fullscreen)(struct sway_view *view, bool fullscreen); 35 void (*set_fullscreen)(struct sway_view *view, bool fullscreen);
36 bool (*wants_floating)(struct sway_view *view);
36 void (*for_each_surface)(struct sway_view *view, 37 void (*for_each_surface)(struct sway_view *view,
37 wlr_surface_iterator_func_t iterator, void *user_data); 38 wlr_surface_iterator_func_t iterator, void *user_data);
38 void (*close)(struct sway_view *view); 39 void (*close)(struct sway_view *view);
@@ -46,10 +47,17 @@ struct sway_view {
46 struct sway_container *swayc; // NULL for unmapped views 47 struct sway_container *swayc; // NULL for unmapped views
47 struct wlr_surface *surface; // NULL for unmapped views 48 struct wlr_surface *surface; // NULL for unmapped views
48 49
49 // Geometry of the view itself (excludes borders) 50 // Geometry of the view itself (excludes borders) in layout coordinates
50 double x, y; 51 double x, y;
51 int width, height; 52 int width, height;
52 53
54 double saved_x, saved_y;
55 int saved_width, saved_height;
56
57 // The size the view would want to be if it weren't tiled.
58 // Used when changing a view from tiled to floating.
59 int natural_width, natural_height;
60
53 bool is_fullscreen; 61 bool is_fullscreen;
54 62
55 char *title_format; 63 char *title_format;
@@ -131,6 +139,7 @@ struct sway_xwayland_view {
131 struct wl_listener unmap; 139 struct wl_listener unmap;
132 struct wl_listener destroy; 140 struct wl_listener destroy;
133 141
142 int pending_lx, pending_ly;
134 int pending_width, pending_height; 143 int pending_width, pending_height;
135}; 144};
136 145
@@ -236,7 +245,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface);
236 245
237void view_unmap(struct sway_view *view); 246void view_unmap(struct sway_view *view);
238 247
239void view_update_position(struct sway_view *view, double ox, double oy); 248void view_update_position(struct sway_view *view, double lx, double ly);
240 249
241void view_update_size(struct sway_view *view, int width, int height); 250void view_update_size(struct sway_view *view, int width, int height);
242 251
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h
index 35e1df3b..81321fc8 100644
--- a/include/sway/tree/workspace.h
+++ b/include/sway/tree/workspace.h
@@ -8,6 +8,7 @@ struct sway_view;
8struct sway_workspace { 8struct sway_workspace {
9 struct sway_container *swayc; 9 struct sway_container *swayc;
10 struct sway_view *fullscreen; 10 struct sway_view *fullscreen;
11 struct sway_container *floating;
11}; 12};
12 13
13extern char *prev_workspace_name; 14extern char *prev_workspace_name;
@@ -30,4 +31,6 @@ struct sway_container *workspace_prev(struct sway_container *current);
30 31
31bool workspace_is_visible(struct sway_container *ws); 32bool workspace_is_visible(struct sway_container *ws);
32 33
34bool workspace_is_empty(struct sway_container *ws);
35
33#endif 36#endif
diff --git a/sway/commands.c b/sway/commands.c
index 6f5113f8..e9762bef 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -110,7 +110,6 @@ static struct cmd_handler handlers[] = {
110 { "font", cmd_font }, 110 { "font", cmd_font },
111 { "for_window", cmd_for_window }, 111 { "for_window", cmd_for_window },
112 { "force_focus_wrapping", cmd_force_focus_wrapping }, 112 { "force_focus_wrapping", cmd_force_focus_wrapping },
113 { "fullscreen", cmd_fullscreen },
114 { "hide_edge_borders", cmd_hide_edge_borders }, 113 { "hide_edge_borders", cmd_hide_edge_borders },
115 { "include", cmd_include }, 114 { "include", cmd_include },
116 { "input", cmd_input }, 115 { "input", cmd_input },
@@ -176,7 +175,9 @@ static struct cmd_handler config_handlers[] = {
176static struct cmd_handler command_handlers[] = { 175static struct cmd_handler command_handlers[] = {
177 { "border", cmd_border }, 176 { "border", cmd_border },
178 { "exit", cmd_exit }, 177 { "exit", cmd_exit },
178 { "floating", cmd_floating },
179 { "focus", cmd_focus }, 179 { "focus", cmd_focus },
180 { "fullscreen", cmd_fullscreen },
180 { "kill", cmd_kill }, 181 { "kill", cmd_kill },
181 { "layout", cmd_layout }, 182 { "layout", cmd_layout },
182 { "mark", cmd_mark }, 183 { "mark", cmd_mark },
@@ -189,6 +190,7 @@ static struct cmd_handler command_handlers[] = {
189 { "splith", cmd_splith }, 190 { "splith", cmd_splith },
190 { "splitt", cmd_splitt }, 191 { "splitt", cmd_splitt },
191 { "splitv", cmd_splitv }, 192 { "splitv", cmd_splitv },
193 { "sticky", cmd_sticky },
192 { "swap", cmd_swap }, 194 { "swap", cmd_swap },
193 { "title_format", cmd_title_format }, 195 { "title_format", cmd_title_format },
194 { "unmark", cmd_unmark }, 196 { "unmark", cmd_unmark },
diff --git a/sway/commands/border.c b/sway/commands/border.c
index 4ba361da..0b059562 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -37,7 +37,13 @@ struct cmd_results *cmd_border(int argc, char **argv) {
37 "or 'border pixel <px>'"); 37 "or 'border pixel <px>'");
38 } 38 }
39 39
40 view_autoconfigure(view); 40 if (container_is_floating(view->swayc)) {
41 container_damage_whole(view->swayc);
42 container_set_geometry_from_floating_view(view->swayc);
43 container_damage_whole(view->swayc);
44 } else {
45 view_autoconfigure(view);
46 }
41 47
42 struct sway_seat *seat = input_manager_current_seat(input_manager); 48 struct sway_seat *seat = input_manager_current_seat(input_manager);
43 if (seat->cursor) { 49 if (seat->cursor) {
diff --git a/sway/commands/floating.c b/sway/commands/floating.c
new file mode 100644
index 00000000..46b761da
--- /dev/null
+++ b/sway/commands/floating.c
@@ -0,0 +1,40 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/input/seat.h"
5#include "sway/ipc-server.h"
6#include "sway/output.h"
7#include "sway/tree/arrange.h"
8#include "sway/tree/container.h"
9#include "sway/tree/layout.h"
10#include "sway/tree/view.h"
11#include "list.h"
12
13struct cmd_results *cmd_floating(int argc, char **argv) {
14 struct cmd_results *error = NULL;
15 if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) {
16 return error;
17 }
18 struct sway_container *container =
19 config->handler_context.current_container;
20 if (container->type != C_VIEW) {
21 // TODO: This doesn't strictly speaking have to be true
22 return cmd_results_new(CMD_INVALID, "float", "Only views can float");
23 }
24
25 bool wants_floating;
26 if (strcasecmp(argv[0], "enable") == 0) {
27 wants_floating = true;
28 } else if (strcasecmp(argv[0], "disable") == 0) {
29 wants_floating = false;
30 } else if (strcasecmp(argv[0], "toggle") == 0) {
31 wants_floating = !container_is_floating(container);
32 } else {
33 return cmd_results_new(CMD_FAILURE, "floating",
34 "Expected 'floating <enable|disable|toggle>'");
35 }
36
37 container_set_floating(container, wants_floating);
38
39 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
40}
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index 6b44b001..a009e38f 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -12,19 +12,15 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
12 } 12 }
13 struct sway_container *parent = config->handler_context.current_container; 13 struct sway_container *parent = config->handler_context.current_container;
14 14
15 // TODO: floating 15 if (container_is_floating(parent)) {
16 /* 16 return cmd_results_new(CMD_FAILURE, "layout",
17 if (parent->is_floating) { 17 "Unable to change layout of floating windows");
18 return cmd_results_new(CMD_FAILURE, "layout", "Unable to change layout of floating windows");
19 } 18 }
20 */
21 19
22 while (parent->type == C_VIEW) { 20 while (parent->type == C_VIEW) {
23 parent = parent->parent; 21 parent = parent->parent;
24 } 22 }
25 23
26 // TODO: stacks and tabs
27
28 if (strcasecmp(argv[0], "default") == 0) { 24 if (strcasecmp(argv[0], "default") == 0) {
29 parent->layout = parent->prev_layout; 25 parent->layout = parent->prev_layout;
30 if (parent->layout == L_NONE) { 26 if (parent->layout == L_NONE) {
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 890b1a8c..dc9a6f6f 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -13,16 +13,14 @@
13#include "stringop.h" 13#include "stringop.h"
14#include "list.h" 14#include "list.h"
15 15
16static const char* expected_syntax = 16static const char* expected_syntax =
17 "Expected 'move <left|right|up|down> <[px] px>' or " 17 "Expected 'move <left|right|up|down> <[px] px>' or "
18 "'move <container|window> to workspace <name>' or " 18 "'move <container|window> to workspace <name>' or "
19 "'move <container|window|workspace> to output <name|direction>' or " 19 "'move <container|window|workspace> to output <name|direction>' or "
20 "'move position mouse'"; 20 "'move position mouse'";
21 21
22static struct sway_container *output_in_direction(const char *direction, 22static struct sway_container *output_in_direction(const char *direction,
23 struct wlr_output *reference, int ref_ox, int ref_oy) { 23 struct wlr_output *reference, int ref_lx, int ref_ly) {
24 int ref_lx = ref_ox + reference->lx,
25 ref_ly = ref_oy + reference->ly;
26 struct { 24 struct {
27 char *name; 25 char *name;
28 enum wlr_direction direction; 26 enum wlr_direction direction;
diff --git a/sway/commands/split.c b/sway/commands/split.c
index 0a61ac8d..57e42a5a 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -10,6 +10,10 @@
10 10
11static struct cmd_results *do_split(int layout) { 11static struct cmd_results *do_split(int layout) {
12 struct sway_container *con = config->handler_context.current_container; 12 struct sway_container *con = config->handler_context.current_container;
13 if (container_is_floating(con)) {
14 return cmd_results_new(CMD_FAILURE, "split",
15 "Can't split a floating view");
16 }
13 struct sway_container *parent = container_split(con, layout); 17 struct sway_container *parent = container_split(con, layout);
14 container_create_notify(parent); 18 container_create_notify(parent);
15 arrange_children_of(parent); 19 arrange_children_of(parent);
@@ -23,24 +27,23 @@ struct cmd_results *cmd_split(int argc, char **argv) {
23 return error; 27 return error;
24 } 28 }
25 if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) { 29 if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) {
26 do_split(L_VERT); 30 return do_split(L_VERT);
27 } else if (strcasecmp(argv[0], "h") == 0 || 31 } else if (strcasecmp(argv[0], "h") == 0 ||
28 strcasecmp(argv[0], "horizontal") == 0) { 32 strcasecmp(argv[0], "horizontal") == 0) {
29 do_split(L_HORIZ); 33 return do_split(L_HORIZ);
30 } else if (strcasecmp(argv[0], "t") == 0 || 34 } else if (strcasecmp(argv[0], "t") == 0 ||
31 strcasecmp(argv[0], "toggle") == 0) { 35 strcasecmp(argv[0], "toggle") == 0) {
32 struct sway_container *focused = 36 struct sway_container *focused =
33 config->handler_context.current_container; 37 config->handler_context.current_container;
34 38
35 if (focused->parent->layout == L_VERT) { 39 if (focused->parent->layout == L_VERT) {
36 do_split(L_HORIZ); 40 return do_split(L_HORIZ);
37 } else { 41 } else {
38 do_split(L_VERT); 42 return do_split(L_VERT);
39 } 43 }
40 } else { 44 } else {
41 error = cmd_results_new(CMD_FAILURE, "split", 45 return cmd_results_new(CMD_FAILURE, "split",
42 "Invalid split command (expected either horizontal or vertical)."); 46 "Invalid split command (expected either horizontal or vertical).");
43 return error;
44 } 47 }
45 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 48 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
46} 49}
diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c
new file mode 100644
index 00000000..732ccb98
--- /dev/null
+++ b/sway/commands/sticky.c
@@ -0,0 +1,40 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/input/seat.h"
5#include "sway/ipc-server.h"
6#include "sway/output.h"
7#include "sway/tree/arrange.h"
8#include "sway/tree/container.h"
9#include "sway/tree/layout.h"
10#include "sway/tree/view.h"
11#include "list.h"
12
13struct cmd_results *cmd_sticky(int argc, char **argv) {
14 struct cmd_results *error = NULL;
15 if ((error = checkarg(argc, "sticky", EXPECTED_EQUAL_TO, 1))) {
16 return error;
17 }
18 struct sway_container *container =
19 config->handler_context.current_container;
20 if (!container_is_floating(container)) {
21 return cmd_results_new(CMD_FAILURE, "sticky",
22 "Can't set sticky on a tiled container");
23 }
24
25 bool wants_sticky;
26 if (strcasecmp(argv[0], "enable") == 0) {
27 wants_sticky = true;
28 } else if (strcasecmp(argv[0], "disable") == 0) {
29 wants_sticky = false;
30 } else if (strcasecmp(argv[0], "toggle") == 0) {
31 wants_sticky = !container->is_sticky;
32 } else {
33 return cmd_results_new(CMD_FAILURE, "sticky",
34 "Expected 'sticky <enable|disable|toggle>'");
35 }
36
37 container->is_sticky = wants_sticky;
38
39 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
40}
diff --git a/sway/config.c b/sway/config.c
index cf05c236..27308066 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -26,6 +26,7 @@
26#include "sway/config.h" 26#include "sway/config.h"
27#include "sway/tree/arrange.h" 27#include "sway/tree/arrange.h"
28#include "sway/tree/layout.h" 28#include "sway/tree/layout.h"
29#include "sway/tree/workspace.h"
29#include "cairo.h" 30#include "cairo.h"
30#include "pango.h" 31#include "pango.h"
31#include "readline.h" 32#include "readline.h"
@@ -751,6 +752,16 @@ void config_update_font_height(bool recalculate) {
751 container_for_each_descendant_dfs(&root_container, 752 container_for_each_descendant_dfs(&root_container,
752 find_font_height_iterator, &recalculate); 753 find_font_height_iterator, &recalculate);
753 754
755 // Also consider floating views
756 for (int i = 0; i < root_container.children->length; ++i) {
757 struct sway_container *output = root_container.children->items[i];
758 for (int j = 0; j < output->children->length; ++j) {
759 struct sway_container *ws = output->children->items[j];
760 container_for_each_descendant_dfs(ws->sway_workspace->floating,
761 find_font_height_iterator, &recalculate);
762 }
763 }
764
754 if (config->font_height != prev_max_height) { 765 if (config->font_height != prev_max_height) {
755 arrange_root(); 766 arrange_root();
756 } 767 }
diff --git a/sway/criteria.c b/sway/criteria.c
index dec5fed7..a263485a 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -121,12 +121,15 @@ static bool criteria_matches_view(struct criteria *criteria,
121 } 121 }
122 122
123 if (criteria->floating) { 123 if (criteria->floating) {
124 // TODO 124 if (!container_is_floating(view->swayc)) {
125 return false; 125 return false;
126 }
126 } 127 }
127 128
128 if (criteria->tiling) { 129 if (criteria->tiling) {
129 // TODO 130 if (container_is_floating(view->swayc)) {
131 return false;
132 }
130 } 133 }
131 134
132 if (criteria->urgent) { 135 if (criteria->urgent) {
diff --git a/sway/debug-tree.c b/sway/debug-tree.c
index ae0a1869..f3465afe 100644
--- a/sway/debug-tree.c
+++ b/sway/debug-tree.c
@@ -25,9 +25,9 @@ static const char *layout_to_str(enum sway_container_layout layout) {
25 case L_FLOATING: 25 case L_FLOATING:
26 return "L_FLOATING"; 26 return "L_FLOATING";
27 case L_NONE: 27 case L_NONE:
28 default:
29 return "L_NONE"; 28 return "L_NONE";
30 } 29 }
30 return "L_NONE";
31} 31}
32 32
33static int draw_container(cairo_t *cairo, struct sway_container *container, 33static int draw_container(cairo_t *cairo, struct sway_container *container,
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 0deb86ca..4047fa3f 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -65,6 +65,13 @@ struct root_geometry {
65 float rotation; 65 float rotation;
66}; 66};
67 67
68struct render_data {
69 struct root_geometry root_geo;
70 struct sway_output *output;
71 pixman_region32_t *damage;
72 float alpha;
73};
74
68static bool get_surface_box(struct root_geometry *geo, 75static bool get_surface_box(struct root_geometry *geo,
69 struct sway_output *output, struct wlr_surface *surface, int sx, int sy, 76 struct sway_output *output, struct wlr_surface *surface, int sx, int sy,
70 struct wlr_box *surface_box) { 77 struct wlr_box *surface_box) {
@@ -116,8 +123,9 @@ static void surface_for_each_surface(struct wlr_surface *surface,
116static void output_view_for_each_surface(struct sway_view *view, 123static void output_view_for_each_surface(struct sway_view *view,
117 struct root_geometry *geo, wlr_surface_iterator_func_t iterator, 124 struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
118 void *user_data) { 125 void *user_data) {
119 geo->x = view->x; 126 struct render_data *data = user_data;
120 geo->y = view->y; 127 geo->x = view->x - data->output->swayc->x;
128 geo->y = view->y - data->output->swayc->y;
121 geo->width = view->surface->current->width; 129 geo->width = view->surface->current->width;
122 geo->height = view->surface->current->height; 130 geo->height = view->surface->current->height;
123 geo->rotation = 0; // TODO 131 geo->rotation = 0; // TODO
@@ -160,13 +168,6 @@ static void scale_box(struct wlr_box *box, float scale) {
160 box->height *= scale; 168 box->height *= scale;
161} 169}
162 170
163struct render_data {
164 struct root_geometry root_geo;
165 struct sway_output *output;
166 pixman_region32_t *damage;
167 float alpha;
168};
169
170static void scissor_output(struct wlr_output *wlr_output, 171static void scissor_output(struct wlr_output *wlr_output,
171 pixman_box32_t *rect) { 172 pixman_box32_t *rect) {
172 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); 173 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
@@ -275,7 +276,10 @@ static void render_rect(struct wlr_output *wlr_output,
275 struct wlr_renderer *renderer = 276 struct wlr_renderer *renderer =
276 wlr_backend_get_renderer(wlr_output->backend); 277 wlr_backend_get_renderer(wlr_output->backend);
277 278
278 struct wlr_box box = *_box; 279 struct wlr_box box;
280 memcpy(&box, _box, sizeof(struct wlr_box));
281 box.x -= wlr_output->lx * wlr_output->scale;
282 box.y -= wlr_output->ly * wlr_output->scale;
279 283
280 pixman_region32_t damage; 284 pixman_region32_t damage;
281 pixman_region32_init(&damage); 285 pixman_region32_init(&damage);
@@ -449,9 +453,10 @@ static void render_titlebar(struct sway_output *output,
449 struct wlr_box texture_box; 453 struct wlr_box texture_box;
450 wlr_texture_get_size(marks_texture, 454 wlr_texture_get_size(marks_texture,
451 &texture_box.width, &texture_box.height); 455 &texture_box.width, &texture_box.height);
452 texture_box.x = 456 texture_box.x = (x - output->swayc->x + width - TITLEBAR_H_PADDING)
453 (x + width - TITLEBAR_H_PADDING) * output_scale - texture_box.width; 457 * output_scale - texture_box.width;
454 texture_box.y = (y + TITLEBAR_V_PADDING) * output_scale; 458 texture_box.y = (y - output->swayc->y + TITLEBAR_V_PADDING)
459 * output_scale;
455 460
456 float matrix[9]; 461 float matrix[9];
457 wlr_matrix_project_box(matrix, &texture_box, 462 wlr_matrix_project_box(matrix, &texture_box,
@@ -472,8 +477,10 @@ static void render_titlebar(struct sway_output *output,
472 struct wlr_box texture_box; 477 struct wlr_box texture_box;
473 wlr_texture_get_size(title_texture, 478 wlr_texture_get_size(title_texture,
474 &texture_box.width, &texture_box.height); 479 &texture_box.width, &texture_box.height);
475 texture_box.x = (x + TITLEBAR_H_PADDING) * output_scale; 480 texture_box.x = (x - output->swayc->x + TITLEBAR_H_PADDING)
476 texture_box.y = (y + TITLEBAR_V_PADDING) * output_scale; 481 * output_scale;
482 texture_box.y = (y - output->swayc->y + TITLEBAR_V_PADDING)
483 * output_scale;
477 484
478 float matrix[9]; 485 float matrix[9];
479 wlr_matrix_project_box(matrix, &texture_box, 486 wlr_matrix_project_box(matrix, &texture_box,
@@ -755,8 +762,58 @@ static void render_container(struct sway_output *output,
755 render_container_tabbed(output, damage, con, parent_focused); 762 render_container_tabbed(output, damage, con, parent_focused);
756 break; 763 break;
757 case L_FLOATING: 764 case L_FLOATING:
758 // TODO 765 sway_assert(false, "Didn't expect to see floating here");
759 break; 766 }
767}
768
769static void render_floating_container(struct sway_output *soutput,
770 pixman_region32_t *damage, struct sway_container *con) {
771 if (con->type == C_VIEW) {
772 struct sway_view *view = con->sway_view;
773 struct sway_seat *seat = input_manager_current_seat(input_manager);
774 struct sway_container *focus = seat_get_focus(seat);
775 struct border_colors *colors;
776 struct wlr_texture *title_texture;
777 struct wlr_texture *marks_texture;
778
779 if (focus == con) {
780 colors = &config->border_colors.focused;
781 title_texture = con->title_focused;
782 marks_texture = view->marks_focused;
783 } else {
784 colors = &config->border_colors.unfocused;
785 title_texture = con->title_unfocused;
786 marks_texture = view->marks_unfocused;
787 }
788
789 if (con->sway_view->border == B_NORMAL) {
790 render_titlebar(soutput, damage, con, con->x, con->y, con->width,
791 colors, title_texture, marks_texture);
792 } else {
793 render_top_border(soutput, damage, con, colors);
794 }
795 render_view(soutput, damage, con, colors);
796 } else {
797 render_container(soutput, damage, con, false);
798 }
799}
800
801static void render_floating(struct sway_output *soutput,
802 pixman_region32_t *damage) {
803 for (int i = 0; i < root_container.children->length; ++i) {
804 struct sway_container *output = root_container.children->items[i];
805 for (int j = 0; j < output->children->length; ++j) {
806 struct sway_container *workspace = output->children->items[j];
807 struct sway_workspace *ws = workspace->sway_workspace;
808 if (!workspace_is_visible(workspace)) {
809 continue;
810 }
811 for (int k = 0; k < ws->floating->children->length; ++k) {
812 struct sway_container *floater =
813 ws->floating->children->items[k];
814 render_floating_container(soutput, damage, floater);
815 }
816 }
760 } 817 }
761} 818}
762 819
@@ -794,8 +851,6 @@ static void render_output(struct sway_output *output, struct timespec *when,
794 goto renderer_end; 851 goto renderer_end;
795 } 852 }
796 853
797 //wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
798
799 struct sway_container *workspace = output_get_active_workspace(output); 854 struct sway_container *workspace = output_get_active_workspace(output);
800 855
801 if (workspace->sway_workspace->fullscreen) { 856 if (workspace->sway_workspace->fullscreen) {
@@ -834,6 +889,7 @@ static void render_output(struct sway_output *output, struct timespec *when,
834 struct sway_seat *seat = input_manager_current_seat(input_manager); 889 struct sway_seat *seat = input_manager_current_seat(input_manager);
835 struct sway_container *focus = seat_get_focus(seat); 890 struct sway_container *focus = seat_get_focus(seat);
836 render_container(output, damage, workspace, focus == workspace); 891 render_container(output, damage, workspace, focus == workspace);
892 render_floating(output, damage);
837 893
838 render_unmanaged(output, damage, 894 render_unmanaged(output, damage,
839 &root_container.sway_root->xwayland_unmanaged); 895 &root_container.sway_root->xwayland_unmanaged);
@@ -915,6 +971,7 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
915 971
916 struct sway_container *workspace = output_get_active_workspace(output); 972 struct sway_container *workspace = output_get_active_workspace(output);
917 send_frame_done_container(&data, workspace); 973 send_frame_done_container(&data, workspace);
974 send_frame_done_container(&data, workspace->sway_workspace->floating);
918 975
919 send_frame_done_unmanaged(&data, 976 send_frame_done_unmanaged(&data,
920 &root_container.sway_root->xwayland_unmanaged); 977 &root_container.sway_root->xwayland_unmanaged);
@@ -1052,21 +1109,14 @@ static void output_damage_whole_container_iterator(struct sway_container *con,
1052 1109
1053void output_damage_whole_container(struct sway_output *output, 1110void output_damage_whole_container(struct sway_output *output,
1054 struct sway_container *con) { 1111 struct sway_container *con) {
1055 float scale = output->wlr_output->scale;
1056 struct wlr_box box = { 1112 struct wlr_box box = {
1057 .x = con->x * scale, 1113 .x = con->x - output->wlr_output->lx,
1058 .y = con->y * scale, 1114 .y = con->y - output->wlr_output->ly,
1059 .width = con->width * scale, 1115 .width = con->width,
1060 .height = con->height * scale, 1116 .height = con->height,
1061 }; 1117 };
1118 scale_box(&box, output->wlr_output->scale);
1062 wlr_output_damage_add_box(output->damage, &box); 1119 wlr_output_damage_add_box(output->damage, &box);
1063
1064 if (con->type == C_VIEW) {
1065 output_damage_whole_container_iterator(con, output);
1066 } else {
1067 container_descendants(con, C_VIEW,
1068 output_damage_whole_container_iterator, output);
1069 }
1070} 1120}
1071 1121
1072static void damage_handle_destroy(struct wl_listener *listener, void *data) { 1122static void damage_handle_destroy(struct wl_listener *listener, void *data) {
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index b2b95fa0..d2b8822c 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -87,7 +87,7 @@ static const char *get_string_prop(struct sway_view *view, enum sway_view_prop p
87 } 87 }
88} 88}
89 89
90static void configure(struct sway_view *view, double ox, double oy, int width, 90static void configure(struct sway_view *view, double lx, double ly, int width,
91 int height) { 91 int height) {
92 struct sway_xdg_shell_view *xdg_shell_view = 92 struct sway_xdg_shell_view *xdg_shell_view =
93 xdg_shell_view_from_view(view); 93 xdg_shell_view_from_view(view);
@@ -98,6 +98,7 @@ static void configure(struct sway_view *view, double ox, double oy, int width,
98 xdg_shell_view->pending_width = width; 98 xdg_shell_view->pending_width = width;
99 xdg_shell_view->pending_height = height; 99 xdg_shell_view->pending_height = height;
100 wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); 100 wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height);
101 view_update_position(view, lx, ly);
101} 102}
102 103
103static void set_activated(struct sway_view *view, bool activated) { 104static void set_activated(struct sway_view *view, bool activated) {
@@ -118,6 +119,14 @@ static void set_fullscreen(struct sway_view *view, bool fullscreen) {
118 wlr_xdg_toplevel_set_fullscreen(surface, fullscreen); 119 wlr_xdg_toplevel_set_fullscreen(surface, fullscreen);
119} 120}
120 121
122static bool wants_floating(struct sway_view *view) {
123 struct wlr_xdg_toplevel_state *state =
124 &view->wlr_xdg_surface->toplevel->current;
125 return state->min_width != 0 && state->min_height != 0
126 && state->min_width == state->max_width
127 && state->min_height == state->max_height;
128}
129
121static void for_each_surface(struct sway_view *view, 130static void for_each_surface(struct sway_view *view,
122 wlr_surface_iterator_func_t iterator, void *user_data) { 131 wlr_surface_iterator_func_t iterator, void *user_data) {
123 if (xdg_shell_view_from_view(view) == NULL) { 132 if (xdg_shell_view_from_view(view) == NULL) {
@@ -155,6 +164,7 @@ static const struct sway_view_impl view_impl = {
155 .configure = configure, 164 .configure = configure,
156 .set_activated = set_activated, 165 .set_activated = set_activated,
157 .set_fullscreen = set_fullscreen, 166 .set_fullscreen = set_fullscreen,
167 .wants_floating = wants_floating,
158 .for_each_surface = for_each_surface, 168 .for_each_surface = for_each_surface,
159 .close = _close, 169 .close = _close,
160 .destroy = destroy, 170 .destroy = destroy,
@@ -164,11 +174,18 @@ static void handle_commit(struct wl_listener *listener, void *data) {
164 struct sway_xdg_shell_view *xdg_shell_view = 174 struct sway_xdg_shell_view *xdg_shell_view =
165 wl_container_of(listener, xdg_shell_view, commit); 175 wl_container_of(listener, xdg_shell_view, commit);
166 struct sway_view *view = &xdg_shell_view->view; 176 struct sway_view *view = &xdg_shell_view->view;
167 // NOTE: We intentionally discard the view's desired width here 177 if (view->swayc && container_is_floating(view->swayc)) {
168 // TODO: Store this for restoration when moving to floating plane 178 int width = view->wlr_xdg_surface->geometry.width;
169 // TODO: Let floating views do whatever 179 int height = view->wlr_xdg_surface->geometry.height;
170 view_update_size(view, xdg_shell_view->pending_width, 180 if (!width && !height) {
171 xdg_shell_view->pending_height); 181 width = view->wlr_xdg_surface->surface->current->width;
182 height = view->wlr_xdg_surface->surface->current->height;
183 }
184 view_update_size(view, width, height);
185 } else {
186 view_update_size(view, xdg_shell_view->pending_width,
187 xdg_shell_view->pending_height);
188 }
172 view_update_title(view, false); 189 view_update_title(view, false);
173 view_damage_from(view); 190 view_damage_from(view);
174} 191}
@@ -196,6 +213,12 @@ static void handle_map(struct wl_listener *listener, void *data) {
196 struct sway_view *view = &xdg_shell_view->view; 213 struct sway_view *view = &xdg_shell_view->view;
197 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; 214 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface;
198 215
216 view->natural_width = view->wlr_xdg_surface->geometry.width;
217 view->natural_height = view->wlr_xdg_surface->geometry.height;
218 if (!view->natural_width && !view->natural_height) {
219 view->natural_width = view->wlr_xdg_surface->surface->current->width;
220 view->natural_height = view->wlr_xdg_surface->surface->current->height;
221 }
199 view_map(view, view->wlr_xdg_surface->surface); 222 view_map(view, view->wlr_xdg_surface->surface);
200 223
201 xdg_shell_view->commit.notify = handle_commit; 224 xdg_shell_view->commit.notify = handle_commit;
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index d098c797..6ffe334a 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -86,7 +86,7 @@ static const char *get_string_prop(struct sway_view *view, enum sway_view_prop p
86 } 86 }
87} 87}
88 88
89static void configure(struct sway_view *view, double ox, double oy, int width, 89static void configure(struct sway_view *view, double lx, double ly, int width,
90 int height) { 90 int height) {
91 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 91 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
92 xdg_shell_v6_view_from_view(view); 92 xdg_shell_v6_view_from_view(view);
@@ -97,6 +97,7 @@ static void configure(struct sway_view *view, double ox, double oy, int width,
97 xdg_shell_v6_view->pending_width = width; 97 xdg_shell_v6_view->pending_width = width;
98 xdg_shell_v6_view->pending_height = height; 98 xdg_shell_v6_view->pending_height = height;
99 wlr_xdg_toplevel_v6_set_size(view->wlr_xdg_surface_v6, width, height); 99 wlr_xdg_toplevel_v6_set_size(view->wlr_xdg_surface_v6, width, height);
100 view_update_position(view, lx, ly);
100} 101}
101 102
102static void set_activated(struct sway_view *view, bool activated) { 103static void set_activated(struct sway_view *view, bool activated) {
@@ -117,6 +118,14 @@ static void set_fullscreen(struct sway_view *view, bool fullscreen) {
117 wlr_xdg_toplevel_v6_set_fullscreen(surface, fullscreen); 118 wlr_xdg_toplevel_v6_set_fullscreen(surface, fullscreen);
118} 119}
119 120
121static bool wants_floating(struct sway_view *view) {
122 struct wlr_xdg_toplevel_v6_state *state =
123 &view->wlr_xdg_surface_v6->toplevel->current;
124 return state->min_width != 0 && state->min_height != 0
125 && state->min_width == state->max_width
126 && state->min_height == state->max_height;
127}
128
120static void for_each_surface(struct sway_view *view, 129static void for_each_surface(struct sway_view *view,
121 wlr_surface_iterator_func_t iterator, void *user_data) { 130 wlr_surface_iterator_func_t iterator, void *user_data) {
122 if (xdg_shell_v6_view_from_view(view) == NULL) { 131 if (xdg_shell_v6_view_from_view(view) == NULL) {
@@ -154,6 +163,7 @@ static const struct sway_view_impl view_impl = {
154 .configure = configure, 163 .configure = configure,
155 .set_activated = set_activated, 164 .set_activated = set_activated,
156 .set_fullscreen = set_fullscreen, 165 .set_fullscreen = set_fullscreen,
166 .wants_floating = wants_floating,
157 .for_each_surface = for_each_surface, 167 .for_each_surface = for_each_surface,
158 .close = _close, 168 .close = _close,
159 .destroy = destroy, 169 .destroy = destroy,
@@ -163,11 +173,18 @@ static void handle_commit(struct wl_listener *listener, void *data) {
163 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 173 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
164 wl_container_of(listener, xdg_shell_v6_view, commit); 174 wl_container_of(listener, xdg_shell_v6_view, commit);
165 struct sway_view *view = &xdg_shell_v6_view->view; 175 struct sway_view *view = &xdg_shell_v6_view->view;
166 // NOTE: We intentionally discard the view's desired width here 176 if (view->swayc && container_is_floating(view->swayc)) {
167 // TODO: Store this for restoration when moving to floating plane 177 int width = view->wlr_xdg_surface_v6->geometry.width;
168 // TODO: Let floating views do whatever 178 int height = view->wlr_xdg_surface_v6->geometry.height;
169 view_update_size(view, xdg_shell_v6_view->pending_width, 179 if (!width && !height) {
170 xdg_shell_v6_view->pending_height); 180 width = view->wlr_xdg_surface_v6->surface->current->width;
181 height = view->wlr_xdg_surface_v6->surface->current->height;
182 }
183 view_update_size(view, width, height);
184 } else {
185 view_update_size(view, xdg_shell_v6_view->pending_width,
186 xdg_shell_v6_view->pending_height);
187 }
171 view_update_title(view, false); 188 view_update_title(view, false);
172 view_damage_from(view); 189 view_damage_from(view);
173} 190}
@@ -195,6 +212,12 @@ static void handle_map(struct wl_listener *listener, void *data) {
195 struct sway_view *view = &xdg_shell_v6_view->view; 212 struct sway_view *view = &xdg_shell_v6_view->view;
196 struct wlr_xdg_surface_v6 *xdg_surface = view->wlr_xdg_surface_v6; 213 struct wlr_xdg_surface_v6 *xdg_surface = view->wlr_xdg_surface_v6;
197 214
215 view->natural_width = view->wlr_xdg_surface_v6->geometry.width;
216 view->natural_height = view->wlr_xdg_surface_v6->geometry.height;
217 if (!view->natural_width && !view->natural_height) {
218 view->natural_width = view->wlr_xdg_surface_v6->surface->current->width;
219 view->natural_height = view->wlr_xdg_surface_v6->surface->current->height;
220 }
198 view_map(view, view->wlr_xdg_surface_v6->surface); 221 view_map(view, view->wlr_xdg_surface_v6->surface);
199 222
200 xdg_shell_v6_view->commit.notify = handle_commit; 223 xdg_shell_v6_view->commit.notify = handle_commit;
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 6a99a66a..75bfb7b2 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -152,7 +152,7 @@ static uint32_t get_int_prop(struct sway_view *view, enum sway_view_prop prop) {
152 } 152 }
153} 153}
154 154
155static void configure(struct sway_view *view, double ox, double oy, int width, 155static void configure(struct sway_view *view, double lx, double ly, int width,
156 int height) { 156 int height) {
157 struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); 157 struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view);
158 if (xwayland_view == NULL) { 158 if (xwayland_view == NULL) {
@@ -160,25 +160,11 @@ static void configure(struct sway_view *view, double ox, double oy, int width,
160 } 160 }
161 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 161 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
162 162
163 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 163 xwayland_view->pending_lx = lx;
164 if (!sway_assert(output, "view must be within tree to set position")) { 164 xwayland_view->pending_ly = ly;
165 return;
166 }
167 struct sway_container *root = container_parent(output, C_ROOT);
168 if (!sway_assert(root, "output must be within tree to set position")) {
169 return;
170 }
171 struct wlr_output_layout *layout = root->sway_root->output_layout;
172 struct wlr_output_layout_output *loutput =
173 wlr_output_layout_get(layout, output->sway_output->wlr_output);
174 if (!sway_assert(loutput, "output must be within layout to set position")) {
175 return;
176 }
177
178 xwayland_view->pending_width = width; 165 xwayland_view->pending_width = width;
179 xwayland_view->pending_height = height; 166 xwayland_view->pending_height = height;
180 wlr_xwayland_surface_configure(xsurface, ox + loutput->x, oy + loutput->y, 167 wlr_xwayland_surface_configure(xsurface, lx, ly, width, height);
181 width, height);
182} 168}
183 169
184static void set_activated(struct sway_view *view, bool activated) { 170static void set_activated(struct sway_view *view, bool activated) {
@@ -197,6 +183,19 @@ static void set_fullscreen(struct sway_view *view, bool fullscreen) {
197 wlr_xwayland_surface_set_fullscreen(surface, fullscreen); 183 wlr_xwayland_surface_set_fullscreen(surface, fullscreen);
198} 184}
199 185
186static bool wants_floating(struct sway_view *view) {
187 // TODO:
188 // We want to return true if the window type contains any of these:
189 // NET_WM_WINDOW_TYPE_DIALOG
190 // NET_WM_WINDOW_TYPE_UTILITY
191 // NET_WM_WINDOW_TYPE_TOOLBAR
192 // NET_WM_WINDOW_TYPE_SPLASH
193 //
194 // We also want to return true if the NET_WM_STATE is MODAL.
195 // wlroots doesn't appear to provide all this information at the moment.
196 return false;
197}
198
200static void _close(struct sway_view *view) { 199static void _close(struct sway_view *view) {
201 if (xwayland_view_from_view(view) == NULL) { 200 if (xwayland_view_from_view(view) == NULL) {
202 return; 201 return;
@@ -226,6 +225,7 @@ static const struct sway_view_impl view_impl = {
226 .configure = configure, 225 .configure = configure,
227 .set_activated = set_activated, 226 .set_activated = set_activated,
228 .set_fullscreen = set_fullscreen, 227 .set_fullscreen = set_fullscreen,
228 .wants_floating = wants_floating,
229 .close = _close, 229 .close = _close,
230 .destroy = destroy, 230 .destroy = destroy,
231}; 231};
@@ -234,10 +234,15 @@ static void handle_commit(struct wl_listener *listener, void *data) {
234 struct sway_xwayland_view *xwayland_view = 234 struct sway_xwayland_view *xwayland_view =
235 wl_container_of(listener, xwayland_view, commit); 235 wl_container_of(listener, xwayland_view, commit);
236 struct sway_view *view = &xwayland_view->view; 236 struct sway_view *view = &xwayland_view->view;
237 // NOTE: We intentionally discard the view's desired width here 237 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
238 // TODO: Let floating views do whatever 238 if (view->swayc && container_is_floating(view->swayc)) {
239 view_update_size(view, xwayland_view->pending_width, 239 view_update_size(view, xsurface->width, xsurface->height);
240 xwayland_view->pending_height); 240 } else {
241 view_update_size(view, xwayland_view->pending_width,
242 xwayland_view->pending_height);
243 }
244 view_update_position(view,
245 xwayland_view->pending_lx, xwayland_view->pending_ly);
241 view_damage_from(view); 246 view_damage_from(view);
242} 247}
243 248
@@ -254,6 +259,9 @@ static void handle_map(struct wl_listener *listener, void *data) {
254 struct wlr_xwayland_surface *xsurface = data; 259 struct wlr_xwayland_surface *xsurface = data;
255 struct sway_view *view = &xwayland_view->view; 260 struct sway_view *view = &xwayland_view->view;
256 261
262 view->natural_width = xsurface->width;
263 view->natural_height = xsurface->height;
264
257 // Wire up the commit listener here, because xwayland map/unmap can change 265 // Wire up the commit listener here, because xwayland map/unmap can change
258 // the underlying wlr_surface 266 // the underlying wlr_surface
259 wl_signal_add(&xsurface->surface->events.commit, &xwayland_view->commit); 267 wl_signal_add(&xsurface->surface->events.commit, &xwayland_view->commit);
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 6751931d..16e5427b 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -46,7 +46,7 @@ static struct wlr_surface *layer_surface_at(struct sway_output *output,
46 * location, it is stored in **surface (it may not be a view). 46 * location, it is stored in **surface (it may not be a view).
47 */ 47 */
48static struct sway_container *container_at_coords( 48static struct sway_container *container_at_coords(
49 struct sway_seat *seat, double x, double y, 49 struct sway_seat *seat, double lx, double ly,
50 struct wlr_surface **surface, double *sx, double *sy) { 50 struct wlr_surface **surface, double *sx, double *sy) {
51 // check for unmanaged views first 51 // check for unmanaged views first
52 struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged; 52 struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged;
@@ -55,8 +55,8 @@ static struct sway_container *container_at_coords(
55 struct wlr_xwayland_surface *xsurface = 55 struct wlr_xwayland_surface *xsurface =
56 unmanaged_surface->wlr_xwayland_surface; 56 unmanaged_surface->wlr_xwayland_surface;
57 57
58 double _sx = x - unmanaged_surface->lx; 58 double _sx = lx - unmanaged_surface->lx;
59 double _sy = y - unmanaged_surface->ly; 59 double _sy = ly - unmanaged_surface->ly;
60 if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) { 60 if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) {
61 *surface = xsurface->surface; 61 *surface = xsurface->surface;
62 *sx = _sx; 62 *sx = _sx;
@@ -69,12 +69,12 @@ static struct sway_container *container_at_coords(
69 struct wlr_output_layout *output_layout = 69 struct wlr_output_layout *output_layout =
70 root_container.sway_root->output_layout; 70 root_container.sway_root->output_layout;
71 struct wlr_output *wlr_output = wlr_output_layout_output_at( 71 struct wlr_output *wlr_output = wlr_output_layout_output_at(
72 output_layout, x, y); 72 output_layout, lx, ly);
73 if (wlr_output == NULL) { 73 if (wlr_output == NULL) {
74 return NULL; 74 return NULL;
75 } 75 }
76 struct sway_output *output = wlr_output->data; 76 struct sway_output *output = wlr_output->data;
77 double ox = x, oy = y; 77 double ox = lx, oy = ly;
78 wlr_output_layout_output_coords(output_layout, wlr_output, &ox, &oy); 78 wlr_output_layout_output_coords(output_layout, wlr_output, &ox, &oy);
79 79
80 // find the focused workspace on the output for this seat 80 // find the focused workspace on the output for this seat
@@ -108,7 +108,10 @@ static struct sway_container *container_at_coords(
108 } 108 }
109 109
110 struct sway_container *c; 110 struct sway_container *c;
111 if ((c = container_at(ws, ox, oy, surface, sx, sy))) { 111 if ((c = floating_container_at(lx, ly, surface, sx, sy))) {
112 return c;
113 }
114 if ((c = container_at(ws, lx, ly, surface, sx, sy))) {
112 return c; 115 return c;
113 } 116 }
114 117
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 0295212c..d35cbeef 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -113,7 +113,14 @@ static void seat_send_focus(struct sway_container *con,
113 113
114static struct sway_container *seat_get_focus_by_type(struct sway_seat *seat, 114static struct sway_container *seat_get_focus_by_type(struct sway_seat *seat,
115 struct sway_container *container, enum sway_container_type type) { 115 struct sway_container *container, enum sway_container_type type) {
116 if (container->type == C_VIEW || container->children->length == 0) { 116 if (container->type == C_VIEW) {
117 return container;
118 }
119
120 struct sway_container *floating = container->type == C_WORKSPACE ?
121 container->sway_workspace->floating : NULL;
122 if (container->children->length == 0 &&
123 (!floating || floating->children->length == 0)) {
117 return container; 124 return container;
118 } 125 }
119 126
@@ -126,6 +133,9 @@ static struct sway_container *seat_get_focus_by_type(struct sway_seat *seat,
126 if (container_has_child(container, current->container)) { 133 if (container_has_child(container, current->container)) {
127 return current->container; 134 return current->container;
128 } 135 }
136 if (floating && container_has_child(floating, current->container)) {
137 return current->container;
138 }
129 } 139 }
130 140
131 return NULL; 141 return NULL;
@@ -568,7 +578,7 @@ void seat_set_focus_warp(struct sway_seat *seat,
568 // clean up unfocused empty workspace on new output 578 // clean up unfocused empty workspace on new output
569 if (new_output_last_ws) { 579 if (new_output_last_ws) {
570 if (!workspace_is_visible(new_output_last_ws) 580 if (!workspace_is_visible(new_output_last_ws)
571 && new_output_last_ws->children->length == 0) { 581 && workspace_is_empty(new_output_last_ws)) {
572 if (last_workspace == new_output_last_ws) { 582 if (last_workspace == new_output_last_ws) {
573 last_focus = NULL; 583 last_focus = NULL;
574 last_workspace = NULL; 584 last_workspace = NULL;
@@ -581,7 +591,7 @@ void seat_set_focus_warp(struct sway_seat *seat,
581 if (last_workspace) { 591 if (last_workspace) {
582 ipc_event_workspace(last_workspace, container, "focus"); 592 ipc_event_workspace(last_workspace, container, "focus");
583 if (!workspace_is_visible(last_workspace) 593 if (!workspace_is_visible(last_workspace)
584 && last_workspace->children->length == 0) { 594 && workspace_is_empty(last_workspace)) {
585 if (last_workspace == last_focus) { 595 if (last_workspace == last_focus) {
586 last_focus = NULL; 596 last_focus = NULL;
587 } 597 }
@@ -591,10 +601,8 @@ void seat_set_focus_warp(struct sway_seat *seat,
591 601
592 if (config->mouse_warping && warp) { 602 if (config->mouse_warping && warp) {
593 if (new_output && last_output && new_output != last_output) { 603 if (new_output && last_output && new_output != last_output) {
594 double x = new_output->x + container->x + 604 double x = container->x + container->width / 2.0;
595 container->width / 2.0; 605 double y = container->y + container->height / 2.0;
596 double y = new_output->y + container->y +
597 container->height / 2.0;
598 struct wlr_output *wlr_output = 606 struct wlr_output *wlr_output =
599 new_output->sway_output->wlr_output; 607 new_output->sway_output->wlr_output;
600 if (!wlr_output_layout_contains_point( 608 if (!wlr_output_layout_contains_point(
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 03582950..6d185449 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -4,6 +4,7 @@
4#include "log.h" 4#include "log.h"
5#include "sway/ipc-json.h" 5#include "sway/ipc-json.h"
6#include "sway/tree/container.h" 6#include "sway/tree/container.h"
7#include "sway/tree/workspace.h"
7#include "sway/output.h" 8#include "sway/output.h"
8#include "sway/input/input-manager.h" 9#include "sway/input/input-manager.h"
9#include "sway/input/seat.h" 10#include "sway/input/seat.h"
@@ -152,6 +153,15 @@ static void ipc_json_describe_workspace(struct sway_container *workspace,
152 153
153 const char *layout = ipc_json_layout_description(workspace->layout); 154 const char *layout = ipc_json_layout_description(workspace->layout);
154 json_object_object_add(object, "layout", json_object_new_string(layout)); 155 json_object_object_add(object, "layout", json_object_new_string(layout));
156
157 // Floating
158 json_object *floating_array = json_object_new_array();
159 struct sway_container *floating = workspace->sway_workspace->floating;
160 for (int i = 0; i < floating->children->length; ++i) {
161 struct sway_container *floater = floating->children->items[i];
162 json_object_array_add(floating_array, ipc_json_describe_container_recursive(floater));
163 }
164 json_object_object_add(object, "floating_nodes", floating_array);
155} 165}
156 166
157static void ipc_json_describe_view(struct sway_container *c, json_object *object) { 167static void ipc_json_describe_view(struct sway_container *c, json_object *object) {
diff --git a/sway/meson.build b/sway/meson.build
index 68675f67..4c038583 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -36,6 +36,7 @@ sway_sources = files(
36 'commands/exit.c', 36 'commands/exit.c',
37 'commands/exec.c', 37 'commands/exec.c',
38 'commands/exec_always.c', 38 'commands/exec_always.c',
39 'commands/floating.c',
39 'commands/focus.c', 40 'commands/focus.c',
40 'commands/focus_follows_mouse.c', 41 'commands/focus_follows_mouse.c',
41 'commands/focus_wrapping.c', 42 'commands/focus_wrapping.c',
@@ -64,6 +65,7 @@ sway_sources = files(
64 'commands/set.c', 65 'commands/set.c',
65 'commands/show_marks.c', 66 'commands/show_marks.c',
66 'commands/split.c', 67 'commands/split.c',
68 'commands/sticky.c',
67 'commands/swaybg_command.c', 69 'commands/swaybg_command.c',
68 'commands/swap.c', 70 'commands/swap.c',
69 'commands/title_format.c', 71 'commands/title_format.c',
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index bdef56ea..721b557e 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -73,8 +73,8 @@ void arrange_workspace(struct sway_container *workspace) {
73 area->width, area->height, area->x, area->y); 73 area->width, area->height, area->x, area->y);
74 workspace->width = area->width; 74 workspace->width = area->width;
75 workspace->height = area->height; 75 workspace->height = area->height;
76 workspace->x = area->x; 76 workspace->x = output->x + area->x;
77 workspace->y = area->y; 77 workspace->y = output->y + area->y;
78 wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", 78 wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f",
79 workspace->name, workspace->x, workspace->y); 79 workspace->name, workspace->x, workspace->y);
80 arrange_children_of(workspace); 80 arrange_children_of(workspace);
@@ -247,6 +247,18 @@ void arrange_children_of(struct sway_container *parent) {
247 arrange_children_of(child); 247 arrange_children_of(child);
248 } 248 }
249 } 249 }
250
251 // If container is a workspace, process floating containers too
252 if (parent->type == C_WORKSPACE) {
253 struct sway_workspace *ws = workspace->sway_workspace;
254 for (int i = 0; i < ws->floating->children->length; ++i) {
255 struct sway_container *child = ws->floating->children->items[i];
256 if (child->type != C_VIEW) {
257 arrange_children_of(child);
258 }
259 }
260 }
261
250 container_damage_whole(parent); 262 container_damage_whole(parent);
251 update_debug_tree(); 263 update_debug_tree();
252} 264}
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 33d88d7f..9e70da09 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -64,16 +64,6 @@ void container_create_notify(struct sway_container *container) {
64 } 64 }
65} 65}
66 66
67static void container_close_notify(struct sway_container *container) {
68 if (container == NULL) {
69 return;
70 }
71 // TODO send ipc event type based on the container type
72 if (container->type == C_VIEW || container->type == C_WORKSPACE) {
73 ipc_event_window(container, "close");
74 }
75}
76
77static void container_update_textures_recursive(struct sway_container *con) { 67static void container_update_textures_recursive(struct sway_container *con) {
78 container_update_title_textures(con); 68 container_update_title_textures(con);
79 69
@@ -143,7 +133,6 @@ static void _container_destroy(struct sway_container *cont) {
143 } 133 }
144 134
145 wl_signal_emit(&cont->events.destroy, cont); 135 wl_signal_emit(&cont->events.destroy, cont);
146 container_close_notify(cont);
147 136
148 struct sway_container *parent = cont->parent; 137 struct sway_container *parent = cont->parent;
149 if (cont->children != NULL && cont->children->length) { 138 if (cont->children != NULL && cont->children->length) {
@@ -151,6 +140,7 @@ static void _container_destroy(struct sway_container *cont) {
151 // container_remove_child, which removes child from this container 140 // container_remove_child, which removes child from this container
152 while (cont->children != NULL && cont->children->length > 0) { 141 while (cont->children != NULL && cont->children->length > 0) {
153 struct sway_container *child = cont->children->items[0]; 142 struct sway_container *child = cont->children->items[0];
143 ipc_event_window(child, "close");
154 container_remove_child(child); 144 container_remove_child(child);
155 _container_destroy(child); 145 _container_destroy(child);
156 } 146 }
@@ -188,15 +178,13 @@ static struct sway_container *container_workspace_destroy(
188 return NULL; 178 return NULL;
189 } 179 }
190 180
181 wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
182 ipc_event_window(workspace, "close");
183
191 struct sway_container *parent = workspace->parent; 184 struct sway_container *parent = workspace->parent;
192 if (workspace->children->length == 0) { 185 if (!workspace_is_empty(workspace) && output) {
193 // destroy the WS if there are no children (TODO check for floating)
194 wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
195 ipc_event_workspace(workspace, NULL, "empty");
196 } else if (output) {
197 // Move children to a different workspace on this output 186 // Move children to a different workspace on this output
198 struct sway_container *new_workspace = NULL; 187 struct sway_container *new_workspace = NULL;
199 // TODO move floating
200 for (int i = 0; i < output->children->length; i++) { 188 for (int i = 0; i < output->children->length; i++) {
201 if (output->children->items[i] != workspace) { 189 if (output->children->items[i] != workspace) {
202 new_workspace = output->children->items[i]; 190 new_workspace = output->children->items[i];
@@ -209,6 +197,11 @@ static struct sway_container *container_workspace_destroy(
209 for (int i = 0; i < workspace->children->length; i++) { 197 for (int i = 0; i < workspace->children->length; i++) {
210 container_move_to(workspace->children->items[i], new_workspace); 198 container_move_to(workspace->children->items[i], new_workspace);
211 } 199 }
200 struct sway_container *floating = workspace->sway_workspace->floating;
201 for (int i = 0; i < floating->children->length; i++) {
202 container_move_to(floating->children->items[i],
203 new_workspace->sway_workspace->floating);
204 }
212 } 205 }
213 206
214 free(workspace->sway_workspace); 207 free(workspace->sway_workspace);
@@ -275,13 +268,17 @@ static void container_root_finish(struct sway_container *con) {
275} 268}
276 269
277bool container_reap_empty(struct sway_container *con) { 270bool container_reap_empty(struct sway_container *con) {
271 if (con->layout == L_FLOATING) {
272 // Don't reap the magical floating container that each workspace has
273 return false;
274 }
278 switch (con->type) { 275 switch (con->type) {
279 case C_ROOT: 276 case C_ROOT:
280 case C_OUTPUT: 277 case C_OUTPUT:
281 // dont reap these 278 // dont reap these
282 break; 279 break;
283 case C_WORKSPACE: 280 case C_WORKSPACE:
284 if (!workspace_is_visible(con) && con->children->length == 0) { 281 if (!workspace_is_visible(con) && workspace_is_empty(con)) {
285 wlr_log(L_DEBUG, "Destroying workspace via reaper"); 282 wlr_log(L_DEBUG, "Destroying workspace via reaper");
286 container_workspace_destroy(con); 283 container_workspace_destroy(con);
287 return true; 284 return true;
@@ -349,10 +346,12 @@ struct sway_container *container_destroy(struct sway_container *con) {
349 if (con->children->length) { 346 if (con->children->length) {
350 for (int i = 0; i < con->children->length; ++i) { 347 for (int i = 0; i < con->children->length; ++i) {
351 struct sway_container *child = con->children->items[0]; 348 struct sway_container *child = con->children->items[0];
349 ipc_event_window(child, "close");
352 container_remove_child(child); 350 container_remove_child(child);
353 container_add_child(parent, child); 351 container_add_child(parent, child);
354 } 352 }
355 } 353 }
354 ipc_event_window(con, "close");
356 _container_destroy(con); 355 _container_destroy(con);
357 break; 356 break;
358 case C_VIEW: 357 case C_VIEW:
@@ -436,7 +435,6 @@ struct sway_container *container_find(struct sway_container *container,
436 if (!container->children) { 435 if (!container->children) {
437 return NULL; 436 return NULL;
438 } 437 }
439 // TODO: floating windows
440 for (int i = 0; i < container->children->length; ++i) { 438 for (int i = 0; i < container->children->length; ++i) {
441 struct sway_container *child = container->children->items[i]; 439 struct sway_container *child = container->children->items[i];
442 if (test(child, data)) { 440 if (test(child, data)) {
@@ -448,6 +446,9 @@ struct sway_container *container_find(struct sway_container *container,
448 } 446 }
449 } 447 }
450 } 448 }
449 if (container->type == C_WORKSPACE) {
450 return container_find(container->sway_workspace->floating, test, data);
451 }
451 return NULL; 452 return NULL;
452} 453}
453 454
@@ -466,14 +467,14 @@ struct sway_container *container_parent(struct sway_container *container,
466} 467}
467 468
468static struct sway_container *container_at_view(struct sway_container *swayc, 469static struct sway_container *container_at_view(struct sway_container *swayc,
469 double ox, double oy, 470 double lx, double ly,
470 struct wlr_surface **surface, double *sx, double *sy) { 471 struct wlr_surface **surface, double *sx, double *sy) {
471 if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) { 472 if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) {
472 return NULL; 473 return NULL;
473 } 474 }
474 struct sway_view *sview = swayc->sway_view; 475 struct sway_view *sview = swayc->sway_view;
475 double view_sx = ox - sview->x; 476 double view_sx = lx - sview->x;
476 double view_sy = oy - sview->y; 477 double view_sy = ly - sview->y;
477 478
478 double _sx, _sy; 479 double _sx, _sy;
479 struct wlr_surface *_surface = NULL; 480 struct wlr_surface *_surface = NULL;
@@ -515,18 +516,18 @@ static struct sway_container *container_at_view(struct sway_container *swayc,
515 * container_at for a container with layout L_TABBED. 516 * container_at for a container with layout L_TABBED.
516 */ 517 */
517static struct sway_container *container_at_tabbed(struct sway_container *parent, 518static struct sway_container *container_at_tabbed(struct sway_container *parent,
518 double ox, double oy, 519 double lx, double ly,
519 struct wlr_surface **surface, double *sx, double *sy) { 520 struct wlr_surface **surface, double *sx, double *sy) {
520 if (oy < parent->y || oy > parent->y + parent->height) { 521 if (ly < parent->y || ly > parent->y + parent->height) {
521 return NULL; 522 return NULL;
522 } 523 }
523 struct sway_seat *seat = input_manager_current_seat(input_manager); 524 struct sway_seat *seat = input_manager_current_seat(input_manager);
524 525
525 // Tab titles 526 // Tab titles
526 int title_height = container_titlebar_height(); 527 int title_height = container_titlebar_height();
527 if (oy < parent->y + title_height) { 528 if (ly < parent->y + title_height) {
528 int tab_width = parent->width / parent->children->length; 529 int tab_width = parent->width / parent->children->length;
529 int child_index = (ox - parent->x) / tab_width; 530 int child_index = (lx - parent->x) / tab_width;
530 if (child_index >= parent->children->length) { 531 if (child_index >= parent->children->length) {
531 child_index = parent->children->length - 1; 532 child_index = parent->children->length - 1;
532 } 533 }
@@ -537,23 +538,23 @@ static struct sway_container *container_at_tabbed(struct sway_container *parent,
537 // Surfaces 538 // Surfaces
538 struct sway_container *current = seat_get_active_child(seat, parent); 539 struct sway_container *current = seat_get_active_child(seat, parent);
539 540
540 return container_at(current, ox, oy, surface, sx, sy); 541 return container_at(current, lx, ly, surface, sx, sy);
541} 542}
542 543
543/** 544/**
544 * container_at for a container with layout L_STACKED. 545 * container_at for a container with layout L_STACKED.
545 */ 546 */
546static struct sway_container *container_at_stacked( 547static struct sway_container *container_at_stacked(
547 struct sway_container *parent, double ox, double oy, 548 struct sway_container *parent, double lx, double ly,
548 struct wlr_surface **surface, double *sx, double *sy) { 549 struct wlr_surface **surface, double *sx, double *sy) {
549 if (oy < parent->y || oy > parent->y + parent->height) { 550 if (ly < parent->y || ly > parent->y + parent->height) {
550 return NULL; 551 return NULL;
551 } 552 }
552 struct sway_seat *seat = input_manager_current_seat(input_manager); 553 struct sway_seat *seat = input_manager_current_seat(input_manager);
553 554
554 // Title bars 555 // Title bars
555 int title_height = container_titlebar_height(); 556 int title_height = container_titlebar_height();
556 int child_index = (oy - parent->y) / title_height; 557 int child_index = (ly - parent->y) / title_height;
557 if (child_index < parent->children->length) { 558 if (child_index < parent->children->length) {
558 struct sway_container *child = parent->children->items[child_index]; 559 struct sway_container *child = parent->children->items[child_index];
559 return seat_get_focus_inactive(seat, child); 560 return seat_get_focus_inactive(seat, child);
@@ -562,14 +563,14 @@ static struct sway_container *container_at_stacked(
562 // Surfaces 563 // Surfaces
563 struct sway_container *current = seat_get_active_child(seat, parent); 564 struct sway_container *current = seat_get_active_child(seat, parent);
564 565
565 return container_at(current, ox, oy, surface, sx, sy); 566 return container_at(current, lx, ly, surface, sx, sy);
566} 567}
567 568
568/** 569/**
569 * container_at for a container with layout L_HORIZ or L_VERT. 570 * container_at for a container with layout L_HORIZ or L_VERT.
570 */ 571 */
571static struct sway_container *container_at_linear(struct sway_container *parent, 572static struct sway_container *container_at_linear(struct sway_container *parent,
572 double ox, double oy, 573 double lx, double ly,
573 struct wlr_surface **surface, double *sx, double *sy) { 574 struct wlr_surface **surface, double *sx, double *sy) {
574 for (int i = 0; i < parent->children->length; ++i) { 575 for (int i = 0; i < parent->children->length; ++i) {
575 struct sway_container *child = parent->children->items[i]; 576 struct sway_container *child = parent->children->items[i];
@@ -579,22 +580,22 @@ static struct sway_container *container_at_linear(struct sway_container *parent,
579 .width = child->width, 580 .width = child->width,
580 .height = child->height, 581 .height = child->height,
581 }; 582 };
582 if (wlr_box_contains_point(&box, ox, oy)) { 583 if (wlr_box_contains_point(&box, lx, ly)) {
583 return container_at(child, ox, oy, surface, sx, sy); 584 return container_at(child, lx, ly, surface, sx, sy);
584 } 585 }
585 } 586 }
586 return NULL; 587 return NULL;
587} 588}
588 589
589struct sway_container *container_at(struct sway_container *parent, 590struct sway_container *container_at(struct sway_container *parent,
590 double ox, double oy, 591 double lx, double ly,
591 struct wlr_surface **surface, double *sx, double *sy) { 592 struct wlr_surface **surface, double *sx, double *sy) {
592 if (!sway_assert(parent->type >= C_WORKSPACE, 593 if (!sway_assert(parent->type >= C_WORKSPACE,
593 "Expected workspace or deeper")) { 594 "Expected workspace or deeper")) {
594 return NULL; 595 return NULL;
595 } 596 }
596 if (parent->type == C_VIEW) { 597 if (parent->type == C_VIEW) {
597 return container_at_view(parent, ox, oy, surface, sx, sy); 598 return container_at_view(parent, lx, ly, surface, sx, sy);
598 } 599 }
599 if (!parent->children->length) { 600 if (!parent->children->length) {
600 return NULL; 601 return NULL;
@@ -603,13 +604,14 @@ struct sway_container *container_at(struct sway_container *parent,
603 switch (parent->layout) { 604 switch (parent->layout) {
604 case L_HORIZ: 605 case L_HORIZ:
605 case L_VERT: 606 case L_VERT:
606 return container_at_linear(parent, ox, oy, surface, sx, sy); 607 return container_at_linear(parent, lx, ly, surface, sx, sy);
607 case L_TABBED: 608 case L_TABBED:
608 return container_at_tabbed(parent, ox, oy, surface, sx, sy); 609 return container_at_tabbed(parent, lx, ly, surface, sx, sy);
609 case L_STACKED: 610 case L_STACKED:
610 return container_at_stacked(parent, ox, oy, surface, sx, sy); 611 return container_at_stacked(parent, lx, ly, surface, sx, sy);
611 case L_FLOATING: 612 case L_FLOATING:
612 return NULL; // TODO 613 sway_assert(false, "Didn't expect to see floating here");
614 return NULL;
613 case L_NONE: 615 case L_NONE:
614 return NULL; 616 return NULL;
615 } 617 }
@@ -617,6 +619,34 @@ struct sway_container *container_at(struct sway_container *parent,
617 return NULL; 619 return NULL;
618} 620}
619 621
622struct sway_container *floating_container_at(double lx, double ly,
623 struct wlr_surface **surface, double *sx, double *sy) {
624 for (int i = 0; i < root_container.children->length; ++i) {
625 struct sway_container *output = root_container.children->items[i];
626 for (int j = 0; j < output->children->length; ++j) {
627 struct sway_container *workspace = output->children->items[j];
628 struct sway_workspace *ws = workspace->sway_workspace;
629 if (!workspace_is_visible(workspace)) {
630 continue;
631 }
632 for (int k = 0; k < ws->floating->children->length; ++k) {
633 struct sway_container *floater =
634 ws->floating->children->items[k];
635 struct wlr_box box = {
636 .x = floater->x,
637 .y = floater->y,
638 .width = floater->width,
639 .height = floater->height,
640 };
641 if (wlr_box_contains_point(&box, lx, ly)) {
642 return container_at(floater, lx, ly, surface, sx, sy);
643 }
644 }
645 }
646 }
647 return NULL;
648}
649
620void container_for_each_descendant_dfs(struct sway_container *container, 650void container_for_each_descendant_dfs(struct sway_container *container,
621 void (*f)(struct sway_container *container, void *data), 651 void (*f)(struct sway_container *container, void *data),
622 void *data) { 652 void *data) {
@@ -674,7 +704,7 @@ static bool find_child_func(struct sway_container *con, void *data) {
674 704
675bool container_has_child(struct sway_container *con, 705bool container_has_child(struct sway_container *con,
676 struct sway_container *child) { 706 struct sway_container *child) {
677 if (con == NULL || con->type == C_VIEW || con->children->length == 0) { 707 if (con == NULL || con->type == C_VIEW) {
678 return false; 708 return false;
679 } 709 }
680 return container_find(con, find_child_func, child); 710 return container_find(con, find_child_func, child);
@@ -866,3 +896,60 @@ void container_notify_subtree_changed(struct sway_container *container) {
866size_t container_titlebar_height() { 896size_t container_titlebar_height() {
867 return config->font_height + TITLEBAR_V_PADDING * 2; 897 return config->font_height + TITLEBAR_V_PADDING * 2;
868} 898}
899
900void container_set_floating(struct sway_container *container, bool enable) {
901 if (container_is_floating(container) == enable) {
902 return;
903 }
904
905 struct sway_container *workspace = container_parent(container, C_WORKSPACE);
906 struct sway_seat *seat = input_manager_current_seat(input_manager);
907 container_damage_whole(container);
908
909 if (enable) {
910 container_remove_child(container);
911 container_add_child(workspace->sway_workspace->floating, container);
912 if (container->type == C_VIEW) {
913 view_autoconfigure(container->sway_view);
914 }
915 seat_set_focus(seat, seat_get_focus_inactive(seat, container));
916 container_reap_empty_recursive(workspace);
917 } else {
918 // Returning to tiled
919 container_remove_child(container);
920 container_add_child(workspace, container);
921 container->width = container->parent->width;
922 container->height = container->parent->height;
923 container->is_sticky = false;
924 container_reap_empty_recursive(workspace->sway_workspace->floating);
925 }
926 arrange_workspace(workspace);
927 container_damage_whole(container);
928}
929
930void container_set_geometry_from_floating_view(struct sway_container *con) {
931 if (!sway_assert(con->type == C_VIEW, "Expected a view")) {
932 return;
933 }
934 if (!sway_assert(container_is_floating(con),
935 "Expected a floating view")) {
936 return;
937 }
938 struct sway_view *view = con->sway_view;
939 size_t border_width = view->border_thickness * (view->border != B_NONE);
940 size_t top =
941 view->border == B_NORMAL ? container_titlebar_height() : border_width;
942
943 con->x = view->x - border_width;
944 con->y = view->y - top;
945 con->width = view->width + border_width * 2;
946 con->height = top + view->height + border_width;
947}
948
949bool container_is_floating(struct sway_container *container) {
950 struct sway_container *workspace = container_parent(container, C_WORKSPACE);
951 if (!workspace) {
952 return false;
953 }
954 return container->parent == workspace->sway_workspace->floating;
955}
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index 2f4ae667..d88948dc 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -45,20 +45,14 @@ void layout_init(void) {
45} 45}
46 46
47static int index_child(const struct sway_container *child) { 47static int index_child(const struct sway_container *child) {
48 // TODO handle floating
49 struct sway_container *parent = child->parent; 48 struct sway_container *parent = child->parent;
50 int i, len; 49 for (int i = 0; i < parent->children->length; ++i) {
51 len = parent->children->length;
52 for (i = 0; i < len; ++i) {
53 if (parent->children->items[i] == child) { 50 if (parent->children->items[i] == child) {
54 break; 51 return i;
55 } 52 }
56 } 53 }
57 54 // This happens if the child is a floating container
58 if (!sway_assert(i < len, "Stray container")) { 55 return -1;
59 return -1;
60 }
61 return i;
62} 56}
63 57
64static void container_handle_fullscreen_reparent(struct sway_container *viewcon, 58static void container_handle_fullscreen_reparent(struct sway_container *viewcon,
@@ -160,6 +154,10 @@ void container_move_to(struct sway_container *container,
160 || container_has_ancestor(container, destination)) { 154 || container_has_ancestor(container, destination)) {
161 return; 155 return;
162 } 156 }
157 if (container_is_floating(container)) {
158 // TODO
159 return;
160 }
163 struct sway_container *old_parent = container_remove_child(container); 161 struct sway_container *old_parent = container_remove_child(container);
164 container->width = container->height = 0; 162 container->width = container->height = 0;
165 container->saved_width = container->saved_height = 0; 163 container->saved_width = container->saved_height = 0;
@@ -172,8 +170,9 @@ void container_move_to(struct sway_container *container,
172 } 170 }
173 wl_signal_emit(&container->events.reparent, old_parent); 171 wl_signal_emit(&container->events.reparent, old_parent);
174 if (container->type == C_WORKSPACE) { 172 if (container->type == C_WORKSPACE) {
175 struct sway_seat *seat = input_manager_get_default_seat( 173 // If moving a workspace to a new output, maybe create a new workspace
176 input_manager); 174 // on the previous output
175 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
177 if (old_parent->children->length == 0) { 176 if (old_parent->children->length == 0) {
178 char *ws_name = workspace_next_name(old_parent->name); 177 char *ws_name = workspace_next_name(old_parent->name);
179 struct sway_container *ws = 178 struct sway_container *ws =
@@ -681,26 +680,6 @@ static struct sway_container *get_swayc_in_output_direction(
681 return ws; 680 return ws;
682} 681}
683 682
684static void get_layout_center_position(struct sway_container *container,
685 int *x, int *y) {
686 // FIXME view coords are inconsistently referred to in layout/output systems
687 if (container->type == C_OUTPUT) {
688 *x = container->x + container->width/2;
689 *y = container->y + container->height/2;
690 } else {
691 struct sway_container *output = container_parent(container, C_OUTPUT);
692 if (container->type == C_WORKSPACE) {
693 // Workspace coordinates are actually wrong/arbitrary, but should
694 // be same as output.
695 *x = output->x;
696 *y = output->y;
697 } else {
698 *x = output->x + container->x;
699 *y = output->y + container->y;
700 }
701 }
702}
703
704static struct sway_container *sway_output_from_wlr(struct wlr_output *output) { 683static struct sway_container *sway_output_from_wlr(struct wlr_output *output) {
705 if (output == NULL) { 684 if (output == NULL) {
706 return NULL; 685 return NULL;
@@ -719,6 +698,10 @@ struct sway_container *container_get_in_direction(
719 enum movement_direction dir) { 698 enum movement_direction dir) {
720 struct sway_container *parent = container->parent; 699 struct sway_container *parent = container->parent;
721 700
701 if (container_is_floating(container)) {
702 return NULL;
703 }
704
722 if (container->type == C_VIEW && container->sway_view->is_fullscreen) { 705 if (container->type == C_VIEW && container->sway_view->is_fullscreen) {
723 if (dir == MOVE_PARENT || dir == MOVE_CHILD) { 706 if (dir == MOVE_PARENT || dir == MOVE_CHILD) {
724 return NULL; 707 return NULL;
@@ -743,14 +726,17 @@ struct sway_container *container_get_in_direction(
743 bool can_move = false; 726 bool can_move = false;
744 int desired; 727 int desired;
745 int idx = index_child(container); 728 int idx = index_child(container);
729 if (idx == -1) {
730 return NULL;
731 }
746 if (parent->type == C_ROOT) { 732 if (parent->type == C_ROOT) {
747 enum wlr_direction wlr_dir = 0; 733 enum wlr_direction wlr_dir = 0;
748 if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir), 734 if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir),
749 "got invalid direction: %d", dir)) { 735 "got invalid direction: %d", dir)) {
750 return NULL; 736 return NULL;
751 } 737 }
752 int lx, ly; 738 int lx = container->x + container->width / 2;
753 get_layout_center_position(container, &lx, &ly); 739 int ly = container->y + container->height / 2;
754 struct wlr_output_layout *layout = 740 struct wlr_output_layout *layout =
755 root_container.sway_root->output_layout; 741 root_container.sway_root->output_layout;
756 struct wlr_output *wlr_adjacent = 742 struct wlr_output *wlr_adjacent =
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 26ff1e8d..3117ded6 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -119,13 +119,28 @@ const char *view_get_shell(struct sway_view *view) {
119 return "unknown"; 119 return "unknown";
120} 120}
121 121
122void view_configure(struct sway_view *view, double ox, double oy, int width, 122void view_configure(struct sway_view *view, double lx, double ly, int width,
123 int height) { 123 int height) {
124 if (view->impl->configure) { 124 if (view->impl->configure) {
125 view->impl->configure(view, ox, oy, width, height); 125 view->impl->configure(view, lx, ly, width, height);
126 } 126 }
127} 127}
128 128
129static void view_autoconfigure_floating(struct sway_view *view) {
130 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
131 int max_width = ws->width * 0.6666;
132 int max_height = ws->height * 0.6666;
133 int width =
134 view->natural_width > max_width ? max_width : view->natural_width;
135 int height =
136 view->natural_height > max_height ? max_height : view->natural_height;
137 int lx = ws->x + (ws->width - width) / 2;
138 int ly = ws->y + (ws->height - height) / 2;
139
140 view->border_left = view->border_right = view->border_bottom = true;
141 view_configure(view, lx, ly, width, height);
142}
143
129void view_autoconfigure(struct sway_view *view) { 144void view_autoconfigure(struct sway_view *view) {
130 if (!sway_assert(view->swayc, 145 if (!sway_assert(view->swayc,
131 "Called view_autoconfigure() on a view without a swayc")) { 146 "Called view_autoconfigure() on a view without a swayc")) {
@@ -135,8 +150,12 @@ void view_autoconfigure(struct sway_view *view) {
135 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 150 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
136 151
137 if (view->is_fullscreen) { 152 if (view->is_fullscreen) {
138 view_configure(view, 0, 0, output->width, output->height); 153 view_configure(view, output->x, output->y, output->width, output->height);
139 view->x = view->y = 0; 154 return;
155 }
156
157 if (container_is_floating(view->swayc)) {
158 view_autoconfigure_floating(view);
140 return; 159 return;
141 } 160 }
142 161
@@ -158,21 +177,19 @@ void view_autoconfigure(struct sway_view *view) {
158 177
159 view->border_top = view->border_bottom = true; 178 view->border_top = view->border_bottom = true;
160 view->border_left = view->border_right = true; 179 view->border_left = view->border_right = true;
161 if (view->swayc->layout != L_FLOATING) { 180 if (config->hide_edge_borders == E_BOTH
162 if (config->hide_edge_borders == E_BOTH 181 || config->hide_edge_borders == E_VERTICAL
163 || config->hide_edge_borders == E_VERTICAL 182 || (config->hide_edge_borders == E_SMART && !other_views)) {
164 || (config->hide_edge_borders == E_SMART && !other_views)) { 183 view->border_left = view->swayc->x != ws->x;
165 view->border_left = view->swayc->x != ws->x; 184 int right_x = view->swayc->x + view->swayc->width;
166 int right_x = view->swayc->x + view->swayc->width; 185 view->border_right = right_x != ws->x + ws->width;
167 view->border_right = right_x != ws->x + ws->width; 186 }
168 } 187 if (config->hide_edge_borders == E_BOTH
169 if (config->hide_edge_borders == E_BOTH 188 || config->hide_edge_borders == E_HORIZONTAL
170 || config->hide_edge_borders == E_HORIZONTAL 189 || (config->hide_edge_borders == E_SMART && !other_views)) {
171 || (config->hide_edge_borders == E_SMART && !other_views)) { 190 view->border_top = view->swayc->y != ws->y;
172 view->border_top = view->swayc->y != ws->y; 191 int bottom_y = view->swayc->y + view->swayc->height;
173 int bottom_y = view->swayc->y + view->swayc->height; 192 view->border_bottom = bottom_y != ws->y + ws->height;
174 view->border_bottom = bottom_y != ws->y + ws->height;
175 }
176 } 193 }
177 194
178 double x, y, width, height; 195 double x, y, width, height;
@@ -184,11 +201,11 @@ void view_autoconfigure(struct sway_view *view) {
184 // disable any top border because we'll always have the title bar. 201 // disable any top border because we'll always have the title bar.
185 if (view->swayc->parent->layout == L_TABBED) { 202 if (view->swayc->parent->layout == L_TABBED) {
186 y_offset = container_titlebar_height(); 203 y_offset = container_titlebar_height();
187 view->border_top = 0; 204 view->border_top = false;
188 } else if (view->swayc->parent->layout == L_STACKED) { 205 } else if (view->swayc->parent->layout == L_STACKED) {
189 y_offset = container_titlebar_height() 206 y_offset = container_titlebar_height()
190 * view->swayc->parent->children->length; 207 * view->swayc->parent->children->length;
191 view->border_top = 0; 208 view->border_top = false;
192 } 209 }
193 210
194 switch (view->border) { 211 switch (view->border) {
@@ -257,6 +274,12 @@ void view_set_fullscreen_raw(struct sway_view *view, bool fullscreen) {
257 view_set_fullscreen(workspace->sway_workspace->fullscreen, false); 274 view_set_fullscreen(workspace->sway_workspace->fullscreen, false);
258 } 275 }
259 workspace->sway_workspace->fullscreen = view; 276 workspace->sway_workspace->fullscreen = view;
277 view->saved_x = view->x;
278 view->saved_y = view->y;
279 view->saved_width = view->width;
280 view->saved_height = view->height;
281 view->swayc->saved_x = view->swayc->x;
282 view->swayc->saved_y = view->swayc->y;
260 view->swayc->saved_width = view->swayc->width; 283 view->swayc->saved_width = view->swayc->width;
261 view->swayc->saved_height = view->swayc->height; 284 view->swayc->saved_height = view->swayc->height;
262 285
@@ -277,8 +300,14 @@ void view_set_fullscreen_raw(struct sway_view *view, bool fullscreen) {
277 } 300 }
278 } else { 301 } else {
279 workspace->sway_workspace->fullscreen = NULL; 302 workspace->sway_workspace->fullscreen = NULL;
280 view->swayc->width = view->swayc->saved_width; 303 if (container_is_floating(view->swayc)) {
281 view->swayc->height = view->swayc->saved_height; 304 view_configure(view, view->saved_x, view->saved_y,
305 view->saved_width, view->saved_height);
306 } else {
307 view->swayc->width = view->swayc->saved_width;
308 view->swayc->height = view->swayc->saved_height;
309 view_autoconfigure(view);
310 }
282 } 311 }
283} 312}
284 313
@@ -452,6 +481,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
452 // TODO: CT_ASSIGN_OUTPUT 481 // TODO: CT_ASSIGN_OUTPUT
453 } 482 }
454 } 483 }
484 // If we're about to launch the view into the floating container, then
485 // launch it as a tiled view in the root of the workspace instead.
486 if (container_is_floating(focus)) {
487 focus = focus->parent->parent;
488 }
455 free(criterias); 489 free(criterias);
456 cont = container_view_create(focus, view); 490 cont = container_view_create(focus, view);
457 491
@@ -468,7 +502,12 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
468 wl_signal_add(&view->swayc->events.reparent, &view->container_reparent); 502 wl_signal_add(&view->swayc->events.reparent, &view->container_reparent);
469 view->container_reparent.notify = view_handle_container_reparent; 503 view->container_reparent.notify = view_handle_container_reparent;
470 504
471 arrange_children_of(cont->parent); 505 if (view->impl->wants_floating && view->impl->wants_floating(view)) {
506 container_set_floating(view->swayc, true);
507 } else {
508 arrange_children_of(cont->parent);
509 }
510
472 input_manager_set_focus(input_manager, cont); 511 input_manager_set_focus(input_manager, cont);
473 if (workspace) { 512 if (workspace) {
474 workspace_switch(workspace); 513 workspace_switch(workspace);
@@ -516,16 +555,16 @@ void view_unmap(struct sway_view *view) {
516 } 555 }
517} 556}
518 557
519void view_update_position(struct sway_view *view, double ox, double oy) { 558void view_update_position(struct sway_view *view, double lx, double ly) {
520 if (view->swayc->x == ox && view->swayc->y == oy) { 559 if (view->x == lx && view->y == ly) {
521 return; 560 return;
522 } 561 }
523
524 // TODO: Only allow this if the view is floating (this function will only be
525 // called in response to wayland clients wanting to reposition themselves).
526 container_damage_whole(view->swayc); 562 container_damage_whole(view->swayc);
527 view->swayc->x = ox; 563 view->x = lx;
528 view->swayc->y = oy; 564 view->y = ly;
565 if (container_is_floating(view->swayc)) {
566 container_set_geometry_from_floating_view(view->swayc);
567 }
529 container_damage_whole(view->swayc); 568 container_damage_whole(view->swayc);
530} 569}
531 570
@@ -533,15 +572,15 @@ void view_update_size(struct sway_view *view, int width, int height) {
533 if (view->width == width && view->height == height) { 572 if (view->width == width && view->height == height) {
534 return; 573 return;
535 } 574 }
536
537 container_damage_whole(view->swayc); 575 container_damage_whole(view->swayc);
538 // Should we update the swayc width/height here too?
539 view->width = width; 576 view->width = width;
540 view->height = height; 577 view->height = height;
578 if (container_is_floating(view->swayc)) {
579 container_set_geometry_from_floating_view(view->swayc);
580 }
541 container_damage_whole(view->swayc); 581 container_damage_whole(view->swayc);
542} 582}
543 583
544
545static void view_subsurface_create(struct sway_view *view, 584static void view_subsurface_create(struct sway_view *view,
546 struct wlr_subsurface *subsurface) { 585 struct wlr_subsurface *subsurface) {
547 struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child)); 586 struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child));
@@ -888,6 +927,19 @@ bool view_is_visible(struct sway_view *view) {
888 if (!view->swayc) { 927 if (!view->swayc) {
889 return false; 928 return false;
890 } 929 }
930 struct sway_container *workspace =
931 container_parent(view->swayc, C_WORKSPACE);
932 // Determine if view is nested inside a floating container which is sticky.
933 // A simple floating view will have this ancestry:
934 // C_VIEW -> floating -> workspace
935 // A more complex ancestry could be:
936 // C_VIEW -> C_CONTAINER (tabbed) -> floating -> workspace
937 struct sway_container *floater = view->swayc;
938 while (floater->parent->type != C_WORKSPACE
939 && floater->parent->parent->type != C_WORKSPACE) {
940 floater = floater->parent;
941 }
942 bool is_sticky = container_is_floating(floater) && floater->is_sticky;
891 // Check view isn't in a tabbed or stacked container on an inactive tab 943 // Check view isn't in a tabbed or stacked container on an inactive tab
892 struct sway_seat *seat = input_manager_current_seat(input_manager); 944 struct sway_seat *seat = input_manager_current_seat(input_manager);
893 struct sway_container *container = view->swayc; 945 struct sway_container *container = view->swayc;
@@ -901,10 +953,12 @@ bool view_is_visible(struct sway_view *view) {
901 container = container->parent; 953 container = container->parent;
902 } 954 }
903 // Check view isn't hidden by another fullscreen view 955 // Check view isn't hidden by another fullscreen view
904 struct sway_container *workspace = container;
905 if (workspace->sway_workspace->fullscreen && !view->is_fullscreen) { 956 if (workspace->sway_workspace->fullscreen && !view->is_fullscreen) {
906 return false; 957 return false;
907 } 958 }
908 // Check the workspace is visible 959 // Check the workspace is visible
909 return workspace_is_visible(workspace); 960 if (!is_sticky) {
961 return workspace_is_visible(workspace);
962 }
963 return true;
910} 964}
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index f34baa9e..f78ae9a5 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -12,6 +12,7 @@
12#include "sway/tree/arrange.h" 12#include "sway/tree/arrange.h"
13#include "sway/tree/container.h" 13#include "sway/tree/container.h"
14#include "sway/tree/workspace.h" 14#include "sway/tree/workspace.h"
15#include "list.h"
15#include "log.h" 16#include "log.h"
16#include "util.h" 17#include "util.h"
17 18
@@ -64,6 +65,9 @@ struct sway_container *workspace_create(struct sway_container *output,
64 return NULL; 65 return NULL;
65 } 66 }
66 swayws->swayc = workspace; 67 swayws->swayc = workspace;
68 swayws->floating = container_create(C_CONTAINER);
69 swayws->floating->parent = swayws->swayc;
70 swayws->floating->layout = L_FLOATING;
67 workspace->sway_workspace = swayws; 71 workspace->sway_workspace = swayws;
68 72
69 container_add_child(output, workspace); 73 container_add_child(output, workspace);
@@ -383,7 +387,21 @@ bool workspace_switch(struct sway_container *workspace) {
383 strcpy(prev_workspace_name, active_ws->name); 387 strcpy(prev_workspace_name, active_ws->name);
384 } 388 }
385 389
386 // TODO: Deal with sticky containers 390 // Move sticky containers to new workspace
391 struct sway_container *next_output = workspace->parent;
392 struct sway_container *next_output_prev_ws =
393 seat_get_active_child(seat, next_output);
394 struct sway_container *floating =
395 next_output_prev_ws->sway_workspace->floating;
396 bool has_sticky = false;
397 for (int i = 0; i < floating->children->length; ++i) {
398 struct sway_container *floater = floating->children->items[i];
399 if (floater->is_sticky) {
400 has_sticky = true;
401 container_remove_child(floater);
402 container_add_child(workspace->sway_workspace->floating, floater);
403 }
404 }
387 405
388 wlr_log(L_DEBUG, "Switching to workspace %p:%s", 406 wlr_log(L_DEBUG, "Switching to workspace %p:%s",
389 workspace, workspace->name); 407 workspace, workspace->name);
@@ -391,6 +409,16 @@ bool workspace_switch(struct sway_container *workspace) {
391 if (next == NULL) { 409 if (next == NULL) {
392 next = workspace; 410 next = workspace;
393 } 411 }
412 if (has_sticky) {
413 // If there's a sticky container, we might be setting focus to the same
414 // container that's already focused, so seat_set_focus is effectively a
415 // no op. We therefore need to send the IPC event and clean up the old
416 // workspace here.
417 ipc_event_workspace(active_ws, workspace, "focus");
418 if (!workspace_is_visible(active_ws) && workspace_is_empty(active_ws)) {
419 container_destroy(active_ws);
420 }
421 }
394 seat_set_focus(seat, next); 422 seat_set_focus(seat, next);
395 struct sway_container *output = container_parent(workspace, C_OUTPUT); 423 struct sway_container *output = container_parent(workspace, C_OUTPUT);
396 arrange_output(output); 424 arrange_output(output);
@@ -406,3 +434,21 @@ bool workspace_is_visible(struct sway_container *ws) {
406 } 434 }
407 return focus == ws; 435 return focus == ws;
408} 436}
437
438bool workspace_is_empty(struct sway_container *ws) {
439 if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) {
440 return false;
441 }
442 if (ws->children->length) {
443 return false;
444 }
445 // Sticky views are not considered to be part of this workspace
446 struct sway_container *floating = ws->sway_workspace->floating;
447 for (int i = 0; i < floating->children->length; ++i) {
448 struct sway_container *floater = floating->children->items[i];
449 if (!floater->is_sticky) {
450 return false;
451 }
452 }
453 return true;
454}