aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-09-24 20:54:57 +1000
committerLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-09-27 22:51:37 +1000
commit7b138e5ef0f679c9bb0078019d7c9c63fef36273 (patch)
tree2cdbeb394889065e0606a1fcbe38c1e99e25d260
parentMerge pull request #2717 from ianyfan/tablet-config (diff)
downloadsway-7b138e5ef0f679c9bb0078019d7c9c63fef36273.tar.gz
sway-7b138e5ef0f679c9bb0078019d7c9c63fef36273.tar.zst
sway-7b138e5ef0f679c9bb0078019d7c9c63fef36273.zip
Add CSD to border modes
This replaces view.using_csd with a new border mode: B_CSD. This also removes sway_xdg_shell{_v6}_view.deco_mode and view->has_client_side_decorations as we can now get these from the border. You can use `border toggle` to cycle through the modes including CSD, or use `border csd` to set it directly. The client must support the xdg-decoration protocol, and the only client I know of that does is the example in wlroots. If the client switches from SSD to CSD without us expecting it (via the server-decoration protocol), we stash the previous border type into view.saved_border so we can restore it if the client returns to SSD. I haven't found a way to test this though.
-rw-r--r--include/sway/server.h5
-rw-r--r--include/sway/tree/container.h2
-rw-r--r--include/sway/tree/view.h21
-rw-r--r--include/sway/xdg_decoration.h19
-rw-r--r--sway/commands/border.c21
-rw-r--r--sway/decoration.c22
-rw-r--r--sway/desktop/render.c108
-rw-r--r--sway/desktop/transaction.c1
-rw-r--r--sway/desktop/xdg_shell.c22
-rw-r--r--sway/desktop/xdg_shell_v6.c22
-rw-r--r--sway/desktop/xwayland.c20
-rw-r--r--sway/input/cursor.c3
-rw-r--r--sway/ipc-json.c2
-rw-r--r--sway/meson.build1
-rw-r--r--sway/server.c9
-rw-r--r--sway/tree/container.c2
-rw-r--r--sway/tree/view.c37
-rw-r--r--sway/xdg_decoration.c73
18 files changed, 250 insertions, 140 deletions
diff --git a/include/sway/server.h b/include/sway/server.h
index 5dabb61f..3e1cbb33 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -54,6 +54,10 @@ struct sway_server {
54 struct wl_listener server_decoration; 54 struct wl_listener server_decoration;
55 struct wl_list decorations; // sway_server_decoration::link 55 struct wl_list decorations; // sway_server_decoration::link
56 56
57 struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager;
58 struct wl_listener xdg_decoration;
59 struct wl_list xdg_decorations; // sway_xdg_decoration::link
60
57 size_t txn_timeout_ms; 61 size_t txn_timeout_ms;
58 list_t *transactions; 62 list_t *transactions;
59 list_t *dirty_nodes; 63 list_t *dirty_nodes;
@@ -78,5 +82,6 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data);
78void handle_xwayland_surface(struct wl_listener *listener, void *data); 82void handle_xwayland_surface(struct wl_listener *listener, void *data);
79#endif 83#endif
80void handle_server_decoration(struct wl_listener *listener, void *data); 84void handle_server_decoration(struct wl_listener *listener, void *data);
85void handle_xdg_decoration(struct wl_listener *listener, void *data);
81 86
82#endif 87#endif
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 5e281a2f..6019602c 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -28,6 +28,7 @@ enum sway_container_border {
28 B_NONE, 28 B_NONE,
29 B_PIXEL, 29 B_PIXEL,
30 B_NORMAL, 30 B_NORMAL,
31 B_CSD,
31}; 32};
32 33
33struct sway_root; 34struct sway_root;
@@ -63,7 +64,6 @@ struct sway_container_state {
63 bool border_bottom; 64 bool border_bottom;
64 bool border_left; 65 bool border_left;
65 bool border_right; 66 bool border_right;
66 bool using_csd;
67}; 67};
68 68
69struct sway_container { 69struct sway_container {
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index d10251dd..49df7c88 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -11,6 +11,7 @@
11#include "sway/input/seat.h" 11#include "sway/input/seat.h"
12 12
13struct sway_container; 13struct sway_container;
14struct sway_xdg_decoration;
14 15
15enum sway_view_type { 16enum sway_view_type {
16 SWAY_VIEW_XDG_SHELL_V6, 17 SWAY_VIEW_XDG_SHELL_V6,
@@ -44,7 +45,6 @@ struct sway_view_impl {
44 void (*set_tiled)(struct sway_view *view, bool tiled); 45 void (*set_tiled)(struct sway_view *view, bool tiled);
45 void (*set_fullscreen)(struct sway_view *view, bool fullscreen); 46 void (*set_fullscreen)(struct sway_view *view, bool fullscreen);
46 bool (*wants_floating)(struct sway_view *view); 47 bool (*wants_floating)(struct sway_view *view);
47 bool (*has_client_side_decorations)(struct sway_view *view);
48 void (*for_each_surface)(struct sway_view *view, 48 void (*for_each_surface)(struct sway_view *view,
49 wlr_surface_iterator_func_t iterator, void *user_data); 49 wlr_surface_iterator_func_t iterator, void *user_data);
50 void (*for_each_popup)(struct sway_view *view, 50 void (*for_each_popup)(struct sway_view *view,
@@ -60,6 +60,7 @@ struct sway_view {
60 60
61 struct sway_container *container; // NULL if unmapped and transactions finished 61 struct sway_container *container; // NULL if unmapped and transactions finished
62 struct wlr_surface *surface; // NULL for unmapped views 62 struct wlr_surface *surface; // NULL for unmapped views
63 struct sway_xdg_decoration *xdg_decoration;
63 64
64 pid_t pid; 65 pid_t pid;
65 66
@@ -76,12 +77,12 @@ struct sway_view {
76 77
77 char *title_format; 78 char *title_format;
78 enum sway_container_border border; 79 enum sway_container_border border;
80 enum sway_container_border saved_border;
79 int border_thickness; 81 int border_thickness;
80 bool border_top; 82 bool border_top;
81 bool border_bottom; 83 bool border_bottom;
82 bool border_left; 84 bool border_left;
83 bool border_right; 85 bool border_right;
84 bool using_csd;
85 86
86 struct timespec urgent; 87 struct timespec urgent;
87 bool allow_request_urgent; 88 bool allow_request_urgent;
@@ -127,8 +128,6 @@ struct sway_view {
127struct sway_xdg_shell_v6_view { 128struct sway_xdg_shell_v6_view {
128 struct sway_view view; 129 struct sway_view view;
129 130
130 enum wlr_server_decoration_manager_mode deco_mode;
131
132 struct wl_listener commit; 131 struct wl_listener commit;
133 struct wl_listener request_move; 132 struct wl_listener request_move;
134 struct wl_listener request_resize; 133 struct wl_listener request_resize;
@@ -145,8 +144,6 @@ struct sway_xdg_shell_v6_view {
145struct sway_xdg_shell_view { 144struct sway_xdg_shell_view {
146 struct sway_view view; 145 struct sway_view view;
147 146
148 enum wlr_server_decoration_manager_mode deco_mode;
149
150 struct wl_listener commit; 147 struct wl_listener commit;
151 struct wl_listener request_move; 148 struct wl_listener request_move;
152 struct wl_listener request_resize; 149 struct wl_listener request_resize;
@@ -175,6 +172,7 @@ struct sway_xwayland_view {
175 struct wl_listener set_role; 172 struct wl_listener set_role;
176 struct wl_listener set_window_type; 173 struct wl_listener set_window_type;
177 struct wl_listener set_hints; 174 struct wl_listener set_hints;
175 struct wl_listener set_decorations;
178 struct wl_listener map; 176 struct wl_listener map;
179 struct wl_listener unmap; 177 struct wl_listener unmap;
180 struct wl_listener destroy; 178 struct wl_listener destroy;
@@ -268,6 +266,17 @@ void view_set_activated(struct sway_view *view, bool activated);
268 */ 266 */
269void view_request_activate(struct sway_view *view); 267void view_request_activate(struct sway_view *view);
270 268
269/**
270 * If possible, instructs the client to change their decoration mode.
271 */
272void view_set_csd_from_server(struct sway_view *view, bool enabled);
273
274/**
275 * Updates the view's border setting when the client unexpectedly changes their
276 * decoration mode.
277 */
278void view_set_csd_from_client(struct sway_view *view, bool enabled);
279
271void view_set_tiled(struct sway_view *view, bool tiled); 280void view_set_tiled(struct sway_view *view, bool tiled);
272 281
273void view_close(struct sway_view *view); 282void view_close(struct sway_view *view);
diff --git a/include/sway/xdg_decoration.h b/include/sway/xdg_decoration.h
new file mode 100644
index 00000000..46fb8d34
--- /dev/null
+++ b/include/sway/xdg_decoration.h
@@ -0,0 +1,19 @@
1#ifndef _SWAY_XDG_DECORATION_H
2#define _SWAY_XDG_DECORATION_H
3
4#include <wlr/types/wlr_xdg_decoration_v1.h>
5
6struct sway_xdg_decoration {
7 struct wlr_xdg_toplevel_decoration_v1 *wlr_xdg_decoration;
8 struct wl_list link;
9
10 struct sway_view *view;
11
12 struct wl_listener destroy;
13 struct wl_listener surface_commit;
14};
15
16struct sway_xdg_decoration *xdg_decoration_from_surface(
17 struct wlr_surface *surface);
18
19#endif
diff --git a/sway/commands/border.c b/sway/commands/border.c
index 95498b2f..673fea08 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -7,6 +7,17 @@
7#include "sway/tree/container.h" 7#include "sway/tree/container.h"
8#include "sway/tree/view.h" 8#include "sway/tree/view.h"
9 9
10static void set_border(struct sway_view *view,
11 enum sway_container_border new_border) {
12 if (view->border == B_CSD && new_border != B_CSD) {
13 view_set_csd_from_server(view, false);
14 } else if (view->border != B_CSD && new_border == B_CSD) {
15 view_set_csd_from_server(view, true);
16 }
17 view->saved_border = view->border;
18 view->border = new_border;
19}
20
10struct cmd_results *cmd_border(int argc, char **argv) { 21struct cmd_results *cmd_border(int argc, char **argv) {
11 struct cmd_results *error = NULL; 22 struct cmd_results *error = NULL;
12 if ((error = checkarg(argc, "border", EXPECTED_AT_LEAST, 1))) { 23 if ((error = checkarg(argc, "border", EXPECTED_AT_LEAST, 1))) {
@@ -21,13 +32,15 @@ struct cmd_results *cmd_border(int argc, char **argv) {
21 struct sway_view *view = container->view; 32 struct sway_view *view = container->view;
22 33
23 if (strcmp(argv[0], "none") == 0) { 34 if (strcmp(argv[0], "none") == 0) {
24 view->border = B_NONE; 35 set_border(view, B_NONE);
25 } else if (strcmp(argv[0], "normal") == 0) { 36 } else if (strcmp(argv[0], "normal") == 0) {
26 view->border = B_NORMAL; 37 set_border(view, B_NORMAL);
27 } else if (strcmp(argv[0], "pixel") == 0) { 38 } else if (strcmp(argv[0], "pixel") == 0) {
28 view->border = B_PIXEL; 39 set_border(view, B_PIXEL);
40 } else if (strcmp(argv[0], "csd") == 0) {
41 set_border(view, B_CSD);
29 } else if (strcmp(argv[0], "toggle") == 0) { 42 } else if (strcmp(argv[0], "toggle") == 0) {
30 view->border = (view->border + 1) % 3; 43 set_border(view, (view->border + 1) % 4);
31 } else { 44 } else {
32 return cmd_results_new(CMD_INVALID, "border", 45 return cmd_results_new(CMD_INVALID, "border",
33 "Expected 'border <none|normal|pixel|toggle>' " 46 "Expected 'border <none|normal|pixel|toggle>' "
diff --git a/sway/decoration.c b/sway/decoration.c
index 0e3e67ac..fea6ed4c 100644
--- a/sway/decoration.c
+++ b/sway/decoration.c
@@ -1,6 +1,8 @@
1#include <stdlib.h> 1#include <stdlib.h>
2#include "sway/decoration.h" 2#include "sway/decoration.h"
3#include "sway/desktop/transaction.h"
3#include "sway/server.h" 4#include "sway/server.h"
5#include "sway/tree/arrange.h"
4#include "sway/tree/view.h" 6#include "sway/tree/view.h"
5#include "log.h" 7#include "log.h"
6 8
@@ -24,20 +26,12 @@ static void server_decoration_handle_mode(struct wl_listener *listener,
24 return; 26 return;
25 } 27 }
26 28
27 switch (view->type) { 29 bool csd = deco->wlr_server_decoration->mode ==
28 case SWAY_VIEW_XDG_SHELL_V6:; 30 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
29 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 31 view_set_csd_from_client(view, csd);
30 (struct sway_xdg_shell_v6_view *)view; 32
31 xdg_shell_v6_view->deco_mode = deco->wlr_server_decoration->mode; 33 arrange_container(view->container);
32 break; 34 transaction_commit_dirty();
33 case SWAY_VIEW_XDG_SHELL:;
34 struct sway_xdg_shell_view *xdg_shell_view =
35 (struct sway_xdg_shell_view *)view;
36 xdg_shell_view->deco_mode = deco->wlr_server_decoration->mode;
37 break;
38 default:
39 break;
40 }
41} 35}
42 36
43void handle_server_decoration(struct wl_listener *listener, void *data) { 37void handle_server_decoration(struct wl_listener *listener, void *data) {
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index af4e2905..c8b08a58 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -272,7 +272,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
272 render_view_toplevels(view, output, damage, view->container->alpha); 272 render_view_toplevels(view, output, damage, view->container->alpha);
273 } 273 }
274 274
275 if (view->container->current.using_csd) { 275 if (con->current.border == B_NONE || con->current.border == B_CSD) {
276 return; 276 return;
277 } 277 }
278 278
@@ -281,51 +281,49 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
281 float color[4]; 281 float color[4];
282 struct sway_container_state *state = &con->current; 282 struct sway_container_state *state = &con->current;
283 283
284 if (state->border != B_NONE) { 284 if (state->border_left) {
285 if (state->border_left) { 285 memcpy(&color, colors->child_border, sizeof(float) * 4);
286 memcpy(&color, colors->child_border, sizeof(float) * 4); 286 premultiply_alpha(color, con->alpha);
287 premultiply_alpha(color, con->alpha); 287 box.x = state->con_x;
288 box.x = state->con_x; 288 box.y = state->view_y;
289 box.y = state->view_y; 289 box.width = state->border_thickness;
290 box.width = state->border_thickness; 290 box.height = state->view_height;
291 box.height = state->view_height; 291 scale_box(&box, output_scale);
292 scale_box(&box, output_scale); 292 render_rect(output->wlr_output, damage, &box, color);
293 render_rect(output->wlr_output, damage, &box, color); 293 }
294 }
295 294
296 list_t *siblings = container_get_current_siblings(con); 295 list_t *siblings = container_get_current_siblings(con);
297 enum sway_container_layout layout = 296 enum sway_container_layout layout =
298 container_current_parent_layout(con); 297 container_current_parent_layout(con);
299 298
300 if (state->border_right) { 299 if (state->border_right) {
301 if (siblings->length == 1 && layout == L_HORIZ) { 300 if (siblings->length == 1 && layout == L_HORIZ) {
302 memcpy(&color, colors->indicator, sizeof(float) * 4); 301 memcpy(&color, colors->indicator, sizeof(float) * 4);
303 } else { 302 } else {
304 memcpy(&color, colors->child_border, sizeof(float) * 4); 303 memcpy(&color, colors->child_border, sizeof(float) * 4);
305 }
306 premultiply_alpha(color, con->alpha);
307 box.x = state->view_x + state->view_width;
308 box.y = state->view_y;
309 box.width = state->border_thickness;
310 box.height = state->view_height;
311 scale_box(&box, output_scale);
312 render_rect(output->wlr_output, damage, &box, color);
313 } 304 }
305 premultiply_alpha(color, con->alpha);
306 box.x = state->view_x + state->view_width;
307 box.y = state->view_y;
308 box.width = state->border_thickness;
309 box.height = state->view_height;
310 scale_box(&box, output_scale);
311 render_rect(output->wlr_output, damage, &box, color);
312 }
314 313
315 if (state->border_bottom) { 314 if (state->border_bottom) {
316 if (siblings->length == 1 && layout == L_VERT) { 315 if (siblings->length == 1 && layout == L_VERT) {
317 memcpy(&color, colors->indicator, sizeof(float) * 4); 316 memcpy(&color, colors->indicator, sizeof(float) * 4);
318 } else { 317 } else {
319 memcpy(&color, colors->child_border, sizeof(float) * 4); 318 memcpy(&color, colors->child_border, sizeof(float) * 4);
320 }
321 premultiply_alpha(color, con->alpha);
322 box.x = state->con_x;
323 box.y = state->view_y + state->view_height;
324 box.width = state->con_width;
325 box.height = state->border_thickness;
326 scale_box(&box, output_scale);
327 render_rect(output->wlr_output, damage, &box, color);
328 } 319 }
320 premultiply_alpha(color, con->alpha);
321 box.x = state->con_x;
322 box.y = state->view_y + state->view_height;
323 box.width = state->con_width;
324 box.height = state->border_thickness;
325 scale_box(&box, output_scale);
326 render_rect(output->wlr_output, damage, &box, color);
329 } 327 }
330} 328}
331 329
@@ -645,14 +643,12 @@ static void render_containers_linear(struct sway_output *output,
645 marks_texture = view->marks_unfocused; 643 marks_texture = view->marks_unfocused;
646 } 644 }
647 645
648 if (!view->container->current.using_csd) { 646 if (state->border == B_NORMAL) {
649 if (state->border == B_NORMAL) { 647 render_titlebar(output, damage, child, state->con_x,
650 render_titlebar(output, damage, child, state->con_x, 648 state->con_y, state->con_width, colors,
651 state->con_y, state->con_width, colors, 649 title_texture, marks_texture);
652 title_texture, marks_texture); 650 } else if (state->border == B_PIXEL) {
653 } else { 651 render_top_border(output, damage, child, colors);
654 render_top_border(output, damage, child, colors);
655 }
656 } 652 }
657 render_view(output, damage, child, colors); 653 render_view(output, damage, child, colors);
658 } else { 654 } else {
@@ -859,14 +855,12 @@ static void render_floating_container(struct sway_output *soutput,
859 marks_texture = view->marks_unfocused; 855 marks_texture = view->marks_unfocused;
860 } 856 }
861 857
862 if (!view->container->current.using_csd) { 858 if (con->current.border == B_NORMAL) {
863 if (con->current.border == B_NORMAL) { 859 render_titlebar(soutput, damage, con, con->current.con_x,
864 render_titlebar(soutput, damage, con, con->current.con_x, 860 con->current.con_y, con->current.con_width, colors,
865 con->current.con_y, con->current.con_width, colors, 861 title_texture, marks_texture);
866 title_texture, marks_texture); 862 } else if (con->current.border == B_PIXEL) {
867 } else if (con->current.border != B_NONE) { 863 render_top_border(soutput, damage, con, colors);
868 render_top_border(soutput, damage, con, colors);
869 }
870 } 864 }
871 render_view(soutput, damage, con, colors); 865 render_view(soutput, damage, con, colors);
872 } else { 866 } else {
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index 797f6b4c..4624d824 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -167,7 +167,6 @@ static void copy_container_state(struct sway_container *container,
167 state->border_left = view->border_left; 167 state->border_left = view->border_left;
168 state->border_right = view->border_right; 168 state->border_right = view->border_right;
169 state->border_bottom = view->border_bottom; 169 state->border_bottom = view->border_bottom;
170 state->using_csd = view->using_csd;
171 } else { 170 } else {
172 state->children = create_list(); 171 state->children = create_list();
173 list_cat(state->children, container->children); 172 list_cat(state->children, container->children);
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 6d1ccdd7..d563edae 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -175,15 +175,6 @@ static bool wants_floating(struct sway_view *view) {
175 || toplevel->parent; 175 || toplevel->parent;
176} 176}
177 177
178static bool has_client_side_decorations(struct sway_view *view) {
179 struct sway_xdg_shell_view *xdg_shell_view =
180 xdg_shell_view_from_view(view);
181 if (xdg_shell_view == NULL) {
182 return true;
183 }
184 return xdg_shell_view->deco_mode != WLR_SERVER_DECORATION_MANAGER_MODE_SERVER;
185}
186
187static void for_each_surface(struct sway_view *view, 178static void for_each_surface(struct sway_view *view,
188 wlr_surface_iterator_func_t iterator, void *user_data) { 179 wlr_surface_iterator_func_t iterator, void *user_data) {
189 if (xdg_shell_view_from_view(view) == NULL) { 180 if (xdg_shell_view_from_view(view) == NULL) {
@@ -240,7 +231,6 @@ static const struct sway_view_impl view_impl = {
240 .set_tiled = set_tiled, 231 .set_tiled = set_tiled,
241 .set_fullscreen = set_fullscreen, 232 .set_fullscreen = set_fullscreen,
242 .wants_floating = wants_floating, 233 .wants_floating = wants_floating,
243 .has_client_side_decorations = has_client_side_decorations,
244 .for_each_surface = for_each_surface, 234 .for_each_surface = for_each_surface,
245 .for_each_popup = for_each_popup, 235 .for_each_popup = for_each_popup,
246 .close = _close, 236 .close = _close,
@@ -385,15 +375,13 @@ static void handle_map(struct wl_listener *listener, void *data) {
385 view->natural_height = view->wlr_xdg_surface->surface->current.height; 375 view->natural_height = view->wlr_xdg_surface->surface->current.height;
386 } 376 }
387 377
378 view_map(view, view->wlr_xdg_surface->surface);
379
388 struct sway_server_decoration *deco = 380 struct sway_server_decoration *deco =
389 decoration_from_surface(xdg_surface->surface); 381 decoration_from_surface(xdg_surface->surface);
390 if (deco != NULL) { 382 bool csd = !deco || deco->wlr_server_decoration->mode ==
391 xdg_shell_view->deco_mode = deco->wlr_server_decoration->mode; 383 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
392 } else { 384 view_set_csd_from_client(view, csd);
393 xdg_shell_view->deco_mode = WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
394 }
395
396 view_map(view, view->wlr_xdg_surface->surface);
397 385
398 if (xdg_surface->toplevel->client_pending.fullscreen) { 386 if (xdg_surface->toplevel->client_pending.fullscreen) {
399 container_set_fullscreen(view->container, true); 387 container_set_fullscreen(view->container, true);
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 95ca396c..8c8085f7 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -171,15 +171,6 @@ static bool wants_floating(struct sway_view *view) {
171 || toplevel->parent; 171 || toplevel->parent;
172} 172}
173 173
174static bool has_client_side_decorations(struct sway_view *view) {
175 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
176 xdg_shell_v6_view_from_view(view);
177 if (xdg_shell_v6_view == NULL) {
178 return true;
179 }
180 return xdg_shell_v6_view->deco_mode != WLR_SERVER_DECORATION_MANAGER_MODE_SERVER;
181}
182
183static void for_each_surface(struct sway_view *view, 174static void for_each_surface(struct sway_view *view,
184 wlr_surface_iterator_func_t iterator, void *user_data) { 175 wlr_surface_iterator_func_t iterator, void *user_data) {
185 if (xdg_shell_v6_view_from_view(view) == NULL) { 176 if (xdg_shell_v6_view_from_view(view) == NULL) {
@@ -237,7 +228,6 @@ static const struct sway_view_impl view_impl = {
237 .set_tiled = set_tiled, 228 .set_tiled = set_tiled,
238 .set_fullscreen = set_fullscreen, 229 .set_fullscreen = set_fullscreen,
239 .wants_floating = wants_floating, 230 .wants_floating = wants_floating,
240 .has_client_side_decorations = has_client_side_decorations,
241 .for_each_surface = for_each_surface, 231 .for_each_surface = for_each_surface,
242 .for_each_popup = for_each_popup, 232 .for_each_popup = for_each_popup,
243 .close = _close, 233 .close = _close,
@@ -382,15 +372,13 @@ static void handle_map(struct wl_listener *listener, void *data) {
382 view->natural_height = view->wlr_xdg_surface_v6->surface->current.height; 372 view->natural_height = view->wlr_xdg_surface_v6->surface->current.height;
383 } 373 }
384 374
375 view_map(view, view->wlr_xdg_surface_v6->surface);
376
385 struct sway_server_decoration *deco = 377 struct sway_server_decoration *deco =
386 decoration_from_surface(xdg_surface->surface); 378 decoration_from_surface(xdg_surface->surface);
387 if (deco != NULL) { 379 bool csd = !deco || deco->wlr_server_decoration->mode ==
388 xdg_shell_v6_view->deco_mode = deco->wlr_server_decoration->mode; 380 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
389 } else { 381 view_set_csd_from_client(view, csd);
390 xdg_shell_v6_view->deco_mode = WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
391 }
392
393 view_map(view, view->wlr_xdg_surface_v6->surface);
394 382
395 if (xdg_surface->toplevel->client_pending.fullscreen) { 383 if (xdg_surface->toplevel->client_pending.fullscreen) {
396 container_set_fullscreen(view->container, true); 384 container_set_fullscreen(view->container, true);
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index a12ac854..f1205518 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -243,12 +243,14 @@ static bool wants_floating(struct sway_view *view) {
243 return false; 243 return false;
244} 244}
245 245
246static bool has_client_side_decorations(struct sway_view *view) { 246static void handle_set_decorations(struct wl_listener *listener, void *data) {
247 if (xwayland_view_from_view(view) == NULL) { 247 struct sway_xwayland_view *xwayland_view =
248 return false; 248 wl_container_of(listener, xwayland_view, set_decorations);
249 } 249 struct sway_view *view = &xwayland_view->view;
250 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; 250 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
251 return surface->decorations != WLR_XWAYLAND_SURFACE_DECORATIONS_ALL; 251
252 bool csd = xsurface->decorations != WLR_XWAYLAND_SURFACE_DECORATIONS_ALL;
253 view_set_csd_from_client(view, csd);
252} 254}
253 255
254static void _close(struct sway_view *view) { 256static void _close(struct sway_view *view) {
@@ -274,7 +276,6 @@ static const struct sway_view_impl view_impl = {
274 .set_tiled = set_tiled, 276 .set_tiled = set_tiled,
275 .set_fullscreen = set_fullscreen, 277 .set_fullscreen = set_fullscreen,
276 .wants_floating = wants_floating, 278 .wants_floating = wants_floating,
277 .has_client_side_decorations = has_client_side_decorations,
278 .close = _close, 279 .close = _close,
279 .destroy = destroy, 280 .destroy = destroy,
280}; 281};
@@ -343,6 +344,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
343 wl_list_remove(&xwayland_view->set_role.link); 344 wl_list_remove(&xwayland_view->set_role.link);
344 wl_list_remove(&xwayland_view->set_window_type.link); 345 wl_list_remove(&xwayland_view->set_window_type.link);
345 wl_list_remove(&xwayland_view->set_hints.link); 346 wl_list_remove(&xwayland_view->set_hints.link);
347 wl_list_remove(&xwayland_view->set_decorations.link);
346 wl_list_remove(&xwayland_view->map.link); 348 wl_list_remove(&xwayland_view->map.link);
347 wl_list_remove(&xwayland_view->unmap.link); 349 wl_list_remove(&xwayland_view->unmap.link);
348 view_begin_destroy(&xwayland_view->view); 350 view_begin_destroy(&xwayland_view->view);
@@ -613,6 +615,10 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
613 wl_signal_add(&xsurface->events.set_hints, &xwayland_view->set_hints); 615 wl_signal_add(&xsurface->events.set_hints, &xwayland_view->set_hints);
614 xwayland_view->set_hints.notify = handle_set_hints; 616 xwayland_view->set_hints.notify = handle_set_hints;
615 617
618 wl_signal_add(&xsurface->events.set_decorations,
619 &xwayland_view->set_decorations);
620 xwayland_view->set_decorations.notify = handle_set_decorations;
621
616 wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); 622 wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap);
617 xwayland_view->unmap.notify = handle_unmap; 623 xwayland_view->unmap.notify = handle_unmap;
618 624
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index aa0e07f5..eab102fd 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -175,7 +175,8 @@ static enum wlr_edges find_edge(struct sway_container *cont,
175 return WLR_EDGE_NONE; 175 return WLR_EDGE_NONE;
176 } 176 }
177 struct sway_view *view = cont->view; 177 struct sway_view *view = cont->view;
178 if (view->border == B_NONE || !view->border_thickness || view->using_csd) { 178 if (view->border == B_NONE || !view->border_thickness ||
179 view->border == B_CSD) {
179 return WLR_EDGE_NONE; 180 return WLR_EDGE_NONE;
180 } 181 }
181 182
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index f054ac9f..df24b812 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -216,6 +216,8 @@ static const char *describe_container_border(enum sway_container_border border)
216 return "pixel"; 216 return "pixel";
217 case B_NORMAL: 217 case B_NORMAL:
218 return "normal"; 218 return "normal";
219 case B_CSD:
220 return "csd";
219 } 221 }
220 return "unknown"; 222 return "unknown";
221} 223}
diff --git a/sway/meson.build b/sway/meson.build
index d67a4c64..2a3c5b5e 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -10,6 +10,7 @@ sway_sources = files(
10 'security.c', 10 'security.c',
11 'server.c', 11 'server.c',
12 'swaynag.c', 12 'swaynag.c',
13 'xdg_decoration.c',
13 14
14 'desktop/desktop.c', 15 'desktop/desktop.c',
15 'desktop/idle_inhibit_v1.c', 16 'desktop/idle_inhibit_v1.c',
diff --git a/sway/server.c b/sway/server.c
index bed5aed1..63bfa7e1 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -16,6 +16,7 @@
16#include <wlr/types/wlr_screencopy_v1.h> 16#include <wlr/types/wlr_screencopy_v1.h>
17#include <wlr/types/wlr_server_decoration.h> 17#include <wlr/types/wlr_server_decoration.h>
18#include <wlr/types/wlr_xcursor_manager.h> 18#include <wlr/types/wlr_xcursor_manager.h>
19#include <wlr/types/wlr_xdg_decoration_v1.h>
19#include <wlr/types/wlr_xdg_output_v1.h> 20#include <wlr/types/wlr_xdg_output_v1.h>
20#include <wlr/util/log.h> 21#include <wlr/util/log.h>
21#include "list.h" 22#include "list.h"
@@ -115,6 +116,14 @@ bool server_init(struct sway_server *server) {
115 server->server_decoration.notify = handle_server_decoration; 116 server->server_decoration.notify = handle_server_decoration;
116 wl_list_init(&server->decorations); 117 wl_list_init(&server->decorations);
117 118
119 server->xdg_decoration_manager =
120 wlr_xdg_decoration_manager_v1_create(server->wl_display);
121 wl_signal_add(
122 &server->xdg_decoration_manager->events.new_toplevel_decoration,
123 &server->xdg_decoration);
124 server->xdg_decoration.notify = handle_xdg_decoration;
125 wl_list_init(&server->xdg_decorations);
126
118 wlr_export_dmabuf_manager_v1_create(server->wl_display); 127 wlr_export_dmabuf_manager_v1_create(server->wl_display);
119 wlr_screencopy_manager_v1_create(server->wl_display); 128 wlr_screencopy_manager_v1_create(server->wl_display);
120 129
diff --git a/sway/tree/container.c b/sway/tree/container.c
index baaa82fd..d75e34a5 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -715,7 +715,7 @@ void container_set_geometry_from_floating_view(struct sway_container *con) {
715 size_t border_width = 0; 715 size_t border_width = 0;
716 size_t top = 0; 716 size_t top = 0;
717 717
718 if (!view->using_csd) { 718 if (view->border != B_CSD) {
719 border_width = view->border_thickness * (view->border != B_NONE); 719 border_width = view->border_thickness * (view->border != B_NONE);
720 top = view->border == B_NORMAL ? 720 top = view->border == B_NORMAL ?
721 container_titlebar_height() : border_width; 721 container_titlebar_height() : border_width;
diff --git a/sway/tree/view.c b/sway/tree/view.c
index e370443c..c370de2d 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -5,6 +5,8 @@
5#include <wlr/render/wlr_renderer.h> 5#include <wlr/render/wlr_renderer.h>
6#include <wlr/types/wlr_buffer.h> 6#include <wlr/types/wlr_buffer.h>
7#include <wlr/types/wlr_output_layout.h> 7#include <wlr/types/wlr_output_layout.h>
8#include <wlr/types/wlr_server_decoration.h>
9#include <wlr/types/wlr_xdg_decoration_v1.h>
8#include "config.h" 10#include "config.h"
9#ifdef HAVE_XWAYLAND 11#ifdef HAVE_XWAYLAND
10#include <wlr/xwayland.h> 12#include <wlr/xwayland.h>
@@ -23,6 +25,7 @@
23#include "sway/tree/view.h" 25#include "sway/tree/view.h"
24#include "sway/tree/workspace.h" 26#include "sway/tree/workspace.h"
25#include "sway/config.h" 27#include "sway/config.h"
28#include "sway/xdg_decoration.h"
26#include "pango.h" 29#include "pango.h"
27#include "stringop.h" 30#include "stringop.h"
28 31
@@ -231,12 +234,8 @@ void view_autoconfigure(struct sway_view *view) {
231 view->border_top = false; 234 view->border_top = false;
232 } 235 }
233 236
234 enum sway_container_border border = view->border; 237 switch (view->border) {
235 if (view->using_csd) { 238 case B_CSD:
236 border = B_NONE;
237 }
238
239 switch (border) {
240 case B_NONE: 239 case B_NONE:
241 x = con->x; 240 x = con->x;
242 y = con->y + y_offset; 241 y = con->y + y_offset;
@@ -309,16 +308,26 @@ void view_request_activate(struct sway_view *view) {
309 } 308 }
310} 309}
311 310
312void view_set_tiled(struct sway_view *view, bool tiled) { 311void view_set_csd_from_server(struct sway_view *view, bool enabled) {
313 if (!tiled) { 312 if (view->xdg_decoration) {
314 view->using_csd = true; 313 uint32_t mode = enabled ?
315 if (view->impl->has_client_side_decorations) { 314 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE :
316 view->using_csd = view->impl->has_client_side_decorations(view); 315 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
317 } 316 wlr_xdg_toplevel_decoration_v1_set_mode(
318 } else { 317 view->xdg_decoration->wlr_xdg_decoration, mode);
319 view->using_csd = false;
320 } 318 }
319}
321 320
321void view_set_csd_from_client(struct sway_view *view, bool enabled) {
322 if (enabled && view->border != B_CSD) {
323 view->saved_border = view->border;
324 view->border = B_CSD;
325 } else if (!enabled && view->border == B_CSD) {
326 view->border = view->saved_border;
327 }
328}
329
330void view_set_tiled(struct sway_view *view, bool tiled) {
322 if (view->impl->set_tiled) { 331 if (view->impl->set_tiled) {
323 view->impl->set_tiled(view, tiled); 332 view->impl->set_tiled(view, tiled);
324 } 333 }
diff --git a/sway/xdg_decoration.c b/sway/xdg_decoration.c
new file mode 100644
index 00000000..2e7e4bd0
--- /dev/null
+++ b/sway/xdg_decoration.c
@@ -0,0 +1,73 @@
1#include <stdlib.h>
2#include "sway/desktop/transaction.h"
3#include "sway/server.h"
4#include "sway/tree/arrange.h"
5#include "sway/tree/view.h"
6#include "sway/xdg_decoration.h"
7#include "log.h"
8
9static void xdg_decoration_handle_destroy(struct wl_listener *listener,
10 void *data) {
11 struct sway_xdg_decoration *deco =
12 wl_container_of(listener, deco, destroy);
13 deco->view->xdg_decoration = NULL;
14 wl_list_remove(&deco->destroy.link);
15 wl_list_remove(&deco->surface_commit.link);
16 wl_list_remove(&deco->link);
17 free(deco);
18}
19
20static void xdg_decoration_handle_surface_commit(struct wl_listener *listener,
21 void *data) {
22 struct sway_xdg_decoration *decoration =
23 wl_container_of(listener, decoration, surface_commit);
24
25 bool csd = decoration->wlr_xdg_decoration->current_mode ==
26 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
27 struct sway_view *view = decoration->view;
28
29 view_set_csd_from_client(view, csd);
30
31 arrange_container(view->container);
32 transaction_commit_dirty();
33}
34
35void handle_xdg_decoration(struct wl_listener *listener, void *data) {
36 struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data;
37 struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->surface->data;
38 struct wlr_xdg_surface *wlr_xdg_surface =
39 xdg_shell_view->view.wlr_xdg_surface;
40
41 struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco));
42 if (deco == NULL) {
43 return;
44 }
45
46 deco->view = &xdg_shell_view->view;
47 deco->view->xdg_decoration = deco;
48 deco->wlr_xdg_decoration = wlr_deco;
49
50 wl_signal_add(&wlr_deco->events.destroy, &deco->destroy);
51 deco->destroy.notify = xdg_decoration_handle_destroy;
52
53 // Note: We don't listen to the request_mode signal here, effectively
54 // ignoring any modes the client asks to set. The client can still force a
55 // mode upon us, in which case we get upset but live with it.
56
57 deco->surface_commit.notify = xdg_decoration_handle_surface_commit;
58 wl_signal_add(&wlr_xdg_surface->surface->events.commit,
59 &deco->surface_commit);
60
61 wl_list_insert(&server.xdg_decorations, &deco->link);
62}
63
64struct sway_xdg_decoration *xdg_decoration_from_surface(
65 struct wlr_surface *surface) {
66 struct sway_xdg_decoration *deco;
67 wl_list_for_each(deco, &server.xdg_decorations, link) {
68 if (deco->wlr_xdg_decoration->surface->surface == surface) {
69 return deco;
70 }
71 }
72 return NULL;
73}