aboutsummaryrefslogtreecommitdiffstats
path: root/sway/desktop
diff options
context:
space:
mode:
Diffstat (limited to 'sway/desktop')
-rw-r--r--sway/desktop/desktop.c40
-rw-r--r--sway/desktop/idle_inhibit_v1.c46
-rw-r--r--sway/desktop/launcher.c267
-rw-r--r--sway/desktop/layer_shell.c780
-rw-r--r--sway/desktop/output.c980
-rw-r--r--sway/desktop/render.c1204
-rw-r--r--sway/desktop/surface.c46
-rw-r--r--sway/desktop/transaction.c500
-rw-r--r--sway/desktop/xdg_shell.c252
-rw-r--r--sway/desktop/xwayland.c268
10 files changed, 1642 insertions, 2741 deletions
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c
deleted file mode 100644
index c8d4502c..00000000
--- a/sway/desktop/desktop.c
+++ /dev/null
@@ -1,40 +0,0 @@
1#include "sway/tree/container.h"
2#include "sway/desktop.h"
3#include "sway/output.h"
4
5void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
6 bool whole) {
7 for (int i = 0; i < root->outputs->length; ++i) {
8 struct sway_output *output = root->outputs->items[i];
9 struct wlr_box output_box;
10 wlr_output_layout_get_box(root->output_layout,
11 output->wlr_output, &output_box);
12 output_damage_surface(output, lx - output_box.x,
13 ly - output_box.y, surface, whole);
14 }
15}
16
17void desktop_damage_whole_container(struct sway_container *con) {
18 for (int i = 0; i < root->outputs->length; ++i) {
19 struct sway_output *output = root->outputs->items[i];
20 output_damage_whole_container(output, con);
21 }
22}
23
24void desktop_damage_box(struct wlr_box *box) {
25 for (int i = 0; i < root->outputs->length; ++i) {
26 struct sway_output *output = root->outputs->items[i];
27 output_damage_box(output, box);
28 }
29}
30
31void desktop_damage_view(struct sway_view *view) {
32 desktop_damage_whole_container(view->container);
33 struct wlr_box box = {
34 .x = view->container->current.content_x - view->geometry.x,
35 .y = view->container->current.content_y - view->geometry.y,
36 .width = view->surface->current.width,
37 .height = view->surface->current.height,
38 };
39 desktop_damage_box(&box);
40}
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c
index 82353038..f3af7aa1 100644
--- a/sway/desktop/idle_inhibit_v1.c
+++ b/sway/desktop/idle_inhibit_v1.c
@@ -1,5 +1,5 @@
1#include <stdlib.h> 1#include <stdlib.h>
2#include <wlr/types/wlr_idle.h> 2#include <wlr/types/wlr_idle_notify_v1.h>
3#include "log.h" 3#include "log.h"
4#include "sway/desktop/idle_inhibit_v1.h" 4#include "sway/desktop/idle_inhibit_v1.h"
5#include "sway/input/seat.h" 5#include "sway/input/seat.h"
@@ -11,7 +11,7 @@
11static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) { 11static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) {
12 wl_list_remove(&inhibitor->link); 12 wl_list_remove(&inhibitor->link);
13 wl_list_remove(&inhibitor->destroy.link); 13 wl_list_remove(&inhibitor->destroy.link);
14 sway_idle_inhibit_v1_check_active(inhibitor->manager); 14 sway_idle_inhibit_v1_check_active();
15 free(inhibitor); 15 free(inhibitor);
16} 16}
17 17
@@ -34,7 +34,6 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) {
34 return; 34 return;
35 } 35 }
36 36
37 inhibitor->manager = manager;
38 inhibitor->mode = INHIBIT_IDLE_APPLICATION; 37 inhibitor->mode = INHIBIT_IDLE_APPLICATION;
39 inhibitor->wlr_inhibitor = wlr_inhibitor; 38 inhibitor->wlr_inhibitor = wlr_inhibitor;
40 wl_list_insert(&manager->inhibitors, &inhibitor->link); 39 wl_list_insert(&manager->inhibitors, &inhibitor->link);
@@ -42,33 +41,34 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) {
42 inhibitor->destroy.notify = handle_destroy; 41 inhibitor->destroy.notify = handle_destroy;
43 wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); 42 wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy);
44 43
45 sway_idle_inhibit_v1_check_active(manager); 44 sway_idle_inhibit_v1_check_active();
46} 45}
47 46
48void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, 47void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view,
49 enum sway_idle_inhibit_mode mode) { 48 enum sway_idle_inhibit_mode mode) {
49 struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;
50
50 struct sway_idle_inhibitor_v1 *inhibitor = 51 struct sway_idle_inhibitor_v1 *inhibitor =
51 calloc(1, sizeof(struct sway_idle_inhibitor_v1)); 52 calloc(1, sizeof(struct sway_idle_inhibitor_v1));
52 if (!inhibitor) { 53 if (!inhibitor) {
53 return; 54 return;
54 } 55 }
55 56
56 inhibitor->manager = server.idle_inhibit_manager_v1;
57 inhibitor->mode = mode; 57 inhibitor->mode = mode;
58 inhibitor->view = view; 58 inhibitor->view = view;
59 wl_list_insert(&inhibitor->manager->inhibitors, &inhibitor->link); 59 wl_list_insert(&manager->inhibitors, &inhibitor->link);
60 60
61 inhibitor->destroy.notify = handle_destroy; 61 inhibitor->destroy.notify = handle_destroy;
62 wl_signal_add(&view->events.unmap, &inhibitor->destroy); 62 wl_signal_add(&view->events.unmap, &inhibitor->destroy);
63 63
64 sway_idle_inhibit_v1_check_active(inhibitor->manager); 64 sway_idle_inhibit_v1_check_active();
65} 65}
66 66
67struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( 67struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view(
68 struct sway_view *view) { 68 struct sway_view *view) {
69 struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;
69 struct sway_idle_inhibitor_v1 *inhibitor; 70 struct sway_idle_inhibitor_v1 *inhibitor;
70 wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, 71 wl_list_for_each(inhibitor, &manager->inhibitors, link) {
71 link) {
72 if (inhibitor->mode != INHIBIT_IDLE_APPLICATION && 72 if (inhibitor->mode != INHIBIT_IDLE_APPLICATION &&
73 inhibitor->view == view) { 73 inhibitor->view == view) {
74 return inhibitor; 74 return inhibitor;
@@ -79,9 +79,9 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view(
79 79
80struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view( 80struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view(
81 struct sway_view *view) { 81 struct sway_view *view) {
82 struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;
82 struct sway_idle_inhibitor_v1 *inhibitor; 83 struct sway_idle_inhibitor_v1 *inhibitor;
83 wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, 84 wl_list_for_each(inhibitor, &manager->inhibitors, link) {
84 link) {
85 if (inhibitor->mode == INHIBIT_IDLE_APPLICATION && 85 if (inhibitor->mode == INHIBIT_IDLE_APPLICATION &&
86 view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) { 86 view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) {
87 return inhibitor; 87 return inhibitor;
@@ -130,8 +130,8 @@ bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) {
130 return false; 130 return false;
131} 131}
132 132
133void sway_idle_inhibit_v1_check_active( 133void sway_idle_inhibit_v1_check_active(void) {
134 struct sway_idle_inhibit_manager_v1 *manager) { 134 struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;
135 struct sway_idle_inhibitor_v1 *inhibitor; 135 struct sway_idle_inhibitor_v1 *inhibitor;
136 bool inhibited = false; 136 bool inhibited = false;
137 wl_list_for_each(inhibitor, &manager->inhibitors, link) { 137 wl_list_for_each(inhibitor, &manager->inhibitors, link) {
@@ -139,27 +139,21 @@ void sway_idle_inhibit_v1_check_active(
139 break; 139 break;
140 } 140 }
141 } 141 }
142 wlr_idle_set_enabled(manager->idle, NULL, !inhibited); 142 wlr_idle_notifier_v1_set_inhibited(server.idle_notifier_v1, inhibited);
143} 143}
144 144
145struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( 145bool sway_idle_inhibit_manager_v1_init(void) {
146 struct wl_display *wl_display, struct wlr_idle *idle) { 146 struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;
147 struct sway_idle_inhibit_manager_v1 *manager =
148 calloc(1, sizeof(struct sway_idle_inhibit_manager_v1));
149 if (!manager) {
150 return NULL;
151 }
152 147
153 manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display); 148 manager->wlr_manager = wlr_idle_inhibit_v1_create(server.wl_display);
154 if (!manager->wlr_manager) { 149 if (!manager->wlr_manager) {
155 free(manager); 150 return false;
156 return NULL;
157 } 151 }
158 manager->idle = idle; 152
159 wl_signal_add(&manager->wlr_manager->events.new_inhibitor, 153 wl_signal_add(&manager->wlr_manager->events.new_inhibitor,
160 &manager->new_idle_inhibitor_v1); 154 &manager->new_idle_inhibitor_v1);
161 manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; 155 manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1;
162 wl_list_init(&manager->inhibitors); 156 wl_list_init(&manager->inhibitors);
163 157
164 return manager; 158 return true;
165} 159}
diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c
new file mode 100644
index 00000000..28043d19
--- /dev/null
+++ b/sway/desktop/launcher.c
@@ -0,0 +1,267 @@
1#include <stdlib.h>
2#include <string.h>
3#include <wlr/types/wlr_xdg_activation_v1.h>
4#include "sway/input/seat.h"
5#include "sway/output.h"
6#include "sway/desktop/launcher.h"
7#include "sway/tree/node.h"
8#include "sway/tree/container.h"
9#include "sway/tree/workspace.h"
10#include "sway/tree/root.h"
11#include "log.h"
12
13/**
14 * Get the pid of a parent process given the pid of a child process.
15 *
16 * Returns the parent pid or NULL if the parent pid cannot be determined.
17 */
18static pid_t get_parent_pid(pid_t child) {
19 pid_t parent = -1;
20 char file_name[100];
21 char *buffer = NULL;
22 const char *sep = " ";
23 FILE *stat = NULL;
24 size_t buf_size = 0;
25
26 snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child);
27
28 if ((stat = fopen(file_name, "r"))) {
29 if (getline(&buffer, &buf_size, stat) != -1) {
30 strtok(buffer, sep); // pid
31 strtok(NULL, sep); // executable name
32 strtok(NULL, sep); // state
33 char *token = strtok(NULL, sep); // parent pid
34 parent = strtol(token, NULL, 10);
35 }
36 free(buffer);
37 fclose(stat);
38 }
39
40 if (parent) {
41 return (parent == child) ? -1 : parent;
42 }
43
44 return -1;
45}
46
47void launcher_ctx_consume(struct launcher_ctx *ctx) {
48 // The view is now responsible for destroying this ctx
49 wl_list_remove(&ctx->token_destroy.link);
50 wl_list_init(&ctx->token_destroy.link);
51
52 if (!ctx->activated) {
53 // An unactivated token hasn't been destroyed yet
54 wlr_xdg_activation_token_v1_destroy(ctx->token);
55 }
56 ctx->token = NULL;
57
58 // Prevent additional matches
59 wl_list_remove(&ctx->link);
60 wl_list_init(&ctx->link);
61}
62
63void launcher_ctx_destroy(struct launcher_ctx *ctx) {
64 if (ctx == NULL) {
65 return;
66 }
67 wl_list_remove(&ctx->node_destroy.link);
68 wl_list_remove(&ctx->token_destroy.link);
69 if (ctx->seat) {
70 wl_list_remove(&ctx->seat_destroy.link);
71 }
72 wl_list_remove(&ctx->link);
73 wlr_xdg_activation_token_v1_destroy(ctx->token);
74 free(ctx->fallback_name);
75 free(ctx);
76}
77
78struct launcher_ctx *launcher_ctx_find_pid(pid_t pid) {
79 if (wl_list_empty(&server.pending_launcher_ctxs)) {
80 return NULL;
81 }
82
83 struct launcher_ctx *ctx = NULL;
84 sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid);
85
86 do {
87 struct launcher_ctx *_ctx = NULL;
88 wl_list_for_each(_ctx, &server.pending_launcher_ctxs, link) {
89 if (pid == _ctx->pid) {
90 ctx = _ctx;
91 sway_log(SWAY_DEBUG,
92 "found %s match for pid %d: %s",
93 node_type_to_str(ctx->node->type), pid, node_get_name(ctx->node));
94 break;
95 }
96 }
97 pid = get_parent_pid(pid);
98 } while (pid > 1);
99
100 return ctx;
101}
102
103struct sway_workspace *launcher_ctx_get_workspace(
104 struct launcher_ctx *ctx) {
105 struct sway_workspace *ws = NULL;
106 struct sway_output *output = NULL;
107
108 switch (ctx->node->type) {
109 case N_CONTAINER:
110 // Unimplemented
111 // TODO: add container matching?
112 ws = ctx->node->sway_container->pending.workspace;
113 break;
114 case N_WORKSPACE:
115 ws = ctx->node->sway_workspace;
116 break;
117 case N_OUTPUT:
118 output = ctx->node->sway_output;
119 ws = workspace_by_name(ctx->fallback_name);
120 if (!ws) {
121 sway_log(SWAY_DEBUG,
122 "Creating workspace %s for pid %d because it disappeared",
123 ctx->fallback_name, ctx->pid);
124 if (!output->enabled) {
125 sway_log(SWAY_DEBUG,
126 "Workspace output %s is disabled, trying another one",
127 output->wlr_output->name);
128 output = NULL;
129 }
130 ws = workspace_create(output, ctx->fallback_name);
131 }
132 break;
133 case N_ROOT:
134 ws = workspace_create(NULL, ctx->fallback_name);
135 break;
136 }
137
138 return ws;
139}
140
141static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) {
142 struct launcher_ctx *ctx = wl_container_of(listener, ctx, node_destroy);
143 switch (ctx->node->type) {
144 case N_CONTAINER:
145 // Unimplemented
146 break;
147 case N_WORKSPACE:;
148 struct sway_workspace *ws = ctx->node->sway_workspace;
149 wl_list_remove(&ctx->node_destroy.link);
150 wl_list_init(&ctx->node_destroy.link);
151 // We want to save this ws name to recreate later, hopefully on the
152 // same output
153 free(ctx->fallback_name);
154 ctx->fallback_name = strdup(ws->name);
155 if (!ws->output || ws->output->node.destroying) {
156 // If the output is being destroyed it would be pointless to track
157 // If the output is being disabled, we'll find out if it's still
158 // disabled when we try to match it.
159 ctx->node = &root->node;
160 break;
161 }
162 ctx->node = &ws->output->node;
163 wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy);
164 break;
165 case N_OUTPUT:
166 wl_list_remove(&ctx->node_destroy.link);
167 wl_list_init(&ctx->node_destroy.link);
168 // We'll make the ws ctx->name somewhere else
169 ctx->node = &root->node;
170 break;
171 case N_ROOT:
172 // Unreachable
173 break;
174 }
175}
176
177static void token_handle_destroy(struct wl_listener *listener, void *data) {
178 struct launcher_ctx *ctx = wl_container_of(listener, ctx, token_destroy);
179 ctx->token = NULL;
180 launcher_ctx_destroy(ctx);
181}
182
183struct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *token,
184 struct sway_node *node) {
185 struct launcher_ctx *ctx = calloc(1, sizeof(struct launcher_ctx));
186
187 const char *fallback_name = NULL;
188 struct sway_workspace *ws = NULL;
189 switch (node->type) {
190 case N_CONTAINER:
191 // Unimplemented
192 free(ctx);
193 return NULL;
194 case N_WORKSPACE:
195 ws = node->sway_workspace;
196 fallback_name = ws->name;
197 break;
198 case N_OUTPUT:;
199 struct sway_output *output = node->sway_output;
200 ws = output_get_active_workspace(output);
201 fallback_name = ws ? ws->name : NULL;
202 break;
203 case N_ROOT:
204 // Unimplemented
205 free(ctx);
206 return NULL;
207 }
208
209 if (!fallback_name) {
210 // TODO: implement a better fallback.
211 free(ctx);
212 return NULL;
213 }
214
215 ctx->fallback_name = strdup(fallback_name);
216 ctx->token = token;
217 ctx->node = node;
218 // Having surface set means that the focus check in wlroots has passed
219 ctx->had_focused_surface = token->surface != NULL;
220
221 ctx->node_destroy.notify = ctx_handle_node_destroy;
222 wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy);
223
224 ctx->token_destroy.notify = token_handle_destroy;
225 wl_signal_add(&token->events.destroy, &ctx->token_destroy);
226
227 wl_list_init(&ctx->link);
228 wl_list_insert(&server.pending_launcher_ctxs, &ctx->link);
229
230 token->data = ctx;
231 return ctx;
232}
233
234static void launch_ctx_handle_seat_destroy(struct wl_listener *listener, void *data) {
235 struct launcher_ctx *ctx = wl_container_of(listener, ctx, seat_destroy);
236 ctx->seat = NULL;
237 wl_list_remove(&ctx->seat_destroy.link);
238}
239
240// Creates a context with a new token for the internal launcher
241struct launcher_ctx *launcher_ctx_create_internal(void) {
242 struct sway_seat *seat = input_manager_current_seat();
243 struct sway_workspace *ws = seat_get_focused_workspace(seat);
244 if (!ws) {
245 sway_log(SWAY_DEBUG, "Failed to create launch context. No workspace.");
246 return NULL;
247 }
248
249 struct wlr_xdg_activation_token_v1 *token =
250 wlr_xdg_activation_token_v1_create(server.xdg_activation_v1);
251
252 struct launcher_ctx *ctx = launcher_ctx_create(token, &ws->node);
253 if (!ctx) {
254 wlr_xdg_activation_token_v1_destroy(token);
255 return NULL;
256 }
257 ctx->seat = seat;
258 ctx->seat_destroy.notify = launch_ctx_handle_seat_destroy;
259 wl_signal_add(&seat->wlr_seat->events.destroy, &ctx->seat_destroy);
260
261 return ctx;
262}
263
264const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx) {
265 const char *token = wlr_xdg_activation_token_v1_get_name(ctx->token);
266 return token;
267}
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index d44d6338..6221b7b9 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -2,11 +2,14 @@
2#include <stdlib.h> 2#include <stdlib.h>
3#include <string.h> 3#include <string.h>
4#include <wayland-server-core.h> 4#include <wayland-server-core.h>
5#include <wlr/types/wlr_fractional_scale_v1.h>
5#include <wlr/types/wlr_layer_shell_v1.h> 6#include <wlr/types/wlr_layer_shell_v1.h>
6#include <wlr/types/wlr_output_damage.h>
7#include <wlr/types/wlr_output.h> 7#include <wlr/types/wlr_output.h>
8#include <wlr/types/wlr_scene.h>
8#include <wlr/types/wlr_subcompositor.h> 9#include <wlr/types/wlr_subcompositor.h>
10#include <wlr/types/wlr_xdg_shell.h>
9#include "log.h" 11#include "log.h"
12#include "sway/scene_descriptor.h"
10#include "sway/desktop/transaction.h" 13#include "sway/desktop/transaction.h"
11#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
12#include "sway/input/input-manager.h" 15#include "sway/input/input-manager.h"
@@ -17,162 +20,55 @@
17#include "sway/tree/arrange.h" 20#include "sway/tree/arrange.h"
18#include "sway/tree/workspace.h" 21#include "sway/tree/workspace.h"
19 22
20static void apply_exclusive(struct wlr_box *usable_area, 23struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface(
21 uint32_t anchor, int32_t exclusive, 24 struct wlr_surface *surface) {
22 int32_t margin_top, int32_t margin_right, 25 struct wlr_layer_surface_v1 *layer;
23 int32_t margin_bottom, int32_t margin_left) { 26 do {
24 if (exclusive <= 0) { 27 if (!surface) {
25 return; 28 return NULL;
26 } 29 }
27 struct { 30 // Topmost layer surface
28 uint32_t singular_anchor; 31 if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface))) {
29 uint32_t anchor_triplet; 32 return layer;
30 int *positive_axis; 33 }
31 int *negative_axis; 34 // Layer subsurface
32 int margin; 35 if (wlr_subsurface_try_from_wlr_surface(surface)) {
33 } edges[] = { 36 surface = wlr_surface_get_root_surface(surface);
34 // Top 37 continue;
35 { 38 }
36 .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, 39
37 .anchor_triplet = 40 // Layer surface popup
38 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | 41 struct wlr_xdg_surface *xdg_surface = NULL;
39 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | 42 if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface)) &&
40 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, 43 xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL) {
41 .positive_axis = &usable_area->y, 44 if (!xdg_surface->popup->parent) {
42 .negative_axis = &usable_area->height, 45 return NULL;
43 .margin = margin_top,
44 },
45 // Bottom
46 {
47 .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
48 .anchor_triplet =
49 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
50 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
51 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
52 .positive_axis = NULL,
53 .negative_axis = &usable_area->height,
54 .margin = margin_bottom,
55 },
56 // Left
57 {
58 .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT,
59 .anchor_triplet =
60 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
61 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
62 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
63 .positive_axis = &usable_area->x,
64 .negative_axis = &usable_area->width,
65 .margin = margin_left,
66 },
67 // Right
68 {
69 .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT,
70 .anchor_triplet =
71 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
72 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
73 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
74 .positive_axis = NULL,
75 .negative_axis = &usable_area->width,
76 .margin = margin_right,
77 },
78 };
79 for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) {
80 if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet)
81 && exclusive + edges[i].margin > 0) {
82 if (edges[i].positive_axis) {
83 *edges[i].positive_axis += exclusive + edges[i].margin;
84 }
85 if (edges[i].negative_axis) {
86 *edges[i].negative_axis -= exclusive + edges[i].margin;
87 } 46 }
88 break; 47 surface = wlr_surface_get_root_surface(xdg_surface->popup->parent);
48 continue;
89 } 49 }
90 } 50
51 // Return early if the surface is not a layer/xdg_popup/sub surface
52 return NULL;
53 } while (true);
91} 54}
92 55
93static void arrange_layer(struct sway_output *output, struct wl_list *list, 56static void arrange_surface(struct sway_output *output, const struct wlr_box *full_area,
94 struct wlr_box *usable_area, bool exclusive) { 57 struct wlr_box *usable_area, struct wlr_scene_tree *tree) {
95 struct sway_layer_surface *sway_layer; 58 struct wlr_scene_node *node;
96 struct wlr_box full_area = { 0 }; 59 wl_list_for_each(node, &tree->children, link) {
97 wlr_output_effective_resolution(output->wlr_output, 60 struct sway_layer_surface *surface = scene_descriptor_try_get(node,
98 &full_area.width, &full_area.height); 61 SWAY_SCENE_DESC_LAYER_SHELL);
99 wl_list_for_each(sway_layer, list, link) { 62 // surface could be null during destruction
100 struct wlr_layer_surface_v1 *layer = sway_layer->layer_surface; 63 if (!surface) {
101 struct wlr_layer_surface_v1_state *state = &layer->current;
102 if (exclusive != (state->exclusive_zone > 0)) {
103 continue; 64 continue;
104 } 65 }
105 struct wlr_box bounds; 66
106 if (state->exclusive_zone == -1) { 67 if (!surface->scene->layer_surface->initialized) {
107 bounds = full_area;
108 } else {
109 bounds = *usable_area;
110 }
111 struct wlr_box box = {
112 .width = state->desired_width,
113 .height = state->desired_height
114 };
115 // Horizontal axis
116 const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
117 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
118 if (box.width == 0) {
119 box.x = bounds.x;
120 } else if ((state->anchor & both_horiz) == both_horiz) {
121 box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
122 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
123 box.x = bounds.x;
124 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
125 box.x = bounds.x + (bounds.width - box.width);
126 } else {
127 box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
128 }
129 // Vertical axis
130 const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
131 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
132 if (box.height == 0) {
133 box.y = bounds.y;
134 } else if ((state->anchor & both_vert) == both_vert) {
135 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
136 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
137 box.y = bounds.y;
138 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
139 box.y = bounds.y + (bounds.height - box.height);
140 } else {
141 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
142 }
143 // Margin
144 if (box.width == 0) {
145 box.x += state->margin.left;
146 box.width = bounds.width -
147 (state->margin.left + state->margin.right);
148 } else if ((state->anchor & both_horiz) == both_horiz) {
149 // don't apply margins
150 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
151 box.x += state->margin.left;
152 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
153 box.x -= state->margin.right;
154 }
155 if (box.height == 0) {
156 box.y += state->margin.top;
157 box.height = bounds.height -
158 (state->margin.top + state->margin.bottom);
159 } else if ((state->anchor & both_vert) == both_vert) {
160 // don't apply margins
161 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
162 box.y += state->margin.top;
163 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
164 box.y -= state->margin.bottom;
165 }
166 if (!sway_assert(box.width >= 0 && box.height >= 0,
167 "Expected layer surface to have positive size")) {
168 continue; 68 continue;
169 } 69 }
170 // Apply 70
171 sway_layer->geo = box; 71 wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area);
172 apply_exclusive(usable_area, state->anchor, state->exclusive_zone,
173 state->margin.top, state->margin.right,
174 state->margin.bottom, state->margin.left);
175 wlr_layer_surface_v1_configure(layer, box.width, box.height);
176 } 72 }
177} 73}
178 74
@@ -180,81 +76,94 @@ void arrange_layers(struct sway_output *output) {
180 struct wlr_box usable_area = { 0 }; 76 struct wlr_box usable_area = { 0 };
181 wlr_output_effective_resolution(output->wlr_output, 77 wlr_output_effective_resolution(output->wlr_output,
182 &usable_area.width, &usable_area.height); 78 &usable_area.width, &usable_area.height);
79 const struct wlr_box full_area = usable_area;
80
81 arrange_surface(output, &full_area, &usable_area, output->layers.shell_background);
82 arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom);
83 arrange_surface(output, &full_area, &usable_area, output->layers.shell_top);
84 arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay);
183 85
184 // Arrange exclusive surfaces from top->bottom 86 if (!wlr_box_equal(&usable_area, &output->usable_area)) {
185 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
186 &usable_area, true);
187 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
188 &usable_area, true);
189 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
190 &usable_area, true);
191 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
192 &usable_area, true);
193
194 if (memcmp(&usable_area, &output->usable_area,
195 sizeof(struct wlr_box)) != 0) {
196 sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); 87 sway_log(SWAY_DEBUG, "Usable area changed, rearranging output");
197 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); 88 output->usable_area = usable_area;
198 arrange_output(output); 89 arrange_output(output);
90 } else {
91 arrange_popups(root->layers.popup);
199 } 92 }
93}
200 94
201 // Arrange non-exclusive surfaces from top->bottom 95static struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output,
202 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], 96 enum zwlr_layer_shell_v1_layer type) {
203 &usable_area, false); 97 switch (type) {
204 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], 98 case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND:
205 &usable_area, false); 99 return output->layers.shell_background;
206 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], 100 case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM:
207 &usable_area, false); 101 return output->layers.shell_bottom;
208 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], 102 case ZWLR_LAYER_SHELL_V1_LAYER_TOP:
209 &usable_area, false); 103 return output->layers.shell_top;
210 104 case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY:
211 // Find topmost keyboard interactive layer, if such a layer exists 105 return output->layers.shell_overlay;
212 uint32_t layers_above_shell[] = {
213 ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
214 ZWLR_LAYER_SHELL_V1_LAYER_TOP,
215 };
216 size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]);
217 struct sway_layer_surface *layer, *topmost = NULL;
218 for (size_t i = 0; i < nlayers; ++i) {
219 wl_list_for_each_reverse(layer,
220 &output->layers[layers_above_shell[i]], link) {
221 if (layer->layer_surface->current.keyboard_interactive &&
222 layer->layer_surface->mapped) {
223 topmost = layer;
224 break;
225 }
226 }
227 if (topmost != NULL) {
228 break;
229 }
230 } 106 }
231 107
232 struct sway_seat *seat; 108 sway_assert(false, "unreachable");
233 wl_list_for_each(seat, &server.input->seats, link) { 109 return NULL;
234 if (topmost != NULL) { 110}
235 seat_set_focus_layer(seat, topmost->layer_surface); 111
236 } else if (seat->focused_layer && 112static struct sway_layer_surface *sway_layer_surface_create(
237 !seat->focused_layer->current.keyboard_interactive) { 113 struct wlr_scene_layer_surface_v1 *scene) {
238 seat_set_focus_layer(seat, NULL); 114 struct sway_layer_surface *surface = calloc(1, sizeof(*surface));
239 } 115 if (!surface) {
116 sway_log(SWAY_ERROR, "Could not allocate a scene_layer surface");
117 return NULL;
118 }
119
120 struct wlr_scene_tree *popups = wlr_scene_tree_create(root->layers.popup);
121 if (!popups) {
122 sway_log(SWAY_ERROR, "Could not allocate a scene_layer popup node");
123 free(surface);
124 return NULL;
125 }
126
127 surface->desc.relative = &scene->tree->node;
128
129 if (!scene_descriptor_assign(&popups->node,
130 SWAY_SCENE_DESC_POPUP, &surface->desc)) {
131 sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor");
132 wlr_scene_node_destroy(&popups->node);
133 free(surface);
134 return NULL;
240 } 135 }
136
137 surface->tree = scene->tree;
138 surface->scene = scene;
139 surface->layer_surface = scene->layer_surface;
140 surface->popups = popups;
141 surface->layer_surface->data = surface;
142
143 return surface;
241} 144}
242 145
243static struct sway_layer_surface *find_mapped_layer_by_client( 146static struct sway_layer_surface *find_mapped_layer_by_client(
244 struct wl_client *client, struct wlr_output *ignore_output) { 147 struct wl_client *client, struct sway_output *ignore_output) {
245 for (int i = 0; i < root->outputs->length; ++i) { 148 for (int i = 0; i < root->outputs->length; ++i) {
246 struct sway_output *output = root->outputs->items[i]; 149 struct sway_output *output = root->outputs->items[i];
247 if (output->wlr_output == ignore_output) { 150 if (output == ignore_output) {
248 continue; 151 continue;
249 } 152 }
250 // For now we'll only check the overlay layer 153 // For now we'll only check the overlay layer
251 struct sway_layer_surface *lsurface; 154 struct wlr_scene_node *node;
252 wl_list_for_each(lsurface, 155 wl_list_for_each (node, &output->layers.shell_overlay->children, link) {
253 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { 156 struct sway_layer_surface *surface = scene_descriptor_try_get(node,
254 struct wl_resource *resource = lsurface->layer_surface->resource; 157 SWAY_SCENE_DESC_LAYER_SHELL);
158 if (!surface) {
159 continue;
160 }
161
162 struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface;
163 struct wl_resource *resource = layer_surface->resource;
255 if (wl_resource_get_client(resource) == client 164 if (wl_resource_get_client(resource) == client
256 && lsurface->layer_surface->mapped) { 165 && layer_surface->surface->mapped) {
257 return lsurface; 166 return surface;
258 } 167 }
259 } 168 }
260 } 169 }
@@ -262,293 +171,142 @@ static struct sway_layer_surface *find_mapped_layer_by_client(
262} 171}
263 172
264static void handle_output_destroy(struct wl_listener *listener, void *data) { 173static void handle_output_destroy(struct wl_listener *listener, void *data) {
265 struct sway_layer_surface *sway_layer = 174 struct sway_layer_surface *layer =
266 wl_container_of(listener, sway_layer, output_destroy); 175 wl_container_of(listener, layer, output_destroy);
267 // Determine if this layer is being used by an exclusive client. If it is,
268 // try and find another layer owned by this client to pass focus to.
269 struct sway_seat *seat = input_manager_get_default_seat();
270 struct wl_client *client =
271 wl_resource_get_client(sway_layer->layer_surface->resource);
272 bool set_focus = seat->exclusive_client == client;
273
274 if (set_focus) {
275 struct sway_layer_surface *layer =
276 find_mapped_layer_by_client(client, sway_layer->layer_surface->output);
277 if (layer) {
278 seat_set_focus_layer(seat, layer->layer_surface);
279 }
280 }
281 176
282 wlr_layer_surface_v1_destroy(sway_layer->layer_surface); 177 layer->output = NULL;
178 wlr_scene_node_destroy(&layer->scene->tree->node);
283} 179}
284 180
285static void handle_surface_commit(struct wl_listener *listener, void *data) { 181static void handle_node_destroy(struct wl_listener *listener, void *data) {
286 struct sway_layer_surface *layer = 182 struct sway_layer_surface *layer =
287 wl_container_of(listener, layer, surface_commit); 183 wl_container_of(listener, layer, node_destroy);
288 struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface;
289 struct wlr_output *wlr_output = layer_surface->output;
290 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
291 struct sway_output *output = wlr_output->data;
292 struct wlr_box old_extent = layer->extent;
293
294 bool layer_changed = false;
295 if (layer_surface->current.committed != 0
296 || layer->mapped != layer_surface->mapped) {
297 layer->mapped = layer_surface->mapped;
298 layer_changed = layer->layer != layer_surface->current.layer;
299 if (layer_changed) {
300 wl_list_remove(&layer->link);
301 wl_list_insert(&output->layers[layer_surface->current.layer],
302 &layer->link);
303 layer->layer = layer_surface->current.layer;
304 }
305 arrange_layers(output);
306 }
307 184
308 wlr_surface_get_extends(layer_surface->surface, &layer->extent); 185 // destroy the scene descriptor straight away if it exists, otherwise
309 layer->extent.x += layer->geo.x; 186 // we will try to reflow still considering the destroyed node.
310 layer->extent.y += layer->geo.y; 187 scene_descriptor_destroy(&layer->tree->node, SWAY_SCENE_DESC_LAYER_SHELL);
311 188
312 bool extent_changed = 189 // Determine if this layer is being used by an exclusive client. If it is,
313 memcmp(&old_extent, &layer->extent, sizeof(struct wlr_box)) != 0; 190 // try and find another layer owned by this client to pass focus to.
314 if (extent_changed || layer_changed) { 191 struct sway_seat *seat = input_manager_get_default_seat();
315 output_damage_box(output, &old_extent); 192 struct wl_client *client =
316 output_damage_surface(output, layer->geo.x, layer->geo.y, 193 wl_resource_get_client(layer->layer_surface->resource);
317 layer_surface->surface, true); 194 if (!server.session_lock.lock) {
318 } else { 195 struct sway_layer_surface *consider_layer =
319 output_damage_surface(output, layer->geo.x, layer->geo.y, 196 find_mapped_layer_by_client(client, layer->output);
320 layer_surface->surface, false); 197 if (consider_layer) {
321 } 198 seat_set_focus_layer(seat, consider_layer->layer_surface);
322
323 transaction_commit_dirty();
324}
325
326static void unmap(struct sway_layer_surface *sway_layer) {
327 struct sway_seat *seat;
328 wl_list_for_each(seat, &server.input->seats, link) {
329 if (seat->focused_layer == sway_layer->layer_surface) {
330 seat_set_focus_layer(seat, NULL);
331 } 199 }
332 } 200 }
333 201
334 cursor_rebase_all(); 202 if (layer->output) {
335 203 arrange_layers(layer->output);
336 struct wlr_output *wlr_output = sway_layer->layer_surface->output; 204 transaction_commit_dirty();
337 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
338 struct sway_output *output = wlr_output->data;
339 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
340 sway_layer->layer_surface->surface, true);
341}
342
343static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface);
344
345static void handle_destroy(struct wl_listener *listener, void *data) {
346 struct sway_layer_surface *sway_layer =
347 wl_container_of(listener, sway_layer, destroy);
348 sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)",
349 sway_layer->layer_surface->namespace);
350 if (sway_layer->layer_surface->mapped) {
351 unmap(sway_layer);
352 } 205 }
353 206
354 struct sway_layer_subsurface *subsurface, *subsurface_tmp; 207 wlr_scene_node_destroy(&layer->popups->node);
355 wl_list_for_each_safe(subsurface, subsurface_tmp, &sway_layer->subsurfaces, link) {
356 layer_subsurface_destroy(subsurface);
357 }
358
359 wl_list_remove(&sway_layer->link);
360 wl_list_remove(&sway_layer->destroy.link);
361 wl_list_remove(&sway_layer->map.link);
362 wl_list_remove(&sway_layer->unmap.link);
363 wl_list_remove(&sway_layer->surface_commit.link);
364 wl_list_remove(&sway_layer->new_popup.link);
365 wl_list_remove(&sway_layer->new_subsurface.link);
366
367 struct wlr_output *wlr_output = sway_layer->layer_surface->output;
368 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
369 struct sway_output *output = wlr_output->data;
370 arrange_layers(output);
371 transaction_commit_dirty();
372 wl_list_remove(&sway_layer->output_destroy.link);
373 sway_layer->layer_surface->output = NULL;
374
375 free(sway_layer);
376}
377
378static void handle_map(struct wl_listener *listener, void *data) {
379 struct sway_layer_surface *sway_layer = wl_container_of(listener,
380 sway_layer, map);
381 struct wlr_output *wlr_output = sway_layer->layer_surface->output;
382 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
383 struct sway_output *output = wlr_output->data;
384 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
385 sway_layer->layer_surface->surface, true);
386 wlr_surface_send_enter(sway_layer->layer_surface->surface,
387 sway_layer->layer_surface->output);
388 cursor_rebase_all();
389}
390 208
391static void handle_unmap(struct wl_listener *listener, void *data) { 209 wl_list_remove(&layer->map.link);
392 struct sway_layer_surface *sway_layer = wl_container_of( 210 wl_list_remove(&layer->unmap.link);
393 listener, sway_layer, unmap); 211 wl_list_remove(&layer->surface_commit.link);
394 unmap(sway_layer); 212 wl_list_remove(&layer->node_destroy.link);
395} 213 wl_list_remove(&layer->output_destroy.link);
396 214
397static void subsurface_damage(struct sway_layer_subsurface *subsurface, 215 layer->layer_surface->data = NULL;
398 bool whole) {
399 struct sway_layer_surface *layer = subsurface->layer_surface;
400 struct wlr_output *wlr_output = layer->layer_surface->output;
401 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
402 struct sway_output *output = wlr_output->data;
403 int ox = subsurface->wlr_subsurface->current.x + layer->geo.x;
404 int oy = subsurface->wlr_subsurface->current.y + layer->geo.y;
405 output_damage_surface(
406 output, ox, oy, subsurface->wlr_subsurface->surface, whole);
407}
408 216
409static void subsurface_handle_unmap(struct wl_listener *listener, void *data) { 217 free(layer);
410 struct sway_layer_subsurface *subsurface =
411 wl_container_of(listener, subsurface, unmap);
412 subsurface_damage(subsurface, true);
413} 218}
414 219
415static void subsurface_handle_map(struct wl_listener *listener, void *data) { 220static void handle_surface_commit(struct wl_listener *listener, void *data) {
416 struct sway_layer_subsurface *subsurface = 221 struct sway_layer_surface *surface =
417 wl_container_of(listener, subsurface, map); 222 wl_container_of(listener, surface, surface_commit);
418 subsurface_damage(subsurface, true);
419}
420
421static void subsurface_handle_commit(struct wl_listener *listener, void *data) {
422 struct sway_layer_subsurface *subsurface =
423 wl_container_of(listener, subsurface, commit);
424 subsurface_damage(subsurface, false);
425}
426
427static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface) {
428 wl_list_remove(&subsurface->link);
429 wl_list_remove(&subsurface->map.link);
430 wl_list_remove(&subsurface->unmap.link);
431 wl_list_remove(&subsurface->destroy.link);
432 wl_list_remove(&subsurface->commit.link);
433 free(subsurface);
434}
435
436static void subsurface_handle_destroy(struct wl_listener *listener,
437 void *data) {
438 struct sway_layer_subsurface *subsurface =
439 wl_container_of(listener, subsurface, destroy);
440 layer_subsurface_destroy(subsurface);
441}
442 223
443static struct sway_layer_subsurface *create_subsurface( 224 struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface;
444 struct wlr_subsurface *wlr_subsurface, 225 if (!layer_surface->initialized) {
445 struct sway_layer_surface *layer_surface) { 226 return;
446 struct sway_layer_subsurface *subsurface =
447 calloc(1, sizeof(struct sway_layer_subsurface));
448 if (subsurface == NULL) {
449 return NULL;
450 } 227 }
451 228
452 subsurface->wlr_subsurface = wlr_subsurface; 229 uint32_t committed = layer_surface->current.committed;
453 subsurface->layer_surface = layer_surface; 230 if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) {
454 wl_list_insert(&layer_surface->subsurfaces, &subsurface->link); 231 enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer;
455 232 struct wlr_scene_tree *output_layer = sway_layer_get_scene(
456 subsurface->map.notify = subsurface_handle_map; 233 surface->output, layer_type);
457 wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); 234 wlr_scene_node_reparent(&surface->scene->tree->node, output_layer);
458 subsurface->unmap.notify = subsurface_handle_unmap; 235 }
459 wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap);
460 subsurface->destroy.notify = subsurface_handle_destroy;
461 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
462 subsurface->commit.notify = subsurface_handle_commit;
463 wl_signal_add(&wlr_subsurface->surface->events.commit, &subsurface->commit);
464
465 return subsurface;
466}
467
468static void handle_new_subsurface(struct wl_listener *listener, void *data) {
469 struct sway_layer_surface *sway_layer_surface =
470 wl_container_of(listener, sway_layer_surface, new_subsurface);
471 struct wlr_subsurface *wlr_subsurface = data;
472 create_subsurface(wlr_subsurface, sway_layer_surface);
473}
474
475 236
476static struct sway_layer_surface *popup_get_layer( 237 if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) {
477 struct sway_layer_popup *popup) { 238 surface->mapped = layer_surface->surface->mapped;
478 while (popup->parent_type == LAYER_PARENT_POPUP) { 239 arrange_layers(surface->output);
479 popup = popup->parent_popup; 240 transaction_commit_dirty();
480 } 241 }
481 return popup->parent_layer;
482} 242}
483 243
484static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { 244static void handle_map(struct wl_listener *listener, void *data) {
485 struct wlr_xdg_popup *popup = layer_popup->wlr_popup; 245 struct sway_layer_surface *surface = wl_container_of(listener,
486 struct wlr_surface *surface = popup->base->surface; 246 surface, map);
487 int popup_sx = popup->current.geometry.x - popup->base->current.geometry.x; 247
488 int popup_sy = popup->current.geometry.y - popup->base->current.geometry.y; 248 struct wlr_layer_surface_v1 *layer_surface =
489 int ox = popup_sx, oy = popup_sy; 249 surface->scene->layer_surface;
490 struct sway_layer_surface *layer; 250
491 while (true) { 251 // focus on new surface
492 if (layer_popup->parent_type == LAYER_PARENT_POPUP) { 252 if (layer_surface->current.keyboard_interactive &&
493 layer_popup = layer_popup->parent_popup; 253 (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY ||
494 ox += layer_popup->wlr_popup->current.geometry.x; 254 layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) {
495 oy += layer_popup->wlr_popup->current.geometry.y; 255 struct sway_seat *seat;
496 } else { 256 wl_list_for_each(seat, &server.input->seats, link) {
497 layer = layer_popup->parent_layer; 257 // but only if the currently focused layer has a lower precedence
498 ox += layer->geo.x; 258 if (!seat->focused_layer ||
499 oy += layer->geo.y; 259 seat->focused_layer->current.layer >= layer_surface->current.layer) {
500 break; 260 seat_set_focus_layer(seat, layer_surface);
261 }
501 } 262 }
263 arrange_layers(surface->output);
502 } 264 }
503 struct wlr_output *wlr_output = layer->layer_surface->output;
504 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
505 struct sway_output *output = wlr_output->data;
506 output_damage_surface(output, ox, oy, surface, whole);
507}
508 265
509static void popup_handle_map(struct wl_listener *listener, void *data) { 266 cursor_rebase_all();
510 struct sway_layer_popup *popup = wl_container_of(listener, popup, map);
511 struct sway_layer_surface *layer = popup_get_layer(popup);
512 struct wlr_output *wlr_output = layer->layer_surface->output;
513 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
514 wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output);
515 popup_damage(popup, true);
516} 267}
517 268
518static void popup_handle_unmap(struct wl_listener *listener, void *data) { 269static void handle_unmap(struct wl_listener *listener, void *data) {
519 struct sway_layer_popup *popup = wl_container_of(listener, popup, unmap); 270 struct sway_layer_surface *surface = wl_container_of(
520 popup_damage(popup, true); 271 listener, surface, unmap);
521} 272 struct sway_seat *seat;
273 wl_list_for_each(seat, &server.input->seats, link) {
274 if (seat->focused_layer == surface->layer_surface) {
275 seat_set_focus_layer(seat, NULL);
276 }
277 }
522 278
523static void popup_handle_commit(struct wl_listener *listener, void *data) { 279 cursor_rebase_all();
524 struct sway_layer_popup *popup = wl_container_of(listener, popup, commit);
525 popup_damage(popup, false);
526} 280}
527 281
528static void popup_handle_destroy(struct wl_listener *listener, void *data) { 282static void popup_handle_destroy(struct wl_listener *listener, void *data) {
529 struct sway_layer_popup *popup = 283 struct sway_layer_popup *popup =
530 wl_container_of(listener, popup, destroy); 284 wl_container_of(listener, popup, destroy);
531 285
532 wl_list_remove(&popup->map.link);
533 wl_list_remove(&popup->unmap.link);
534 wl_list_remove(&popup->destroy.link); 286 wl_list_remove(&popup->destroy.link);
287 wl_list_remove(&popup->new_popup.link);
535 wl_list_remove(&popup->commit.link); 288 wl_list_remove(&popup->commit.link);
536 free(popup); 289 free(popup);
537} 290}
538 291
539static void popup_unconstrain(struct sway_layer_popup *popup) { 292static void popup_unconstrain(struct sway_layer_popup *popup) {
540 struct sway_layer_surface *layer = popup_get_layer(popup);
541 struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; 293 struct wlr_xdg_popup *wlr_popup = popup->wlr_popup;
294 struct sway_output *output = popup->toplevel->output;
542 295
543 struct wlr_output *wlr_output = layer->layer_surface->output; 296 // if a client tries to create a popup while we are in the process of destroying
544 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); 297 // its output, don't crash.
545 struct sway_output *output = wlr_output->data; 298 if (!output) {
299 return;
300 }
301
302 int lx, ly;
303 wlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly);
546 304
547 // the output box expressed in the coordinate system of the toplevel parent 305 // the output box expressed in the coordinate system of the toplevel parent
548 // of the popup 306 // of the popup
549 struct wlr_box output_toplevel_sx_box = { 307 struct wlr_box output_toplevel_sx_box = {
550 .x = -layer->geo.x, 308 .x = output->lx - lx,
551 .y = -layer->geo.y, 309 .y = output->ly - ly,
552 .width = output->width, 310 .width = output->width,
553 .height = output->height, 311 .height = output->height,
554 }; 312 };
@@ -556,32 +314,38 @@ static void popup_unconstrain(struct sway_layer_popup *popup) {
556 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 314 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
557} 315}
558 316
317static void popup_handle_commit(struct wl_listener *listener, void *data) {
318 struct sway_layer_popup *popup = wl_container_of(listener, popup, commit);
319 if (popup->wlr_popup->base->initial_commit) {
320 popup_unconstrain(popup);
321 }
322}
323
559static void popup_handle_new_popup(struct wl_listener *listener, void *data); 324static void popup_handle_new_popup(struct wl_listener *listener, void *data);
560 325
561static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, 326static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup,
562 enum layer_parent parent_type, void *parent) { 327 struct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) {
563 struct sway_layer_popup *popup = 328 struct sway_layer_popup *popup = calloc(1, sizeof(*popup));
564 calloc(1, sizeof(struct sway_layer_popup));
565 if (popup == NULL) { 329 if (popup == NULL) {
566 return NULL; 330 return NULL;
567 } 331 }
568 332
333 popup->toplevel = toplevel;
569 popup->wlr_popup = wlr_popup; 334 popup->wlr_popup = wlr_popup;
570 popup->parent_type = parent_type; 335 popup->scene = wlr_scene_xdg_surface_create(parent,
571 popup->parent_layer = parent; 336 wlr_popup->base);
337
338 if (!popup->scene) {
339 free(popup);
340 return NULL;
341 }
572 342
573 popup->map.notify = popup_handle_map;
574 wl_signal_add(&wlr_popup->base->events.map, &popup->map);
575 popup->unmap.notify = popup_handle_unmap;
576 wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
577 popup->destroy.notify = popup_handle_destroy; 343 popup->destroy.notify = popup_handle_destroy;
578 wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); 344 wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
579 popup->commit.notify = popup_handle_commit;
580 wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
581 popup->new_popup.notify = popup_handle_new_popup; 345 popup->new_popup.notify = popup_handle_new_popup;
582 wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); 346 wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
583 347 popup->commit.notify = popup_handle_commit;
584 popup_unconstrain(popup); 348 wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
585 349
586 return popup; 350 return popup;
587} 351}
@@ -590,19 +354,14 @@ static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
590 struct sway_layer_popup *sway_layer_popup = 354 struct sway_layer_popup *sway_layer_popup =
591 wl_container_of(listener, sway_layer_popup, new_popup); 355 wl_container_of(listener, sway_layer_popup, new_popup);
592 struct wlr_xdg_popup *wlr_popup = data; 356 struct wlr_xdg_popup *wlr_popup = data;
593 create_popup(wlr_popup, LAYER_PARENT_POPUP, sway_layer_popup); 357 create_popup(wlr_popup, sway_layer_popup->toplevel, sway_layer_popup->scene);
594} 358}
595 359
596static void handle_new_popup(struct wl_listener *listener, void *data) { 360static void handle_new_popup(struct wl_listener *listener, void *data) {
597 struct sway_layer_surface *sway_layer_surface = 361 struct sway_layer_surface *sway_layer_surface =
598 wl_container_of(listener, sway_layer_surface, new_popup); 362 wl_container_of(listener, sway_layer_surface, new_popup);
599 struct wlr_xdg_popup *wlr_popup = data; 363 struct wlr_xdg_popup *wlr_popup = data;
600 create_popup(wlr_popup, LAYER_PARENT_LAYER, sway_layer_surface); 364 create_popup(wlr_popup, sway_layer_surface, sway_layer_surface->popups);
601}
602
603struct sway_layer_surface *layer_from_wlr_layer_surface_v1(
604 struct wlr_layer_surface_v1 *layer_surface) {
605 return layer_surface->data;
606} 365}
607 366
608void handle_layer_shell_surface(struct wl_listener *listener, void *data) { 367void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
@@ -634,10 +393,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
634 sway_log(SWAY_ERROR, 393 sway_log(SWAY_ERROR,
635 "no output to auto-assign layer surface '%s' to", 394 "no output to auto-assign layer surface '%s' to",
636 layer_surface->namespace); 395 layer_surface->namespace);
637 // Note that layer_surface->output can be NULL
638 // here, but none of our destroy callbacks are
639 // registered yet so we don't have to make them
640 // handle that case.
641 wlr_layer_surface_v1_destroy(layer_surface); 396 wlr_layer_surface_v1_destroy(layer_surface);
642 return; 397 return;
643 } 398 }
@@ -646,44 +401,57 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
646 layer_surface->output = output->wlr_output; 401 layer_surface->output = output->wlr_output;
647 } 402 }
648 403
649 struct sway_layer_surface *sway_layer = 404 struct sway_output *output = layer_surface->output->data;
650 calloc(1, sizeof(struct sway_layer_surface)); 405
651 if (!sway_layer) { 406 enum zwlr_layer_shell_v1_layer layer_type = layer_surface->pending.layer;
407 struct wlr_scene_tree *output_layer = sway_layer_get_scene(
408 output, layer_type);
409 struct wlr_scene_layer_surface_v1 *scene_surface =
410 wlr_scene_layer_surface_v1_create(output_layer, layer_surface);
411 if (!scene_surface) {
412 sway_log(SWAY_ERROR, "Could not allocate a layer_surface_v1");
652 return; 413 return;
653 } 414 }
654 415
655 wl_list_init(&sway_layer->subsurfaces); 416 struct sway_layer_surface *surface =
417 sway_layer_surface_create(scene_surface);
418 if (!surface) {
419 wlr_layer_surface_v1_destroy(layer_surface);
656 420
657 sway_layer->surface_commit.notify = handle_surface_commit; 421 sway_log(SWAY_ERROR, "Could not allocate a sway_layer_surface");
658 wl_signal_add(&layer_surface->surface->events.commit, 422 return;
659 &sway_layer->surface_commit); 423 }
660
661 sway_layer->destroy.notify = handle_destroy;
662 wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy);
663 sway_layer->map.notify = handle_map;
664 wl_signal_add(&layer_surface->events.map, &sway_layer->map);
665 sway_layer->unmap.notify = handle_unmap;
666 wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap);
667 sway_layer->new_popup.notify = handle_new_popup;
668 wl_signal_add(&layer_surface->events.new_popup, &sway_layer->new_popup);
669 sway_layer->new_subsurface.notify = handle_new_subsurface;
670 wl_signal_add(&layer_surface->surface->events.new_subsurface,
671 &sway_layer->new_subsurface);
672
673 sway_layer->layer_surface = layer_surface;
674 layer_surface->data = sway_layer;
675 424
676 struct sway_output *output = layer_surface->output->data; 425 if (!scene_descriptor_assign(&scene_surface->tree->node,
677 sway_layer->output_destroy.notify = handle_output_destroy; 426 SWAY_SCENE_DESC_LAYER_SHELL, surface)) {
678 wl_signal_add(&output->events.disable, &sway_layer->output_destroy); 427 sway_log(SWAY_ERROR, "Failed to allocate a layer surface descriptor");
679 428 // destroying the layer_surface will also destroy its corresponding
680 wl_list_insert(&output->layers[layer_surface->pending.layer], 429 // scene node
681 &sway_layer->link); 430 wlr_layer_surface_v1_destroy(layer_surface);
682 431 return;
683 // Temporarily set the layer's current state to pending 432 }
684 // So that we can easily arrange it 433
685 struct wlr_layer_surface_v1_state old_state = layer_surface->current; 434 surface->output = output;
686 layer_surface->current = layer_surface->pending; 435
687 arrange_layers(output); 436 // now that the surface's output is known, we can advertise its scale
688 layer_surface->current = old_state; 437 wlr_fractional_scale_v1_notify_scale(surface->layer_surface->surface,
438 layer_surface->output->scale);
439 wlr_surface_set_preferred_buffer_scale(surface->layer_surface->surface,
440 ceil(layer_surface->output->scale));
441
442 surface->surface_commit.notify = handle_surface_commit;
443 wl_signal_add(&layer_surface->surface->events.commit,
444 &surface->surface_commit);
445 surface->map.notify = handle_map;
446 wl_signal_add(&layer_surface->surface->events.map, &surface->map);
447 surface->unmap.notify = handle_unmap;
448 wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap);
449 surface->new_popup.notify = handle_new_popup;
450 wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup);
451
452 surface->output_destroy.notify = handle_output_destroy;
453 wl_signal_add(&output->events.disable, &surface->output_destroy);
454
455 surface->node_destroy.notify = handle_node_destroy;
456 wl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy);
689} 457}
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 7bb9dab2..2722e556 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -1,44 +1,61 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <strings.h> 3#include <strings.h>
5#include <time.h> 4#include <time.h>
6#include <wayland-server-core.h> 5#include <wayland-server-core.h>
7#include <wlr/backend/drm.h> 6#include <wlr/config.h>
8#include <wlr/backend/headless.h> 7#include <wlr/backend/headless.h>
8#include <wlr/render/swapchain.h>
9#include <wlr/render/wlr_renderer.h> 9#include <wlr/render/wlr_renderer.h>
10#include <wlr/types/wlr_buffer.h> 10#include <wlr/types/wlr_buffer.h>
11#include <wlr/types/wlr_drm_lease_v1.h> 11#include <wlr/types/wlr_gamma_control_v1.h>
12#include <wlr/types/wlr_matrix.h> 12#include <wlr/types/wlr_matrix.h>
13#include <wlr/types/wlr_output_damage.h>
14#include <wlr/types/wlr_output_layout.h> 13#include <wlr/types/wlr_output_layout.h>
14#include <wlr/types/wlr_output_management_v1.h>
15#include <wlr/types/wlr_output_power_management_v1.h>
15#include <wlr/types/wlr_output.h> 16#include <wlr/types/wlr_output.h>
16#include <wlr/types/wlr_presentation_time.h> 17#include <wlr/types/wlr_presentation_time.h>
17#include <wlr/types/wlr_compositor.h> 18#include <wlr/types/wlr_compositor.h>
18#include <wlr/util/region.h> 19#include <wlr/util/region.h>
20#include <wlr/util/transform.h>
19#include "config.h" 21#include "config.h"
20#include "log.h" 22#include "log.h"
21#include "sway/config.h" 23#include "sway/config.h"
22#include "sway/desktop/transaction.h" 24#include "sway/desktop/transaction.h"
23#include "sway/input/input-manager.h" 25#include "sway/input/input-manager.h"
24#include "sway/input/seat.h" 26#include "sway/input/seat.h"
27#include "sway/ipc-server.h"
25#include "sway/layers.h" 28#include "sway/layers.h"
26#include "sway/output.h" 29#include "sway/output.h"
30#include "sway/scene_descriptor.h"
27#include "sway/server.h" 31#include "sway/server.h"
28#include "sway/surface.h"
29#include "sway/tree/arrange.h" 32#include "sway/tree/arrange.h"
30#include "sway/tree/container.h" 33#include "sway/tree/container.h"
31#include "sway/tree/root.h" 34#include "sway/tree/root.h"
32#include "sway/tree/view.h" 35#include "sway/tree/view.h"
33#include "sway/tree/workspace.h" 36#include "sway/tree/workspace.h"
34 37
38#if WLR_HAS_DRM_BACKEND
39#include <wlr/backend/drm.h>
40#include <wlr/types/wlr_drm_lease_v1.h>
41#endif
42
43bool output_match_name_or_id(struct sway_output *output,
44 const char *name_or_id) {
45 if (strcmp(name_or_id, "*") == 0) {
46 return true;
47 }
48
49 char identifier[128];
50 output_get_identifier(identifier, sizeof(identifier), output);
51 return strcasecmp(identifier, name_or_id) == 0
52 || strcasecmp(output->wlr_output->name, name_or_id) == 0;
53}
54
35struct sway_output *output_by_name_or_id(const char *name_or_id) { 55struct sway_output *output_by_name_or_id(const char *name_or_id) {
36 for (int i = 0; i < root->outputs->length; ++i) { 56 for (int i = 0; i < root->outputs->length; ++i) {
37 struct sway_output *output = root->outputs->items[i]; 57 struct sway_output *output = root->outputs->items[i];
38 char identifier[128]; 58 if (output_match_name_or_id(output, name_or_id)) {
39 output_get_identifier(identifier, sizeof(identifier), output);
40 if (strcasecmp(identifier, name_or_id) == 0
41 || strcasecmp(output->wlr_output->name, name_or_id) == 0) {
42 return output; 59 return output;
43 } 60 }
44 } 61 }
@@ -48,536 +65,217 @@ struct sway_output *output_by_name_or_id(const char *name_or_id) {
48struct sway_output *all_output_by_name_or_id(const char *name_or_id) { 65struct sway_output *all_output_by_name_or_id(const char *name_or_id) {
49 struct sway_output *output; 66 struct sway_output *output;
50 wl_list_for_each(output, &root->all_outputs, link) { 67 wl_list_for_each(output, &root->all_outputs, link) {
51 char identifier[128]; 68 if (output_match_name_or_id(output, name_or_id)) {
52 output_get_identifier(identifier, sizeof(identifier), output);
53 if (strcasecmp(identifier, name_or_id) == 0
54 || strcasecmp(output->wlr_output->name, name_or_id) == 0) {
55 return output; 69 return output;
56 } 70 }
57 } 71 }
58 return NULL; 72 return NULL;
59} 73}
60 74
61struct surface_iterator_data {
62 sway_surface_iterator_func_t user_iterator;
63 void *user_data;
64
65 struct sway_output *output;
66 struct sway_view *view;
67 double ox, oy;
68 int width, height;
69};
70
71static bool get_surface_box(struct surface_iterator_data *data,
72 struct wlr_surface *surface, int sx, int sy,
73 struct wlr_box *surface_box) {
74 struct sway_output *output = data->output;
75
76 if (!wlr_surface_has_buffer(surface)) {
77 return false;
78 }
79 75
80 int sw = surface->current.width; 76struct sway_workspace *output_get_active_workspace(struct sway_output *output) {
81 int sh = surface->current.height; 77 struct sway_seat *seat = input_manager_current_seat();
82 78 struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node);
83 struct wlr_box box = { 79 if (!focus) {
84 .x = floor(data->ox + sx), 80 if (!output->workspaces->length) {
85 .y = floor(data->oy + sy), 81 return NULL;
86 .width = sw, 82 }
87 .height = sh, 83 return output->workspaces->items[0];
88 };
89 if (surface_box != NULL) {
90 memcpy(surface_box, &box, sizeof(struct wlr_box));
91 } 84 }
92 85 return focus->sway_workspace;
93 struct wlr_box output_box = {
94 .width = output->width,
95 .height = output->height,
96 };
97
98 struct wlr_box intersection;
99 return wlr_box_intersection(&intersection, &output_box, &box);
100} 86}
101 87
102static void output_for_each_surface_iterator(struct wlr_surface *surface, 88struct send_frame_done_data {
103 int sx, int sy, void *_data) { 89 struct timespec when;
104 struct surface_iterator_data *data = _data; 90 int msec_until_refresh;
105 91 struct sway_output *output;
106 struct wlr_box box; 92};
107 bool intersects = get_surface_box(data, surface, sx, sy, &box);
108 if (!intersects) {
109 return;
110 }
111 93
112 data->user_iterator(data->output, data->view, surface, &box, 94struct buffer_timer {
113 data->user_data); 95 struct wl_listener destroy;
114} 96 struct wl_event_source *frame_done_timer;
97};
115 98
116void output_surface_for_each_surface(struct sway_output *output, 99static int handle_buffer_timer(void *data) {
117 struct wlr_surface *surface, double ox, double oy, 100 struct wlr_scene_buffer *buffer = data;
118 sway_surface_iterator_func_t iterator, void *user_data) {
119 struct surface_iterator_data data = {
120 .user_iterator = iterator,
121 .user_data = user_data,
122 .output = output,
123 .view = NULL,
124 .ox = ox,
125 .oy = oy,
126 .width = surface->current.width,
127 .height = surface->current.height,
128 };
129
130 wlr_surface_for_each_surface(surface,
131 output_for_each_surface_iterator, &data);
132}
133 101
134void output_view_for_each_surface(struct sway_output *output, 102 struct timespec now;
135 struct sway_view *view, sway_surface_iterator_func_t iterator, 103 clock_gettime(CLOCK_MONOTONIC, &now);
136 void *user_data) { 104 wlr_scene_buffer_send_frame_done(buffer, &now);
137 struct surface_iterator_data data = { 105 return 0;
138 .user_iterator = iterator,
139 .user_data = user_data,
140 .output = output,
141 .view = view,
142 .ox = view->container->surface_x - output->lx
143 - view->geometry.x,
144 .oy = view->container->surface_y - output->ly
145 - view->geometry.y,
146 .width = view->container->current.content_width,
147 .height = view->container->current.content_height,
148 };
149
150 view_for_each_surface(view, output_for_each_surface_iterator, &data);
151} 106}
152 107
153void output_view_for_each_popup_surface(struct sway_output *output, 108static void handle_buffer_timer_destroy(struct wl_listener *listener,
154 struct sway_view *view, sway_surface_iterator_func_t iterator, 109 void *data) {
155 void *user_data) { 110 struct buffer_timer *timer = wl_container_of(listener, timer, destroy);
156 struct surface_iterator_data data = {
157 .user_iterator = iterator,
158 .user_data = user_data,
159 .output = output,
160 .view = view,
161 .ox = view->container->surface_x - output->lx
162 - view->geometry.x,
163 .oy = view->container->surface_y - output->ly
164 - view->geometry.y,
165 .width = view->container->current.content_width,
166 .height = view->container->current.content_height,
167 };
168
169 view_for_each_popup_surface(view, output_for_each_surface_iterator, &data);
170}
171 111
172void output_layer_for_each_surface(struct sway_output *output, 112 wl_list_remove(&timer->destroy.link);
173 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 113 wl_event_source_remove(timer->frame_done_timer);
174 void *user_data) { 114 free(timer);
175 struct sway_layer_surface *layer_surface;
176 wl_list_for_each(layer_surface, layer_surfaces, link) {
177 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
178 layer_surface->layer_surface;
179 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
180 struct surface_iterator_data data = {
181 .user_iterator = iterator,
182 .user_data = user_data,
183 .output = output,
184 .view = NULL,
185 .ox = layer_surface->geo.x,
186 .oy = layer_surface->geo.y,
187 .width = surface->current.width,
188 .height = surface->current.height,
189 };
190 wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1,
191 output_for_each_surface_iterator, &data);
192 }
193} 115}
194 116
195void output_layer_for_each_toplevel_surface(struct sway_output *output, 117static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_buffer *buffer) {
196 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 118 struct buffer_timer *timer =
197 void *user_data) { 119 scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER);
198 struct sway_layer_surface *layer_surface; 120 if (timer) {
199 wl_list_for_each(layer_surface, layer_surfaces, link) { 121 return timer;
200 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
201 layer_surface->layer_surface;
202 output_surface_for_each_surface(output, wlr_layer_surface_v1->surface,
203 layer_surface->geo.x, layer_surface->geo.y, iterator,
204 user_data);
205 } 122 }
206}
207
208 123
209void output_layer_for_each_popup_surface(struct sway_output *output, 124 timer = calloc(1, sizeof(struct buffer_timer));
210 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 125 if (!timer) {
211 void *user_data) { 126 return NULL;
212 struct sway_layer_surface *layer_surface;
213 wl_list_for_each(layer_surface, layer_surfaces, link) {
214 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
215 layer_surface->layer_surface;
216 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
217 struct surface_iterator_data data = {
218 .user_iterator = iterator,
219 .user_data = user_data,
220 .output = output,
221 .view = NULL,
222 .ox = layer_surface->geo.x,
223 .oy = layer_surface->geo.y,
224 .width = surface->current.width,
225 .height = surface->current.height,
226 };
227 wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1,
228 output_for_each_surface_iterator, &data);
229 } 127 }
230}
231 128
232#if HAVE_XWAYLAND 129 timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop,
233void output_unmanaged_for_each_surface(struct sway_output *output, 130 handle_buffer_timer, buffer);
234 struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, 131 if (!timer->frame_done_timer) {
235 void *user_data) { 132 free(timer);
236 struct sway_xwayland_unmanaged *unmanaged_surface; 133 return NULL;
237 wl_list_for_each(unmanaged_surface, unmanaged, link) {
238 struct wlr_xwayland_surface *xsurface =
239 unmanaged_surface->wlr_xwayland_surface;
240 double ox = unmanaged_surface->lx - output->lx;
241 double oy = unmanaged_surface->ly - output->ly;
242
243 output_surface_for_each_surface(output, xsurface->surface, ox, oy,
244 iterator, user_data);
245 } 134 }
246}
247#endif
248 135
249void output_drag_icons_for_each_surface(struct sway_output *output, 136 scene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer);
250 struct wl_list *drag_icons, sway_surface_iterator_func_t iterator,
251 void *user_data) {
252 struct sway_drag_icon *drag_icon;
253 wl_list_for_each(drag_icon, drag_icons, link) {
254 double ox = drag_icon->x - output->lx;
255 double oy = drag_icon->y - output->ly;
256
257 if (drag_icon->wlr_drag_icon->mapped) {
258 output_surface_for_each_surface(output,
259 drag_icon->wlr_drag_icon->surface, ox, oy,
260 iterator, user_data);
261 }
262 }
263}
264 137
265static void for_each_surface_container_iterator(struct sway_container *con, 138 timer->destroy.notify = handle_buffer_timer_destroy;
266 void *_data) { 139 wl_signal_add(&buffer->node.events.destroy, &timer->destroy);
267 if (!con->view || !view_is_visible(con->view)) {
268 return;
269 }
270 140
271 struct surface_iterator_data *data = _data; 141 return timer;
272 output_view_for_each_surface(data->output, con->view,
273 data->user_iterator, data->user_data);
274} 142}
275 143
276static void output_for_each_surface(struct sway_output *output, 144static void send_frame_done_iterator(struct wlr_scene_buffer *buffer,
277 sway_surface_iterator_func_t iterator, void *user_data) { 145 int x, int y, void *user_data) {
278 if (server.session_lock.locked) { 146 struct send_frame_done_data *data = user_data;
279 if (server.session_lock.lock == NULL) { 147 struct sway_output *output = data->output;
280 return; 148 int view_max_render_time = 0;
281 }
282 struct wlr_session_lock_surface_v1 *lock_surface;
283 wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) {
284 if (lock_surface->output != output->wlr_output) {
285 continue;
286 }
287 if (!lock_surface->mapped) {
288 continue;
289 }
290 149
291 output_surface_for_each_surface(output, lock_surface->surface, 150 if (buffer->primary_output != data->output->scene_output) {
292 0.0, 0.0, iterator, user_data);
293 }
294 return; 151 return;
295 } 152 }
296 153
297 if (output_has_opaque_overlay_layer_surface(output)) { 154 struct wlr_scene_node *current = &buffer->node;
298 goto overlay; 155 while (true) {
299 } 156 struct sway_view *view = scene_descriptor_try_get(current,
300 157 SWAY_SCENE_DESC_VIEW);
301 struct surface_iterator_data data = { 158 if (view) {
302 .user_iterator = iterator, 159 view_max_render_time = view->max_render_time;
303 .user_data = user_data, 160 break;
304 .output = output,
305 .view = NULL,
306 };
307
308 struct sway_workspace *workspace = output_get_active_workspace(output);
309 struct sway_container *fullscreen_con = root->fullscreen_global;
310 if (!fullscreen_con) {
311 if (!workspace) {
312 return;
313 }
314 fullscreen_con = workspace->current.fullscreen;
315 }
316 if (fullscreen_con) {
317 for_each_surface_container_iterator(fullscreen_con, &data);
318 container_for_each_child(fullscreen_con,
319 for_each_surface_container_iterator, &data);
320
321 // TODO: Show transient containers for fullscreen global
322 if (fullscreen_con == workspace->current.fullscreen) {
323 for (int i = 0; i < workspace->current.floating->length; ++i) {
324 struct sway_container *floater =
325 workspace->current.floating->items[i];
326 if (container_is_transient_for(floater, fullscreen_con)) {
327 for_each_surface_container_iterator(floater, &data);
328 }
329 }
330 } 161 }
331#if HAVE_XWAYLAND
332 output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged,
333 iterator, user_data);
334#endif
335 } else {
336 output_layer_for_each_surface(output,
337 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
338 iterator, user_data);
339 output_layer_for_each_surface(output,
340 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
341 iterator, user_data);
342
343 workspace_for_each_container(workspace,
344 for_each_surface_container_iterator, &data);
345
346#if HAVE_XWAYLAND
347 output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged,
348 iterator, user_data);
349#endif
350 output_layer_for_each_surface(output,
351 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
352 iterator, user_data);
353 }
354
355overlay:
356 output_layer_for_each_surface(output,
357 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
358 iterator, user_data);
359 output_drag_icons_for_each_surface(output, &root->drag_icons,
360 iterator, user_data);
361}
362
363static int scale_length(int length, int offset, float scale) {
364 return round((offset + length) * scale) - round(offset * scale);
365}
366 162
367void scale_box(struct wlr_box *box, float scale) { 163 if (!current->parent) {
368 box->width = scale_length(box->width, box->x, scale); 164 break;
369 box->height = scale_length(box->height, box->y, scale);
370 box->x = round(box->x * scale);
371 box->y = round(box->y * scale);
372}
373
374struct sway_workspace *output_get_active_workspace(struct sway_output *output) {
375 struct sway_seat *seat = input_manager_current_seat();
376 struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node);
377 if (!focus) {
378 if (!output->workspaces->length) {
379 return NULL;
380 } 165 }
381 return output->workspaces->items[0];
382 }
383 return focus->sway_workspace;
384}
385 166
386bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { 167 current = &current->parent->node;
387 struct sway_layer_surface *sway_layer_surface;
388 wl_list_for_each(sway_layer_surface,
389 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) {
390 struct wlr_surface *wlr_surface = sway_layer_surface->layer_surface->surface;
391 pixman_box32_t output_box = {
392 .x2 = output->width,
393 .y2 = output->height,
394 };
395 pixman_region32_t surface_opaque_box;
396 pixman_region32_init(&surface_opaque_box);
397 pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region);
398 pixman_region32_translate(&surface_opaque_box,
399 sway_layer_surface->geo.x, sway_layer_surface->geo.y);
400 pixman_region_overlap_t contains =
401 pixman_region32_contains_rectangle(&surface_opaque_box, &output_box);
402 pixman_region32_fini(&surface_opaque_box);
403
404 if (contains == PIXMAN_REGION_IN) {
405 return true;
406 }
407 } 168 }
408 return false;
409}
410
411struct send_frame_done_data {
412 struct timespec when;
413 int msec_until_refresh;
414};
415
416static void send_frame_done_iterator(struct sway_output *output,
417 struct sway_view *view, struct wlr_surface *surface,
418 struct wlr_box *box, void *user_data) {
419 int view_max_render_time = 0;
420 if (view != NULL) {
421 view_max_render_time = view->max_render_time;
422 }
423
424 struct send_frame_done_data *data = user_data;
425 169
426 int delay = data->msec_until_refresh - output->max_render_time 170 int delay = data->msec_until_refresh - output->max_render_time
427 - view_max_render_time; 171 - view_max_render_time;
428 172
429 if (output->max_render_time == 0 || view_max_render_time == 0 || delay < 1) { 173 struct buffer_timer *timer = NULL;
430 wlr_surface_send_frame_done(surface, &data->when);
431 } else {
432 struct sway_surface *sway_surface = surface->data;
433 wl_event_source_timer_update(sway_surface->frame_done_timer, delay);
434 }
435}
436 174
437static void send_frame_done(struct sway_output *output, struct send_frame_done_data *data) { 175 if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) {
438 output_for_each_surface(output, send_frame_done_iterator, data); 176 timer = buffer_timer_get_or_create(buffer);
439}
440
441static void count_surface_iterator(struct sway_output *output,
442 struct sway_view *view, struct wlr_surface *surface,
443 struct wlr_box *box, void *data) {
444 size_t *n = data;
445 (*n)++;
446}
447
448static bool scan_out_fullscreen_view(struct sway_output *output,
449 struct sway_view *view) {
450 struct wlr_output *wlr_output = output->wlr_output;
451 struct sway_workspace *workspace = output->current.active_workspace;
452 if (!sway_assert(workspace, "Expected an active workspace")) {
453 return false;
454 }
455
456 if (server.session_lock.locked) {
457 return false;
458 } 177 }
459 178
460 if (!wl_list_empty(&view->saved_buffers)) { 179 if (timer) {
461 return false; 180 wl_event_source_timer_update(timer->frame_done_timer, delay);
181 } else {
182 wlr_scene_buffer_send_frame_done(buffer, &data->when);
462 } 183 }
184}
463 185
464 for (int i = 0; i < workspace->current.floating->length; ++i) { 186static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output,
465 struct sway_container *floater = 187 struct wlr_scene_buffer *buffer) {
466 workspace->current.floating->items[i]; 188 // if we are scaling down, we should always choose linear
467 if (container_is_transient_for(floater, view->container)) { 189 if (buffer->dst_width > 0 && buffer->dst_height > 0 && (
468 return false; 190 buffer->dst_width < buffer->buffer_width ||
469 } 191 buffer->dst_height < buffer->buffer_height)) {
192 return WLR_SCALE_FILTER_BILINEAR;
470 } 193 }
471 194
472#if HAVE_XWAYLAND 195 switch (output->scale_filter) {
473 if (!wl_list_empty(&root->xwayland_unmanaged)) { 196 case SCALE_FILTER_LINEAR:
474 return false; 197 return WLR_SCALE_FILTER_BILINEAR;
198 case SCALE_FILTER_NEAREST:
199 return WLR_SCALE_FILTER_NEAREST;
200 default:
201 abort(); // unreachable
475 } 202 }
476#endif 203}
477 204
478 if (!wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) { 205static void output_configure_scene(struct sway_output *output,
479 return false; 206 struct wlr_scene_node *node, float opacity) {
480 } 207 if (!node->enabled) {
481 if (!wl_list_empty(&root->drag_icons)) { 208 return;
482 return false;
483 } 209 }
484 210
485 struct wlr_surface *surface = view->surface; 211 struct sway_container *con =
486 if (surface == NULL) { 212 scene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER);
487 return false; 213 if (con) {
488 } 214 opacity = con->alpha;
489 size_t n_surfaces = 0;
490 output_view_for_each_surface(output, view,
491 count_surface_iterator, &n_surfaces);
492 if (n_surfaces != 1) {
493 return false;
494 } 215 }
495 216
496 if (surface->buffer == NULL) { 217 if (node->type == WLR_SCENE_NODE_BUFFER) {
497 return false; 218 struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
498 }
499 219
500 if ((float)surface->current.scale != wlr_output->scale || 220 // hack: don't call the scene setter because that will damage all outputs
501 surface->current.transform != wlr_output->transform) { 221 // We don't want to damage outputs that aren't our current output that
502 return false; 222 // we're configuring
503 } 223 buffer->filter_mode = get_scale_filter(output, buffer);
504 224
505 wlr_output_attach_buffer(wlr_output, &surface->buffer->base); 225 wlr_scene_buffer_set_opacity(buffer, opacity);
506 if (!wlr_output_test(wlr_output)) { 226 } else if (node->type == WLR_SCENE_NODE_TREE) {
507 return false; 227 struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node);
228 struct wlr_scene_node *node;
229 wl_list_for_each(node, &tree->children, link) {
230 output_configure_scene(output, node, opacity);
231 }
508 } 232 }
509
510 wlr_presentation_surface_sampled_on_output(server.presentation, surface,
511 wlr_output);
512
513 return wlr_output_commit(wlr_output);
514} 233}
515 234
516static int output_repaint_timer_handler(void *data) { 235static int output_repaint_timer_handler(void *data) {
517 struct sway_output *output = data; 236 struct sway_output *output = data;
518 if (output->wlr_output == NULL) {
519 return 0;
520 }
521 237
522 output->wlr_output->frame_pending = false; 238 if (!output->enabled) {
523
524 struct sway_workspace *workspace = output->current.active_workspace;
525 if (workspace == NULL) {
526 return 0; 239 return 0;
527 } 240 }
528 241
529 struct sway_container *fullscreen_con = root->fullscreen_global; 242 output->wlr_output->frame_pending = false;
530 if (!fullscreen_con) {
531 fullscreen_con = workspace->current.fullscreen;
532 }
533 243
534 if (fullscreen_con && fullscreen_con->view && !debug.noscanout) { 244 output_configure_scene(output, &root->root_scene->tree.node, 1.0f);
535 // Try to scan-out the fullscreen view
536 static bool last_scanned_out = false;
537 bool scanned_out =
538 scan_out_fullscreen_view(output, fullscreen_con->view);
539 245
540 if (scanned_out && !last_scanned_out) { 246 if (output->gamma_lut_changed) {
541 sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", 247 struct wlr_output_state pending;
542 output->wlr_output->name); 248 wlr_output_state_init(&pending);
249 if (!wlr_scene_output_build_state(output->scene_output, &pending, NULL)) {
250 return 0;
543 } 251 }
544 if (last_scanned_out && !scanned_out) { 252
545 sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", 253 output->gamma_lut_changed = false;
546 output->wlr_output->name); 254 struct wlr_gamma_control_v1 *gamma_control =
547 output_damage_whole(output); 255 wlr_gamma_control_manager_v1_get_control(
256 server.gamma_control_manager_v1, output->wlr_output);
257 if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) {
258 wlr_output_state_finish(&pending);
259 return 0;
548 } 260 }
549 last_scanned_out = scanned_out;
550 261
551 if (scanned_out) { 262 if (!wlr_output_commit_state(output->wlr_output, &pending)) {
263 wlr_gamma_control_v1_send_failed_and_destroy(gamma_control);
264 wlr_output_state_finish(&pending);
552 return 0; 265 return 0;
553 } 266 }
554 }
555 267
556 bool needs_frame; 268 wlr_output_state_finish(&pending);
557 pixman_region32_t damage;
558 pixman_region32_init(&damage);
559 if (!wlr_output_damage_attach_render(output->damage,
560 &needs_frame, &damage)) {
561 return 0; 269 return 0;
562 } 270 }
563 271
564 if (needs_frame) { 272 wlr_scene_output_commit(output->scene_output, NULL);
565 struct timespec now;
566 clock_gettime(CLOCK_MONOTONIC, &now);
567
568 output_render(output, &now, &damage);
569 } else {
570 wlr_output_rollback(output->wlr_output);
571 }
572
573 pixman_region32_fini(&damage);
574
575 return 0; 273 return 0;
576} 274}
577 275
578static void damage_handle_frame(struct wl_listener *listener, void *user_data) { 276static void handle_frame(struct wl_listener *listener, void *user_data) {
579 struct sway_output *output = 277 struct sway_output *output =
580 wl_container_of(listener, output, damage_frame); 278 wl_container_of(listener, output, frame);
581 if (!output->enabled || !output->wlr_output->enabled) { 279 if (!output->enabled || !output->wlr_output->enabled) {
582 return; 280 return;
583 } 281 }
@@ -588,9 +286,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) {
588 286
589 if (output->max_render_time != 0) { 287 if (output->max_render_time != 0) {
590 struct timespec now; 288 struct timespec now;
591 clockid_t presentation_clock 289 clock_gettime(CLOCK_MONOTONIC, &now);
592 = wlr_backend_get_presentation_clock(server.backend);
593 clock_gettime(presentation_clock, &now);
594 290
595 const long NSEC_IN_SECONDS = 1000000000; 291 const long NSEC_IN_SECONDS = 1000000000;
596 struct timespec predicted_refresh = output->last_presentation; 292 struct timespec predicted_refresh = output->last_presentation;
@@ -637,116 +333,8 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) {
637 struct send_frame_done_data data = {0}; 333 struct send_frame_done_data data = {0};
638 clock_gettime(CLOCK_MONOTONIC, &data.when); 334 clock_gettime(CLOCK_MONOTONIC, &data.when);
639 data.msec_until_refresh = msec_until_refresh; 335 data.msec_until_refresh = msec_until_refresh;
640 send_frame_done(output, &data); 336 data.output = output;
641} 337 wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data);
642
643void output_damage_whole(struct sway_output *output) {
644 // The output can exist with no wlr_output if it's just been disconnected
645 // and the transaction to evacuate it has't completed yet.
646 if (output && output->wlr_output && output->damage) {
647 wlr_output_damage_add_whole(output->damage);
648 }
649}
650
651static void damage_surface_iterator(struct sway_output *output,
652 struct sway_view *view, struct wlr_surface *surface,
653 struct wlr_box *_box, void *_data) {
654 bool *data = _data;
655 bool whole = *data;
656
657 struct wlr_box box = *_box;
658 scale_box(&box, output->wlr_output->scale);
659
660 pixman_region32_t damage;
661 pixman_region32_init(&damage);
662 wlr_surface_get_effective_damage(surface, &damage);
663 wlr_region_scale(&damage, &damage, output->wlr_output->scale);
664 if (ceil(output->wlr_output->scale) > surface->current.scale) {
665 // When scaling up a surface, it'll become blurry so we need to
666 // expand the damage region
667 wlr_region_expand(&damage, &damage,
668 ceil(output->wlr_output->scale) - surface->current.scale);
669 }
670 pixman_region32_translate(&damage, box.x, box.y);
671 wlr_output_damage_add(output->damage, &damage);
672 pixman_region32_fini(&damage);
673
674 if (whole) {
675 wlr_output_damage_add_box(output->damage, &box);
676 }
677
678 if (!wl_list_empty(&surface->current.frame_callback_list)) {
679 wlr_output_schedule_frame(output->wlr_output);
680 }
681}
682
683void output_damage_surface(struct sway_output *output, double ox, double oy,
684 struct wlr_surface *surface, bool whole) {
685 output_surface_for_each_surface(output, surface, ox, oy,
686 damage_surface_iterator, &whole);
687}
688
689void output_damage_from_view(struct sway_output *output,
690 struct sway_view *view) {
691 if (!view_is_visible(view)) {
692 return;
693 }
694 bool whole = false;
695 output_view_for_each_surface(output, view, damage_surface_iterator, &whole);
696}
697
698// Expecting an unscaled box in layout coordinates
699void output_damage_box(struct sway_output *output, struct wlr_box *_box) {
700 struct wlr_box box;
701 memcpy(&box, _box, sizeof(struct wlr_box));
702 box.x -= output->lx;
703 box.y -= output->ly;
704 scale_box(&box, output->wlr_output->scale);
705 wlr_output_damage_add_box(output->damage, &box);
706}
707
708static void damage_child_views_iterator(struct sway_container *con,
709 void *data) {
710 if (!con->view || !view_is_visible(con->view)) {
711 return;
712 }
713 struct sway_output *output = data;
714 bool whole = true;
715 output_view_for_each_surface(output, con->view, damage_surface_iterator,
716 &whole);
717}
718
719void output_damage_whole_container(struct sway_output *output,
720 struct sway_container *con) {
721 // Pad the box by 1px, because the width is a double and might be a fraction
722 struct wlr_box box = {
723 .x = con->current.x - output->lx - 1,
724 .y = con->current.y - output->ly - 1,
725 .width = con->current.width + 2,
726 .height = con->current.height + 2,
727 };
728 scale_box(&box, output->wlr_output->scale);
729 wlr_output_damage_add_box(output->damage, &box);
730 // Damage subsurfaces as well, which may extend outside the box
731 if (con->view) {
732 damage_child_views_iterator(con, output);
733 } else {
734 container_for_each_child(con, damage_child_views_iterator, output);
735 }
736}
737
738static void damage_handle_destroy(struct wl_listener *listener, void *data) {
739 struct sway_output *output =
740 wl_container_of(listener, output, damage_destroy);
741 if (!output->enabled) {
742 return;
743 }
744 output_disable(output);
745
746 wl_list_remove(&output->damage_destroy.link);
747 wl_list_remove(&output->damage_frame.link);
748
749 transaction_commit_dirty();
750} 338}
751 339
752static void update_output_manager_config(struct sway_server *server) { 340static void update_output_manager_config(struct sway_server *server) {
@@ -764,33 +352,36 @@ static void update_output_manager_config(struct sway_server *server) {
764 wlr_output_layout_get_box(root->output_layout, 352 wlr_output_layout_get_box(root->output_layout,
765 output->wlr_output, &output_box); 353 output->wlr_output, &output_box);
766 // We mark the output enabled when it's switched off but not disabled 354 // We mark the output enabled when it's switched off but not disabled
767 config_head->state.enabled = output->current_mode != NULL && output->enabled; 355 config_head->state.enabled = !wlr_box_empty(&output_box);
768 config_head->state.mode = output->current_mode; 356 config_head->state.x = output_box.x;
769 if (!wlr_box_empty(&output_box)) { 357 config_head->state.y = output_box.y;
770 config_head->state.x = output_box.x;
771 config_head->state.y = output_box.y;
772 }
773 } 358 }
774 359
775 wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); 360 wlr_output_manager_v1_set_configuration(server->output_manager_v1, config);
361
362 ipc_event_output();
776} 363}
777 364
778static void handle_destroy(struct wl_listener *listener, void *data) { 365static void begin_destroy(struct sway_output *output) {
779 struct sway_output *output = wl_container_of(listener, output, destroy);
780 struct sway_server *server = output->server; 366 struct sway_server *server = output->server;
781 output_begin_destroy(output);
782 367
783 if (output->enabled) { 368 if (output->enabled) {
784 output_disable(output); 369 output_disable(output);
785 } 370 }
786 371
372 output_begin_destroy(output);
373
787 wl_list_remove(&output->link); 374 wl_list_remove(&output->link);
788 375
376 wl_list_remove(&output->layout_destroy.link);
789 wl_list_remove(&output->destroy.link); 377 wl_list_remove(&output->destroy.link);
790 wl_list_remove(&output->commit.link); 378 wl_list_remove(&output->commit.link);
791 wl_list_remove(&output->mode.link);
792 wl_list_remove(&output->present.link); 379 wl_list_remove(&output->present.link);
380 wl_list_remove(&output->frame.link);
381 wl_list_remove(&output->request_state.link);
793 382
383 wlr_scene_output_destroy(output->scene_output);
384 output->scene_output = NULL;
794 output->wlr_output->data = NULL; 385 output->wlr_output->data = NULL;
795 output->wlr_output = NULL; 386 output->wlr_output = NULL;
796 387
@@ -799,34 +390,14 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
799 update_output_manager_config(server); 390 update_output_manager_config(server);
800} 391}
801 392
802static void handle_mode(struct wl_listener *listener, void *data) { 393static void handle_destroy(struct wl_listener *listener, void *data) {
803 struct sway_output *output = wl_container_of(listener, output, mode); 394 struct sway_output *output = wl_container_of(listener, output, destroy);
804 if (!output->enabled && !output->enabling) { 395 begin_destroy(output);
805 struct output_config *oc = find_output_config(output);
806 if (output->wlr_output->current_mode != NULL &&
807 (!oc || oc->enabled)) {
808 // We want to enable this output, but it didn't work last time,
809 // possibly because we hadn't enough CRTCs. Try again now that the
810 // output has a mode.
811 sway_log(SWAY_DEBUG, "Output %s has gained a CRTC, "
812 "trying to enable it", output->wlr_output->name);
813 apply_output_config(oc, output);
814 }
815 return;
816 }
817 if (!output->enabled) {
818 return;
819 }
820 arrange_layers(output);
821 arrange_output(output);
822 transaction_commit_dirty();
823
824 update_output_manager_config(output->server);
825} 396}
826 397
827static void update_textures(struct sway_container *con, void *data) { 398static void handle_layout_destroy(struct wl_listener *listener, void *data) {
828 container_update_title_textures(con); 399 struct sway_output *output = wl_container_of(listener, output, layout_destroy);
829 container_update_marks_textures(con); 400 begin_destroy(output);
830} 401}
831 402
832static void handle_commit(struct wl_listener *listener, void *data) { 403static void handle_commit(struct wl_listener *listener, void *data) {
@@ -837,17 +408,21 @@ static void handle_commit(struct wl_listener *listener, void *data) {
837 return; 408 return;
838 } 409 }
839 410
840 if (event->committed & WLR_OUTPUT_STATE_SCALE) { 411 if (event->state->committed & (
841 output_for_each_container(output, update_textures, NULL); 412 WLR_OUTPUT_STATE_MODE |
842 } 413 WLR_OUTPUT_STATE_TRANSFORM |
843 414 WLR_OUTPUT_STATE_SCALE)) {
844 if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) {
845 arrange_layers(output); 415 arrange_layers(output);
846 arrange_output(output); 416 arrange_output(output);
847 transaction_commit_dirty(); 417 transaction_commit_dirty();
848 418
849 update_output_manager_config(output->server); 419 update_output_manager_config(output->server);
850 } 420 }
421
422 // Next time the output is enabled, try to re-apply the gamma LUT
423 if ((event->state->committed & WLR_OUTPUT_STATE_ENABLED) && !output->wlr_output->enabled) {
424 output->gamma_lut_changed = true;
425 }
851} 426}
852 427
853static void handle_present(struct wl_listener *listener, void *data) { 428static void handle_present(struct wl_listener *listener, void *data) {
@@ -862,6 +437,13 @@ static void handle_present(struct wl_listener *listener, void *data) {
862 output->refresh_nsec = output_event->refresh; 437 output->refresh_nsec = output_event->refresh;
863} 438}
864 439
440static void handle_request_state(struct wl_listener *listener, void *data) {
441 struct sway_output *output =
442 wl_container_of(listener, output, request_state);
443 const struct wlr_output_event_request_state *event = data;
444 wlr_output_commit_state(output->wlr_output, event->state);
445}
446
865static unsigned int last_headless_num = 0; 447static unsigned int last_headless_num = 0;
866 448
867void handle_new_output(struct wl_listener *listener, void *data) { 449void handle_new_output(struct wl_listener *listener, void *data) {
@@ -883,10 +465,14 @@ void handle_new_output(struct wl_listener *listener, void *data) {
883 465
884 if (wlr_output->non_desktop) { 466 if (wlr_output->non_desktop) {
885 sway_log(SWAY_DEBUG, "Not configuring non-desktop output"); 467 sway_log(SWAY_DEBUG, "Not configuring non-desktop output");
468 struct sway_output_non_desktop *non_desktop = output_non_desktop_create(wlr_output);
469#if WLR_HAS_DRM_BACKEND
886 if (server->drm_lease_manager) { 470 if (server->drm_lease_manager) {
887 wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, 471 wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager,
888 wlr_output); 472 wlr_output);
889 } 473 }
474#endif
475 list_add(root->non_desktop_outputs, non_desktop);
890 return; 476 return;
891 } 477 }
892 478
@@ -896,32 +482,46 @@ void handle_new_output(struct wl_listener *listener, void *data) {
896 return; 482 return;
897 } 483 }
898 484
485 // Create the scene output here so we're not accidentally creating one for
486 // the fallback output
487 struct wlr_scene_output *scene_output =
488 wlr_scene_output_create(root->root_scene, wlr_output);
489 if (!scene_output) {
490 sway_log(SWAY_ERROR, "Failed to create a scene output");
491 return;
492 }
493
899 struct sway_output *output = output_create(wlr_output); 494 struct sway_output *output = output_create(wlr_output);
900 if (!output) { 495 if (!output) {
496 sway_log(SWAY_ERROR, "Failed to create a sway output");
497 wlr_scene_output_destroy(scene_output);
901 return; 498 return;
902 } 499 }
500
903 output->server = server; 501 output->server = server;
904 output->damage = wlr_output_damage_create(wlr_output); 502 output->scene_output = scene_output;
905 503
504 wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy);
505 output->layout_destroy.notify = handle_layout_destroy;
906 wl_signal_add(&wlr_output->events.destroy, &output->destroy); 506 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
907 output->destroy.notify = handle_destroy; 507 output->destroy.notify = handle_destroy;
908 wl_signal_add(&wlr_output->events.commit, &output->commit); 508 wl_signal_add(&wlr_output->events.commit, &output->commit);
909 output->commit.notify = handle_commit; 509 output->commit.notify = handle_commit;
910 wl_signal_add(&wlr_output->events.mode, &output->mode);
911 output->mode.notify = handle_mode;
912 wl_signal_add(&wlr_output->events.present, &output->present); 510 wl_signal_add(&wlr_output->events.present, &output->present);
913 output->present.notify = handle_present; 511 output->present.notify = handle_present;
914 wl_signal_add(&output->damage->events.frame, &output->damage_frame); 512 wl_signal_add(&wlr_output->events.frame, &output->frame);
915 output->damage_frame.notify = damage_handle_frame; 513 output->frame.notify = handle_frame;
916 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); 514 wl_signal_add(&wlr_output->events.request_state, &output->request_state);
917 output->damage_destroy.notify = damage_handle_destroy; 515 output->request_state.notify = handle_request_state;
918 516
919 output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, 517 output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop,
920 output_repaint_timer_handler, output); 518 output_repaint_timer_handler, output);
921 519
922 struct output_config *oc = find_output_config(output); 520 if (server->session_lock.lock) {
923 apply_output_config(oc, output); 521 sway_session_lock_add_output(server->session_lock.lock, output);
924 free_output_config(oc); 522 }
523
524 apply_all_output_configs();
925 525
926 transaction_commit_dirty(); 526 transaction_commit_dirty();
927 527
@@ -935,62 +535,104 @@ void handle_output_layout_change(struct wl_listener *listener,
935 update_output_manager_config(server); 535 update_output_manager_config(server);
936} 536}
937 537
538void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) {
539 struct sway_server *server =
540 wl_container_of(listener, server, gamma_control_set_gamma);
541 const struct wlr_gamma_control_manager_v1_set_gamma_event *event = data;
542
543 struct sway_output *output = event->output->data;
544
545 if(!output) {
546 return;
547 }
548
549 output->gamma_lut_changed = true;
550 wlr_output_schedule_frame(output->wlr_output);
551}
552
553static struct output_config *output_config_for_config_head(
554 struct wlr_output_configuration_head_v1 *config_head,
555 struct sway_output *output) {
556 struct output_config *oc = new_output_config(output->wlr_output->name);
557 oc->enabled = config_head->state.enabled;
558 if (!oc->enabled) {
559 return oc;
560 }
561
562 if (config_head->state.mode != NULL) {
563 struct wlr_output_mode *mode = config_head->state.mode;
564 oc->width = mode->width;
565 oc->height = mode->height;
566 oc->refresh_rate = mode->refresh / 1000.f;
567 } else {
568 oc->width = config_head->state.custom_mode.width;
569 oc->height = config_head->state.custom_mode.height;
570 oc->refresh_rate =
571 config_head->state.custom_mode.refresh / 1000.f;
572 }
573 oc->x = config_head->state.x;
574 oc->y = config_head->state.y;
575 oc->transform = config_head->state.transform;
576 oc->scale = config_head->state.scale;
577 oc->adaptive_sync = config_head->state.adaptive_sync_enabled;
578 return oc;
579}
580
938static void output_manager_apply(struct sway_server *server, 581static void output_manager_apply(struct sway_server *server,
939 struct wlr_output_configuration_v1 *config, bool test_only) { 582 struct wlr_output_configuration_v1 *config, bool test_only) {
940 // TODO: perform atomic tests on the whole backend atomically 583 size_t configs_len = wl_list_length(&root->all_outputs);
941 584 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
942 struct wlr_output_configuration_head_v1 *config_head; 585 if (!configs) {
943 // First disable outputs we need to disable 586 return;
944 bool ok = true; 587 }
945 wl_list_for_each(config_head, &config->heads, link) { 588
946 struct wlr_output *wlr_output = config_head->state.output; 589 int config_idx = 0;
947 struct sway_output *output = wlr_output->data; 590 struct sway_output *sway_output;
948 if (!output->enabled || config_head->state.enabled) { 591 wl_list_for_each(sway_output, &root->all_outputs, link) {
592 if (sway_output == root->fallback_output) {
593 configs_len--;
949 continue; 594 continue;
950 } 595 }
951 struct output_config *oc = new_output_config(output->wlr_output->name);
952 oc->enabled = false;
953 596
954 if (test_only) { 597 struct matched_output_config *cfg = &configs[config_idx++];
955 ok &= test_output_config(oc, output); 598 cfg->output = sway_output;
956 } else {
957 oc = store_output_config(oc);
958 ok &= apply_output_config(oc, output);
959 }
960 }
961 599
962 // Then enable outputs that need to 600 struct wlr_output_configuration_head_v1 *config_head;
963 wl_list_for_each(config_head, &config->heads, link) { 601 wl_list_for_each(config_head, &config->heads, link) {
964 struct wlr_output *wlr_output = config_head->state.output; 602 if (config_head->state.output == sway_output->wlr_output) {
965 struct sway_output *output = wlr_output->data; 603 cfg->config = output_config_for_config_head(config_head, sway_output);
966 if (!config_head->state.enabled) { 604 break;
967 continue; 605 }
968 } 606 }
969 struct output_config *oc = new_output_config(output->wlr_output->name); 607 if (!cfg->config) {
970 oc->enabled = true; 608 cfg->config = find_output_config(sway_output);
971 if (config_head->state.mode != NULL) {
972 struct wlr_output_mode *mode = config_head->state.mode;
973 oc->width = mode->width;
974 oc->height = mode->height;
975 oc->refresh_rate = mode->refresh / 1000.f;
976 } else {
977 oc->width = config_head->state.custom_mode.width;
978 oc->height = config_head->state.custom_mode.height;
979 oc->refresh_rate =
980 config_head->state.custom_mode.refresh / 1000.f;
981 } 609 }
982 oc->x = config_head->state.x; 610 }
983 oc->y = config_head->state.y;
984 oc->transform = config_head->state.transform;
985 oc->scale = config_head->state.scale;
986 611
987 if (test_only) { 612 sort_output_configs_by_priority(configs, configs_len);
988 ok &= test_output_config(oc, output); 613 bool ok = apply_output_configs(configs, configs_len, test_only, false);
614 for (size_t idx = 0; idx < configs_len; idx++) {
615 struct matched_output_config *cfg = &configs[idx];
616
617 // Only store new configs for successful non-test commits. Old configs,
618 // test-only and failed commits just get freed.
619 bool store_config = false;
620 if (!test_only && ok) {
621 struct wlr_output_configuration_head_v1 *config_head;
622 wl_list_for_each(config_head, &config->heads, link) {
623 if (config_head->state.output == cfg->output->wlr_output) {
624 store_config = true;
625 break;
626 }
627 }
628 }
629 if (store_config) {
630 store_output_config(cfg->config);
989 } else { 631 } else {
990 oc = store_output_config(oc); 632 free_output_config(cfg->config);
991 ok &= apply_output_config(oc, output);
992 } 633 }
993 } 634 }
635 free(configs);
994 636
995 if (ok) { 637 if (ok) {
996 wlr_output_configuration_v1_send_succeeded(config); 638 wlr_output_configuration_v1_send_succeeded(config);
@@ -1034,6 +676,6 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
1034 oc->power = 1; 676 oc->power = 1;
1035 break; 677 break;
1036 } 678 }
1037 oc = store_output_config(oc); 679 store_output_config(oc);
1038 apply_output_config(oc, output); 680 apply_all_output_configs();
1039} 681}
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
deleted file mode 100644
index ed9ad490..00000000
--- a/sway/desktop/render.c
+++ /dev/null
@@ -1,1204 +0,0 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
3#include <GLES2/gl2.h>
4#include <stdlib.h>
5#include <strings.h>
6#include <time.h>
7#include <wayland-server-core.h>
8#include <wlr/render/gles2.h>
9#include <wlr/render/wlr_renderer.h>
10#include <wlr/types/wlr_buffer.h>
11#include <wlr/types/wlr_matrix.h>
12#include <wlr/types/wlr_output_damage.h>
13#include <wlr/types/wlr_output_layout.h>
14#include <wlr/types/wlr_output.h>
15#include <wlr/types/wlr_compositor.h>
16#include <wlr/util/region.h>
17#include "log.h"
18#include "config.h"
19#include "sway/config.h"
20#include "sway/input/input-manager.h"
21#include "sway/input/seat.h"
22#include "sway/layers.h"
23#include "sway/output.h"
24#include "sway/server.h"
25#include "sway/tree/arrange.h"
26#include "sway/tree/container.h"
27#include "sway/tree/root.h"
28#include "sway/tree/view.h"
29#include "sway/tree/workspace.h"
30
31struct render_data {
32 pixman_region32_t *damage;
33 float alpha;
34 struct wlr_box *clip_box;
35};
36
37/**
38 * Apply scale to a width or height.
39 *
40 * One does not simply multiply the width by the scale. We allow fractional
41 * scaling, which means the resulting scaled width might be a decimal.
42 * So we round it.
43 *
44 * But even this can produce undesirable results depending on the X or Y offset
45 * of the box. For example, with a scale of 1.5, a box with width=1 should not
46 * scale to 2px if its X coordinate is 1, because the X coordinate would have
47 * scaled to 2px.
48 */
49static int scale_length(int length, int offset, float scale) {
50 return round((offset + length) * scale) - round(offset * scale);
51}
52
53static void scissor_output(struct wlr_output *wlr_output,
54 pixman_box32_t *rect) {
55 struct wlr_renderer *renderer = wlr_output->renderer;
56 assert(renderer);
57
58 struct wlr_box box = {
59 .x = rect->x1,
60 .y = rect->y1,
61 .width = rect->x2 - rect->x1,
62 .height = rect->y2 - rect->y1,
63 };
64
65 int ow, oh;
66 wlr_output_transformed_resolution(wlr_output, &ow, &oh);
67
68 enum wl_output_transform transform =
69 wlr_output_transform_invert(wlr_output->transform);
70 wlr_box_transform(&box, &box, transform, ow, oh);
71
72 wlr_renderer_scissor(renderer, &box);
73}
74
75static void set_scale_filter(struct wlr_output *wlr_output,
76 struct wlr_texture *texture, enum scale_filter_mode scale_filter) {
77 if (!wlr_texture_is_gles2(texture)) {
78 return;
79 }
80
81 struct wlr_gles2_texture_attribs attribs;
82 wlr_gles2_texture_get_attribs(texture, &attribs);
83
84 glBindTexture(attribs.target, attribs.tex);
85
86 switch (scale_filter) {
87 case SCALE_FILTER_LINEAR:
88 glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
89 break;
90 case SCALE_FILTER_NEAREST:
91 glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
92 break;
93 case SCALE_FILTER_DEFAULT:
94 case SCALE_FILTER_SMART:
95 assert(false); // unreachable
96 }
97}
98
99static void render_texture(struct wlr_output *wlr_output,
100 pixman_region32_t *output_damage, struct wlr_texture *texture,
101 const struct wlr_fbox *src_box, const struct wlr_box *dst_box,
102 const float matrix[static 9], float alpha) {
103 struct wlr_renderer *renderer = wlr_output->renderer;
104 struct sway_output *output = wlr_output->data;
105
106 pixman_region32_t damage;
107 pixman_region32_init(&damage);
108 pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y,
109 dst_box->width, dst_box->height);
110 pixman_region32_intersect(&damage, &damage, output_damage);
111 bool damaged = pixman_region32_not_empty(&damage);
112 if (!damaged) {
113 goto damage_finish;
114 }
115
116 int nrects;
117 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
118 for (int i = 0; i < nrects; ++i) {
119 scissor_output(wlr_output, &rects[i]);
120 set_scale_filter(wlr_output, texture, output->scale_filter);
121 if (src_box != NULL) {
122 wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, alpha);
123 } else {
124 wlr_render_texture_with_matrix(renderer, texture, matrix, alpha);
125 }
126 }
127
128damage_finish:
129 pixman_region32_fini(&damage);
130}
131
132static void render_surface_iterator(struct sway_output *output,
133 struct sway_view *view, struct wlr_surface *surface,
134 struct wlr_box *_box, void *_data) {
135 struct render_data *data = _data;
136 struct wlr_output *wlr_output = output->wlr_output;
137 pixman_region32_t *output_damage = data->damage;
138 float alpha = data->alpha;
139
140 struct wlr_texture *texture = wlr_surface_get_texture(surface);
141 if (!texture) {
142 return;
143 }
144
145 struct wlr_fbox src_box;
146 wlr_surface_get_buffer_source_box(surface, &src_box);
147
148 struct wlr_box proj_box = *_box;
149 scale_box(&proj_box, wlr_output->scale);
150
151 float matrix[9];
152 enum wl_output_transform transform =
153 wlr_output_transform_invert(surface->current.transform);
154 wlr_matrix_project_box(matrix, &proj_box, transform, 0.0,
155 wlr_output->transform_matrix);
156
157 struct wlr_box dst_box = *_box;
158 struct wlr_box *clip_box = data->clip_box;
159 if (clip_box != NULL) {
160 dst_box.width = fmin(dst_box.width, clip_box->width);
161 dst_box.height = fmin(dst_box.height, clip_box->height);
162 }
163 scale_box(&dst_box, wlr_output->scale);
164
165 render_texture(wlr_output, output_damage, texture,
166 &src_box, &dst_box, matrix, alpha);
167
168 wlr_presentation_surface_sampled_on_output(server.presentation, surface,
169 wlr_output);
170}
171
172static void render_layer_toplevel(struct sway_output *output,
173 pixman_region32_t *damage, struct wl_list *layer_surfaces) {
174 struct render_data data = {
175 .damage = damage,
176 .alpha = 1.0f,
177 };
178 output_layer_for_each_toplevel_surface(output, layer_surfaces,
179 render_surface_iterator, &data);
180}
181
182static void render_layer_popups(struct sway_output *output,
183 pixman_region32_t *damage, struct wl_list *layer_surfaces) {
184 struct render_data data = {
185 .damage = damage,
186 .alpha = 1.0f,
187 };
188 output_layer_for_each_popup_surface(output, layer_surfaces,
189 render_surface_iterator, &data);
190}
191
192#if HAVE_XWAYLAND
193static void render_unmanaged(struct sway_output *output,
194 pixman_region32_t *damage, struct wl_list *unmanaged) {
195 struct render_data data = {
196 .damage = damage,
197 .alpha = 1.0f,
198 };
199 output_unmanaged_for_each_surface(output, unmanaged,
200 render_surface_iterator, &data);
201}
202#endif
203
204static void render_drag_icons(struct sway_output *output,
205 pixman_region32_t *damage, struct wl_list *drag_icons) {
206 struct render_data data = {
207 .damage = damage,
208 .alpha = 1.0f,
209 };
210 output_drag_icons_for_each_surface(output, drag_icons,
211 render_surface_iterator, &data);
212}
213
214// _box.x and .y are expected to be layout-local
215// _box.width and .height are expected to be output-buffer-local
216void render_rect(struct sway_output *output,
217 pixman_region32_t *output_damage, const struct wlr_box *_box,
218 float color[static 4]) {
219 struct wlr_output *wlr_output = output->wlr_output;
220 struct wlr_renderer *renderer = wlr_output->renderer;
221
222 struct wlr_box box;
223 memcpy(&box, _box, sizeof(struct wlr_box));
224 box.x -= output->lx * wlr_output->scale;
225 box.y -= output->ly * wlr_output->scale;
226
227 pixman_region32_t damage;
228 pixman_region32_init(&damage);
229 pixman_region32_union_rect(&damage, &damage, box.x, box.y,
230 box.width, box.height);
231 pixman_region32_intersect(&damage, &damage, output_damage);
232 bool damaged = pixman_region32_not_empty(&damage);
233 if (!damaged) {
234 goto damage_finish;
235 }
236
237 int nrects;
238 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
239 for (int i = 0; i < nrects; ++i) {
240 scissor_output(wlr_output, &rects[i]);
241 wlr_render_rect(renderer, &box, color,
242 wlr_output->transform_matrix);
243 }
244
245damage_finish:
246 pixman_region32_fini(&damage);
247}
248
249void premultiply_alpha(float color[4], float opacity) {
250 color[3] *= opacity;
251 color[0] *= color[3];
252 color[1] *= color[3];
253 color[2] *= color[3];
254}
255
256static void render_view_toplevels(struct sway_view *view,
257 struct sway_output *output, pixman_region32_t *damage, float alpha) {
258 struct render_data data = {
259 .damage = damage,
260 .alpha = alpha,
261 };
262 struct wlr_box clip_box;
263 if (!container_is_current_floating(view->container)) {
264 // As we pass the geometry offsets to the surface iterator, we will
265 // need to account for the offsets in the clip dimensions.
266 clip_box.width = view->container->current.content_width + view->geometry.x;
267 clip_box.height = view->container->current.content_height + view->geometry.y;
268 data.clip_box = &clip_box;
269 }
270 // Render all toplevels without descending into popups
271 double ox = view->container->surface_x -
272 output->lx - view->geometry.x;
273 double oy = view->container->surface_y -
274 output->ly - view->geometry.y;
275 output_surface_for_each_surface(output, view->surface, ox, oy,
276 render_surface_iterator, &data);
277}
278
279static void render_view_popups(struct sway_view *view,
280 struct sway_output *output, pixman_region32_t *damage, float alpha) {
281 struct render_data data = {
282 .damage = damage,
283 .alpha = alpha,
284 };
285 output_view_for_each_popup_surface(output, view,
286 render_surface_iterator, &data);
287}
288
289static void render_saved_view(struct sway_view *view,
290 struct sway_output *output, pixman_region32_t *damage, float alpha) {
291 struct wlr_output *wlr_output = output->wlr_output;
292
293 if (wl_list_empty(&view->saved_buffers)) {
294 return;
295 }
296
297 bool floating = container_is_current_floating(view->container);
298
299 struct sway_saved_buffer *saved_buf;
300 wl_list_for_each(saved_buf, &view->saved_buffers, link) {
301 if (!saved_buf->buffer->texture) {
302 continue;
303 }
304
305 struct wlr_box proj_box = {
306 .x = saved_buf->x - view->saved_geometry.x - output->lx,
307 .y = saved_buf->y - view->saved_geometry.y - output->ly,
308 .width = saved_buf->width,
309 .height = saved_buf->height,
310 };
311
312 struct wlr_box output_box = {
313 .width = output->width,
314 .height = output->height,
315 };
316
317 struct wlr_box intersection;
318 bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box);
319 if (!intersects) {
320 continue;
321 }
322
323 struct wlr_box dst_box = proj_box;
324 scale_box(&proj_box, wlr_output->scale);
325
326 float matrix[9];
327 enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform);
328 wlr_matrix_project_box(matrix, &proj_box, transform, 0,
329 wlr_output->transform_matrix);
330
331 if (!floating) {
332 dst_box.width = fmin(dst_box.width,
333 view->container->current.content_width -
334 (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x);
335 dst_box.height = fmin(dst_box.height,
336 view->container->current.content_height -
337 (saved_buf->y - view->container->current.content_y) + view->saved_geometry.y);
338 }
339 scale_box(&dst_box, wlr_output->scale);
340
341 render_texture(wlr_output, damage, saved_buf->buffer->texture,
342 &saved_buf->source_box, &dst_box, matrix, alpha);
343 }
344
345 // FIXME: we should set the surface that this saved buffer originates from
346 // as sampled here.
347 // https://github.com/swaywm/sway/pull/4465#discussion_r321082059
348}
349
350/**
351 * Render a view's surface and left/bottom/right borders.
352 */
353static void render_view(struct sway_output *output, pixman_region32_t *damage,
354 struct sway_container *con, struct border_colors *colors) {
355 struct sway_view *view = con->view;
356 if (!wl_list_empty(&view->saved_buffers)) {
357 render_saved_view(view, output, damage, view->container->alpha);
358 } else if (view->surface) {
359 render_view_toplevels(view, output, damage, view->container->alpha);
360 }
361
362 if (con->current.border == B_NONE || con->current.border == B_CSD) {
363 return;
364 }
365
366 struct wlr_box box;
367 float output_scale = output->wlr_output->scale;
368 float color[4];
369 struct sway_container_state *state = &con->current;
370
371 if (state->border_left) {
372 memcpy(&color, colors->child_border, sizeof(float) * 4);
373 premultiply_alpha(color, con->alpha);
374 box.x = floor(state->x);
375 box.y = floor(state->content_y);
376 box.width = state->border_thickness;
377 box.height = state->content_height;
378 scale_box(&box, output_scale);
379 render_rect(output, damage, &box, color);
380 }
381
382 list_t *siblings = container_get_current_siblings(con);
383 enum sway_container_layout layout =
384 container_current_parent_layout(con);
385
386 if (state->border_right) {
387 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_HORIZ) {
388 memcpy(&color, colors->indicator, sizeof(float) * 4);
389 } else {
390 memcpy(&color, colors->child_border, sizeof(float) * 4);
391 }
392 premultiply_alpha(color, con->alpha);
393 box.x = floor(state->content_x + state->content_width);
394 box.y = floor(state->content_y);
395 box.width = state->border_thickness;
396 box.height = state->content_height;
397 scale_box(&box, output_scale);
398 render_rect(output, damage, &box, color);
399 }
400
401 if (state->border_bottom) {
402 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_VERT) {
403 memcpy(&color, colors->indicator, sizeof(float) * 4);
404 } else {
405 memcpy(&color, colors->child_border, sizeof(float) * 4);
406 }
407 premultiply_alpha(color, con->alpha);
408 box.x = floor(state->x);
409 box.y = floor(state->content_y + state->content_height);
410 box.width = state->width;
411 box.height = state->border_thickness;
412 scale_box(&box, output_scale);
413 render_rect(output, damage, &box, color);
414 }
415}
416
417/**
418 * Render a titlebar.
419 *
420 * Care must be taken not to render over the same pixel multiple times,
421 * otherwise the colors will be incorrect when using opacity.
422 *
423 * The height is: 1px border, 3px padding, font height, 3px padding, 1px border
424 * The left side is: 1px border, 2px padding, title
425 */
426static void render_titlebar(struct sway_output *output,
427 pixman_region32_t *output_damage, struct sway_container *con,
428 int x, int y, int width,
429 struct border_colors *colors, struct wlr_texture *title_texture,
430 struct wlr_texture *marks_texture) {
431 struct wlr_box box;
432 float color[4];
433 float output_scale = output->wlr_output->scale;
434 double output_x = output->lx;
435 double output_y = output->ly;
436 int titlebar_border_thickness = config->titlebar_border_thickness;
437 int titlebar_h_padding = config->titlebar_h_padding;
438 int titlebar_v_padding = config->titlebar_v_padding;
439 enum alignment title_align = config->title_align;
440
441 // Single pixel bar above title
442 memcpy(&color, colors->border, sizeof(float) * 4);
443 premultiply_alpha(color, con->alpha);
444 box.x = x;
445 box.y = y;
446 box.width = width;
447 box.height = titlebar_border_thickness;
448 scale_box(&box, output_scale);
449 render_rect(output, output_damage, &box, color);
450
451 // Single pixel bar below title
452 box.x = x;
453 box.y = y + container_titlebar_height() - titlebar_border_thickness;
454 box.width = width;
455 box.height = titlebar_border_thickness;
456 scale_box(&box, output_scale);
457 render_rect(output, output_damage, &box, color);
458
459 // Single pixel left edge
460 box.x = x;
461 box.y = y + titlebar_border_thickness;
462 box.width = titlebar_border_thickness;
463 box.height = container_titlebar_height() - titlebar_border_thickness * 2;
464 scale_box(&box, output_scale);
465 render_rect(output, output_damage, &box, color);
466
467 // Single pixel right edge
468 box.x = x + width - titlebar_border_thickness;
469 box.y = y + titlebar_border_thickness;
470 box.width = titlebar_border_thickness;
471 box.height = container_titlebar_height() - titlebar_border_thickness * 2;
472 scale_box(&box, output_scale);
473 render_rect(output, output_damage, &box, color);
474
475 int inner_x = x - output_x + titlebar_h_padding;
476 int bg_y = y + titlebar_border_thickness;
477 size_t inner_width = width - titlebar_h_padding * 2;
478
479 // output-buffer local
480 int ob_inner_x = round(inner_x * output_scale);
481 int ob_inner_width = scale_length(inner_width, inner_x, output_scale);
482 int ob_bg_height = scale_length(
483 (titlebar_v_padding - titlebar_border_thickness) * 2 +
484 config->font_height, bg_y, output_scale);
485
486 // Marks
487 int ob_marks_x = 0; // output-buffer-local
488 int ob_marks_width = 0; // output-buffer-local
489 if (config->show_marks && marks_texture) {
490 struct wlr_box texture_box = {
491 .width = marks_texture->width,
492 .height = marks_texture->height,
493 };
494 ob_marks_width = texture_box.width;
495
496 // The marks texture might be shorter than the config->font_height, in
497 // which case we need to pad it as evenly as possible above and below.
498 int ob_padding_total = ob_bg_height - texture_box.height;
499 int ob_padding_above = floor(ob_padding_total / 2.0);
500 int ob_padding_below = ceil(ob_padding_total / 2.0);
501
502 // Render texture. If the title is on the right, the marks will be on
503 // the left. Otherwise, they will be on the right.
504 if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) {
505 texture_box.x = ob_inner_x;
506 } else {
507 texture_box.x = ob_inner_x + ob_inner_width - texture_box.width;
508 }
509 ob_marks_x = texture_box.x;
510
511 texture_box.y = round((bg_y - output_y) * output_scale) +
512 ob_padding_above;
513
514 float matrix[9];
515 wlr_matrix_project_box(matrix, &texture_box,
516 WL_OUTPUT_TRANSFORM_NORMAL,
517 0.0, output->wlr_output->transform_matrix);
518
519 if (ob_inner_width < texture_box.width) {
520 texture_box.width = ob_inner_width;
521 }
522 render_texture(output->wlr_output, output_damage, marks_texture,
523 NULL, &texture_box, matrix, con->alpha);
524
525 // Padding above
526 memcpy(&color, colors->background, sizeof(float) * 4);
527 premultiply_alpha(color, con->alpha);
528 box.x = texture_box.x + round(output_x * output_scale);
529 box.y = round((y + titlebar_border_thickness) * output_scale);
530 box.width = texture_box.width;
531 box.height = ob_padding_above;
532 render_rect(output, output_damage, &box, color);
533
534 // Padding below
535 box.y += ob_padding_above + texture_box.height;
536 box.height = ob_padding_below;
537 render_rect(output, output_damage, &box, color);
538 }
539
540 // Title text
541 int ob_title_x = 0; // output-buffer-local
542 int ob_title_width = 0; // output-buffer-local
543 if (title_texture) {
544 struct wlr_box texture_box = {
545 .width = title_texture->width,
546 .height = title_texture->height,
547 };
548
549 // The effective output may be NULL when con is not on any output.
550 // This can happen because we render all children of containers,
551 // even those that are out of the bounds of any output.
552 struct sway_output *effective = container_get_effective_output(con);
553 float title_scale = effective ? effective->wlr_output->scale : output_scale;
554 texture_box.width = texture_box.width * output_scale / title_scale;
555 texture_box.height = texture_box.height * output_scale / title_scale;
556 ob_title_width = texture_box.width;
557
558 // The title texture might be shorter than the config->font_height,
559 // in which case we need to pad it above and below.
560 int ob_padding_above = round((titlebar_v_padding -
561 titlebar_border_thickness) * output_scale);
562 int ob_padding_below = ob_bg_height - ob_padding_above -
563 texture_box.height;
564
565 // Render texture
566 if (texture_box.width > ob_inner_width - ob_marks_width) {
567 texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width)
568 ? ob_marks_x + ob_marks_width : ob_inner_x;
569 } else if (title_align == ALIGN_LEFT) {
570 texture_box.x = ob_inner_x;
571 } else if (title_align == ALIGN_CENTER) {
572 // If there are marks visible, center between the edge and marks.
573 // Otherwise, center in the inner area.
574 if (ob_marks_width) {
575 texture_box.x = (ob_inner_x + ob_marks_x) / 2
576 - texture_box.width / 2;
577 } else {
578 texture_box.x = ob_inner_x + ob_inner_width / 2
579 - texture_box.width / 2;
580 }
581 } else {
582 texture_box.x = ob_inner_x + ob_inner_width - texture_box.width;
583 }
584 ob_title_x = texture_box.x;
585
586 texture_box.y =
587 round((bg_y - output_y) * output_scale) + ob_padding_above;
588
589 float matrix[9];
590 wlr_matrix_project_box(matrix, &texture_box,
591 WL_OUTPUT_TRANSFORM_NORMAL,
592 0.0, output->wlr_output->transform_matrix);
593
594 if (ob_inner_width - ob_marks_width < texture_box.width) {
595 texture_box.width = ob_inner_width - ob_marks_width;
596 }
597
598 render_texture(output->wlr_output, output_damage, title_texture,
599 NULL, &texture_box, matrix, con->alpha);
600
601 // Padding above
602 memcpy(&color, colors->background, sizeof(float) * 4);
603 premultiply_alpha(color, con->alpha);
604 box.x = texture_box.x + round(output_x * output_scale);
605 box.y = round((y + titlebar_border_thickness) * output_scale);
606 box.width = texture_box.width;
607 box.height = ob_padding_above;
608 render_rect(output, output_damage, &box, color);
609
610 // Padding below
611 box.y += ob_padding_above + texture_box.height;
612 box.height = ob_padding_below;
613 render_rect(output, output_damage, &box, color);
614 }
615
616 // Determine the left + right extends of the textures (output-buffer local)
617 int ob_left_x, ob_left_width, ob_right_x, ob_right_width;
618 if (ob_title_width == 0 && ob_marks_width == 0) {
619 ob_left_x = ob_inner_x;
620 ob_left_width = 0;
621 ob_right_x = ob_inner_x;
622 ob_right_width = 0;
623 } else if (ob_title_x < ob_marks_x) {
624 ob_left_x = ob_title_x;
625 ob_left_width = ob_title_width;
626 ob_right_x = ob_marks_x;
627 ob_right_width = ob_marks_width;
628 } else {
629 ob_left_x = ob_marks_x;
630 ob_left_width = ob_marks_width;
631 ob_right_x = ob_title_x;
632 ob_right_width = ob_title_width;
633 }
634 if (ob_left_x < ob_inner_x) {
635 ob_left_x = ob_inner_x;
636 } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) {
637 ob_right_x = ob_left_x;
638 ob_right_width = ob_left_width;
639 }
640
641 // Filler between title and marks
642 box.width = ob_right_x - ob_left_x - ob_left_width;
643 if (box.width > 0) {
644 box.x = ob_left_x + ob_left_width + round(output_x * output_scale);
645 box.y = round(bg_y * output_scale);
646 box.height = ob_bg_height;
647 render_rect(output, output_damage, &box, color);
648 }
649
650 // Padding on left side
651 box.x = x + titlebar_border_thickness;
652 box.y = y + titlebar_border_thickness;
653 box.width = titlebar_h_padding - titlebar_border_thickness;
654 box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 +
655 config->font_height;
656 scale_box(&box, output_scale);
657 int left_x = ob_left_x + round(output_x * output_scale);
658 if (box.x + box.width < left_x) {
659 box.width += left_x - box.x - box.width;
660 }
661 render_rect(output, output_damage, &box, color);
662
663 // Padding on right side
664 box.x = x + width - titlebar_h_padding;
665 box.y = y + titlebar_border_thickness;
666 box.width = titlebar_h_padding - titlebar_border_thickness;
667 box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 +
668 config->font_height;
669 scale_box(&box, output_scale);
670 int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale);
671 if (right_rx < box.x) {
672 box.width += box.x - right_rx;
673 box.x = right_rx;
674 }
675 render_rect(output, output_damage, &box, color);
676}
677
678/**
679 * Render the top border line for a view using "border pixel".
680 */
681static void render_top_border(struct sway_output *output,
682 pixman_region32_t *output_damage, struct sway_container *con,
683 struct border_colors *colors) {
684 struct sway_container_state *state = &con->current;
685 if (!state->border_top) {
686 return;
687 }
688 struct wlr_box box;
689 float color[4];
690 float output_scale = output->wlr_output->scale;
691
692 // Child border - top edge
693 memcpy(&color, colors->child_border, sizeof(float) * 4);
694 premultiply_alpha(color, con->alpha);
695 box.x = floor(state->x);
696 box.y = floor(state->y);
697 box.width = state->width;
698 box.height = state->border_thickness;
699 scale_box(&box, output_scale);
700 render_rect(output, output_damage, &box, color);
701}
702
703struct parent_data {
704 enum sway_container_layout layout;
705 struct wlr_box box;
706 list_t *children;
707 bool focused;
708 struct sway_container *active_child;
709};
710
711static void render_container(struct sway_output *output,
712 pixman_region32_t *damage, struct sway_container *con, bool parent_focused);
713
714/**
715 * Render a container's children using a L_HORIZ or L_VERT layout.
716 *
717 * Wrap child views in borders and leave child containers borderless because
718 * they'll apply their own borders to their children.
719 */
720static void render_containers_linear(struct sway_output *output,
721 pixman_region32_t *damage, struct parent_data *parent) {
722 for (int i = 0; i < parent->children->length; ++i) {
723 struct sway_container *child = parent->children->items[i];
724
725 if (child->view) {
726 struct sway_view *view = child->view;
727 struct border_colors *colors;
728 struct wlr_texture *title_texture;
729 struct wlr_texture *marks_texture;
730 struct sway_container_state *state = &child->current;
731
732 if (view_is_urgent(view)) {
733 colors = &config->border_colors.urgent;
734 title_texture = child->title_urgent;
735 marks_texture = child->marks_urgent;
736 } else if (state->focused || parent->focused) {
737 colors = &config->border_colors.focused;
738 title_texture = child->title_focused;
739 marks_texture = child->marks_focused;
740 } else if (child == parent->active_child) {
741 colors = &config->border_colors.focused_inactive;
742 title_texture = child->title_focused_inactive;
743 marks_texture = child->marks_focused_inactive;
744 } else {
745 colors = &config->border_colors.unfocused;
746 title_texture = child->title_unfocused;
747 marks_texture = child->marks_unfocused;
748 }
749
750 if (state->border == B_NORMAL) {
751 render_titlebar(output, damage, child, floor(state->x),
752 floor(state->y), state->width, colors,
753 title_texture, marks_texture);
754 } else if (state->border == B_PIXEL) {
755 render_top_border(output, damage, child, colors);
756 }
757 render_view(output, damage, child, colors);
758 } else {
759 render_container(output, damage, child,
760 parent->focused || child->current.focused);
761 }
762 }
763}
764
765static bool container_is_focused(struct sway_container *con, void *data) {
766 return con->current.focused;
767}
768
769static bool container_has_focused_child(struct sway_container *con) {
770 return container_find_child(con, container_is_focused, NULL);
771}
772
773/**
774 * Render a container's children using the L_TABBED layout.
775 */
776static void render_containers_tabbed(struct sway_output *output,
777 pixman_region32_t *damage, struct parent_data *parent) {
778 if (!parent->children->length) {
779 return;
780 }
781 struct sway_container *current = parent->active_child;
782 struct border_colors *current_colors = &config->border_colors.unfocused;
783 int tab_width = parent->box.width / parent->children->length;
784
785 // Render tabs
786 for (int i = 0; i < parent->children->length; ++i) {
787 struct sway_container *child = parent->children->items[i];
788 struct sway_view *view = child->view;
789 struct sway_container_state *cstate = &child->current;
790 struct border_colors *colors;
791 struct wlr_texture *title_texture;
792 struct wlr_texture *marks_texture;
793 bool urgent = view ?
794 view_is_urgent(view) : container_has_urgent_child(child);
795
796 if (urgent) {
797 colors = &config->border_colors.urgent;
798 title_texture = child->title_urgent;
799 marks_texture = child->marks_urgent;
800 } else if (cstate->focused || parent->focused) {
801 colors = &config->border_colors.focused;
802 title_texture = child->title_focused;
803 marks_texture = child->marks_focused;
804 } else if (config->has_focused_tab_title && container_has_focused_child(child)) {
805 colors = &config->border_colors.focused_tab_title;
806 title_texture = child->title_focused_tab_title;
807 marks_texture = child->marks_focused_tab_title;
808 } else if (child == parent->active_child) {
809 colors = &config->border_colors.focused_inactive;
810 title_texture = child->title_focused_inactive;
811 marks_texture = child->marks_focused_inactive;
812 } else {
813 colors = &config->border_colors.unfocused;
814 title_texture = child->title_unfocused;
815 marks_texture = child->marks_unfocused;
816 }
817
818 int x = floor(cstate->x + tab_width * i);
819
820 // Make last tab use the remaining width of the parent
821 if (i == parent->children->length - 1) {
822 tab_width = parent->box.width - tab_width * i;
823 }
824
825 render_titlebar(output, damage, child, x, parent->box.y, tab_width,
826 colors, title_texture, marks_texture);
827
828 if (child == current) {
829 current_colors = colors;
830 }
831 }
832
833 // Render surface and left/right/bottom borders
834 if (current->view) {
835 render_view(output, damage, current, current_colors);
836 } else {
837 render_container(output, damage, current,
838 parent->focused || current->current.focused);
839 }
840}
841
842/**
843 * Render a container's children using the L_STACKED layout.
844 */
845static void render_containers_stacked(struct sway_output *output,
846 pixman_region32_t *damage, struct parent_data *parent) {
847 if (!parent->children->length) {
848 return;
849 }
850 struct sway_container *current = parent->active_child;
851 struct border_colors *current_colors = &config->border_colors.unfocused;
852 size_t titlebar_height = container_titlebar_height();
853
854 // Render titles
855 for (int i = 0; i < parent->children->length; ++i) {
856 struct sway_container *child = parent->children->items[i];
857 struct sway_view *view = child->view;
858 struct sway_container_state *cstate = &child->current;
859 struct border_colors *colors;
860 struct wlr_texture *title_texture;
861 struct wlr_texture *marks_texture;
862 bool urgent = view ?
863 view_is_urgent(view) : container_has_urgent_child(child);
864
865 if (urgent) {
866 colors = &config->border_colors.urgent;
867 title_texture = child->title_urgent;
868 marks_texture = child->marks_urgent;
869 } else if (cstate->focused || parent->focused) {
870 colors = &config->border_colors.focused;
871 title_texture = child->title_focused;
872 marks_texture = child->marks_focused;
873 } else if (config->has_focused_tab_title && container_has_focused_child(child)) {
874 colors = &config->border_colors.focused_tab_title;
875 title_texture = child->title_focused_tab_title;
876 marks_texture = child->marks_focused_tab_title;
877 } else if (child == parent->active_child) {
878 colors = &config->border_colors.focused_inactive;
879 title_texture = child->title_focused_inactive;
880 marks_texture = child->marks_focused_inactive;
881 } else {
882 colors = &config->border_colors.unfocused;
883 title_texture = child->title_unfocused;
884 marks_texture = child->marks_unfocused;
885 }
886
887 int y = parent->box.y + titlebar_height * i;
888 render_titlebar(output, damage, child, parent->box.x, y,
889 parent->box.width, colors, title_texture, marks_texture);
890
891 if (child == current) {
892 current_colors = colors;
893 }
894 }
895
896 // Render surface and left/right/bottom borders
897 if (current->view) {
898 render_view(output, damage, current, current_colors);
899 } else {
900 render_container(output, damage, current,
901 parent->focused || current->current.focused);
902 }
903}
904
905static void render_containers(struct sway_output *output,
906 pixman_region32_t *damage, struct parent_data *parent) {
907 if (config->hide_lone_tab && parent->children->length == 1) {
908 struct sway_container *child = parent->children->items[0];
909 if (child->view) {
910 render_containers_linear(output,damage, parent);
911 return;
912 }
913 }
914
915 switch (parent->layout) {
916 case L_NONE:
917 case L_HORIZ:
918 case L_VERT:
919 render_containers_linear(output, damage, parent);
920 break;
921 case L_STACKED:
922 render_containers_stacked(output, damage, parent);
923 break;
924 case L_TABBED:
925 render_containers_tabbed(output, damage, parent);
926 break;
927 }
928}
929
930static void render_container(struct sway_output *output,
931 pixman_region32_t *damage, struct sway_container *con, bool focused) {
932 struct parent_data data = {
933 .layout = con->current.layout,
934 .box = {
935 .x = floor(con->current.x),
936 .y = floor(con->current.y),
937 .width = con->current.width,
938 .height = con->current.height,
939 },
940 .children = con->current.children,
941 .focused = focused,
942 .active_child = con->current.focused_inactive_child,
943 };
944 render_containers(output, damage, &data);
945}
946
947static void render_workspace(struct sway_output *output,
948 pixman_region32_t *damage, struct sway_workspace *ws, bool focused) {
949 struct parent_data data = {
950 .layout = ws->current.layout,
951 .box = {
952 .x = floor(ws->current.x),
953 .y = floor(ws->current.y),
954 .width = ws->current.width,
955 .height = ws->current.height,
956 },
957 .children = ws->current.tiling,
958 .focused = focused,
959 .active_child = ws->current.focused_inactive_child,
960 };
961 render_containers(output, damage, &data);
962}
963
964static void render_floating_container(struct sway_output *soutput,
965 pixman_region32_t *damage, struct sway_container *con) {
966 if (con->view) {
967 struct sway_view *view = con->view;
968 struct border_colors *colors;
969 struct wlr_texture *title_texture;
970 struct wlr_texture *marks_texture;
971
972 if (view_is_urgent(view)) {
973 colors = &config->border_colors.urgent;
974 title_texture = con->title_urgent;
975 marks_texture = con->marks_urgent;
976 } else if (con->current.focused) {
977 colors = &config->border_colors.focused;
978 title_texture = con->title_focused;
979 marks_texture = con->marks_focused;
980 } else {
981 colors = &config->border_colors.unfocused;
982 title_texture = con->title_unfocused;
983 marks_texture = con->marks_unfocused;
984 }
985
986 if (con->current.border == B_NORMAL) {
987 render_titlebar(soutput, damage, con, floor(con->current.x),
988 floor(con->current.y), con->current.width, colors,
989 title_texture, marks_texture);
990 } else if (con->current.border == B_PIXEL) {
991 render_top_border(soutput, damage, con, colors);
992 }
993 render_view(soutput, damage, con, colors);
994 } else {
995 render_container(soutput, damage, con, con->current.focused);
996 }
997}
998
999static void render_floating(struct sway_output *soutput,
1000 pixman_region32_t *damage) {
1001 for (int i = 0; i < root->outputs->length; ++i) {
1002 struct sway_output *output = root->outputs->items[i];
1003 for (int j = 0; j < output->current.workspaces->length; ++j) {
1004 struct sway_workspace *ws = output->current.workspaces->items[j];
1005 if (!workspace_is_visible(ws)) {
1006 continue;
1007 }
1008 for (int k = 0; k < ws->current.floating->length; ++k) {
1009 struct sway_container *floater = ws->current.floating->items[k];
1010 if (floater->current.fullscreen_mode != FULLSCREEN_NONE) {
1011 continue;
1012 }
1013 render_floating_container(soutput, damage, floater);
1014 }
1015 }
1016 }
1017}
1018
1019static void render_seatops(struct sway_output *output,
1020 pixman_region32_t *damage) {
1021 struct sway_seat *seat;
1022 wl_list_for_each(seat, &server.input->seats, link) {
1023 seatop_render(seat, output, damage);
1024 }
1025}
1026
1027void output_render(struct sway_output *output, struct timespec *when,
1028 pixman_region32_t *damage) {
1029 struct wlr_output *wlr_output = output->wlr_output;
1030 struct wlr_renderer *renderer = output->server->renderer;
1031
1032 struct sway_workspace *workspace = output->current.active_workspace;
1033 if (workspace == NULL) {
1034 return;
1035 }
1036
1037 struct sway_container *fullscreen_con = root->fullscreen_global;
1038 if (!fullscreen_con) {
1039 fullscreen_con = workspace->current.fullscreen;
1040 }
1041
1042 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
1043
1044 if (debug.damage == DAMAGE_RERENDER) {
1045 int width, height;
1046 wlr_output_transformed_resolution(wlr_output, &width, &height);
1047 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1048 }
1049
1050 if (!pixman_region32_not_empty(damage)) {
1051 // Output isn't damaged but needs buffer swap
1052 goto renderer_end;
1053 }
1054
1055 if (debug.damage == DAMAGE_HIGHLIGHT) {
1056 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
1057 }
1058
1059 if (server.session_lock.locked) {
1060 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
1061 if (server.session_lock.lock == NULL) {
1062 // abandoned lock -> red BG
1063 clear_color[0] = 1.f;
1064 }
1065 int nrects;
1066 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1067 for (int i = 0; i < nrects; ++i) {
1068 scissor_output(wlr_output, &rects[i]);
1069 wlr_renderer_clear(renderer, clear_color);
1070 }
1071
1072 if (server.session_lock.lock != NULL) {
1073 struct render_data data = {
1074 .damage = damage,
1075 .alpha = 1.0f,
1076 };
1077
1078 struct wlr_session_lock_surface_v1 *lock_surface;
1079 wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) {
1080 if (lock_surface->output != wlr_output) {
1081 continue;
1082 }
1083 if (!lock_surface->mapped) {
1084 continue;
1085 }
1086
1087 output_surface_for_each_surface(output, lock_surface->surface,
1088 0.0, 0.0, render_surface_iterator, &data);
1089 }
1090 }
1091 goto renderer_end;
1092 }
1093
1094 if (output_has_opaque_overlay_layer_surface(output)) {
1095 goto render_overlay;
1096 }
1097
1098 if (fullscreen_con) {
1099 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
1100
1101 int nrects;
1102 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1103 for (int i = 0; i < nrects; ++i) {
1104 scissor_output(wlr_output, &rects[i]);
1105 wlr_renderer_clear(renderer, clear_color);
1106 }
1107
1108 if (fullscreen_con->view) {
1109 if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) {
1110 render_saved_view(fullscreen_con->view, output, damage, 1.0f);
1111 } else if (fullscreen_con->view->surface) {
1112 render_view_toplevels(fullscreen_con->view,
1113 output, damage, 1.0f);
1114 }
1115 } else {
1116 render_container(output, damage, fullscreen_con,
1117 fullscreen_con->current.focused);
1118 }
1119
1120 for (int i = 0; i < workspace->current.floating->length; ++i) {
1121 struct sway_container *floater =
1122 workspace->current.floating->items[i];
1123 if (container_is_transient_for(floater, fullscreen_con)) {
1124 render_floating_container(output, damage, floater);
1125 }
1126 }
1127#if HAVE_XWAYLAND
1128 render_unmanaged(output, damage, &root->xwayland_unmanaged);
1129#endif
1130 } else {
1131 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
1132
1133 int nrects;
1134 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1135 for (int i = 0; i < nrects; ++i) {
1136 scissor_output(wlr_output, &rects[i]);
1137 wlr_renderer_clear(renderer, clear_color);
1138 }
1139
1140 render_layer_toplevel(output, damage,
1141 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
1142 render_layer_toplevel(output, damage,
1143 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
1144
1145 render_workspace(output, damage, workspace, workspace->current.focused);
1146 render_floating(output, damage);
1147#if HAVE_XWAYLAND
1148 render_unmanaged(output, damage, &root->xwayland_unmanaged);
1149#endif
1150 render_layer_toplevel(output, damage,
1151 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
1152
1153 render_layer_popups(output, damage,
1154 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
1155 render_layer_popups(output, damage,
1156 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
1157 render_layer_popups(output, damage,
1158 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
1159 }
1160
1161 render_seatops(output, damage);
1162
1163 struct sway_seat *seat = input_manager_current_seat();
1164 struct sway_container *focus = seat_get_focused_container(seat);
1165 if (focus && focus->view) {
1166 render_view_popups(focus->view, output, damage, focus->alpha);
1167 }
1168
1169render_overlay:
1170 render_layer_toplevel(output, damage,
1171 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
1172 render_layer_popups(output, damage,
1173 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
1174 render_drag_icons(output, damage, &root->drag_icons);
1175
1176renderer_end:
1177 wlr_renderer_scissor(renderer, NULL);
1178 wlr_output_render_software_cursors(wlr_output, damage);
1179 wlr_renderer_end(renderer);
1180
1181 int width, height;
1182 wlr_output_transformed_resolution(wlr_output, &width, &height);
1183
1184 pixman_region32_t frame_damage;
1185 pixman_region32_init(&frame_damage);
1186
1187 enum wl_output_transform transform =
1188 wlr_output_transform_invert(wlr_output->transform);
1189 wlr_region_transform(&frame_damage, &output->damage->current,
1190 transform, width, height);
1191
1192 if (debug.damage != DAMAGE_DEFAULT) {
1193 pixman_region32_union_rect(&frame_damage, &frame_damage,
1194 0, 0, wlr_output->width, wlr_output->height);
1195 }
1196
1197 wlr_output_set_damage(wlr_output, &frame_damage);
1198 pixman_region32_fini(&frame_damage);
1199
1200 if (!wlr_output_commit(wlr_output)) {
1201 return;
1202 }
1203 output->last_frame = *when;
1204}
diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c
deleted file mode 100644
index 1d7b536d..00000000
--- a/sway/desktop/surface.c
+++ /dev/null
@@ -1,46 +0,0 @@
1#define _POSIX_C_SOURCE 200112L
2#include <stdlib.h>
3#include <time.h>
4#include <wlr/types/wlr_compositor.h>
5#include "sway/server.h"
6#include "sway/surface.h"
7
8static void handle_destroy(struct wl_listener *listener, void *data) {
9 struct sway_surface *surface = wl_container_of(listener, surface, destroy);
10
11 surface->wlr_surface->data = NULL;
12 wl_list_remove(&surface->destroy.link);
13
14 if (surface->frame_done_timer) {
15 wl_event_source_remove(surface->frame_done_timer);
16 }
17
18 free(surface);
19}
20
21static int surface_frame_done_timer_handler(void *data) {
22 struct sway_surface *surface = data;
23
24 struct timespec now;
25 clock_gettime(CLOCK_MONOTONIC, &now);
26 wlr_surface_send_frame_done(surface->wlr_surface, &now);
27
28 return 0;
29}
30
31void handle_compositor_new_surface(struct wl_listener *listener, void *data) {
32 struct wlr_surface *wlr_surface = data;
33
34 struct sway_surface *surface = calloc(1, sizeof(struct sway_surface));
35 surface->wlr_surface = wlr_surface;
36 wlr_surface->data = surface;
37
38 surface->destroy.notify = handle_destroy;
39 wl_signal_add(&wlr_surface->events.destroy, &surface->destroy);
40
41 surface->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop,
42 surface_frame_done_timer_handler, surface);
43 if (!surface->frame_done_timer) {
44 wl_resource_post_no_memory(wlr_surface->resource);
45 }
46}
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index f5a3a053..042141ab 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -1,11 +1,10 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
5#include <time.h> 4#include <time.h>
6#include <wlr/types/wlr_buffer.h> 5#include <wlr/types/wlr_buffer.h>
7#include "sway/config.h" 6#include "sway/config.h"
8#include "sway/desktop.h" 7#include "sway/scene_descriptor.h"
9#include "sway/desktop/idle_inhibit_v1.h" 8#include "sway/desktop/idle_inhibit_v1.h"
10#include "sway/desktop/transaction.h" 9#include "sway/desktop/transaction.h"
11#include "sway/input/cursor.h" 10#include "sway/input/cursor.h"
@@ -214,39 +213,20 @@ static void transaction_add_node(struct sway_transaction *transaction,
214 213
215static void apply_output_state(struct sway_output *output, 214static void apply_output_state(struct sway_output *output,
216 struct sway_output_state *state) { 215 struct sway_output_state *state) {
217 output_damage_whole(output);
218 list_free(output->current.workspaces); 216 list_free(output->current.workspaces);
219 memcpy(&output->current, state, sizeof(struct sway_output_state)); 217 memcpy(&output->current, state, sizeof(struct sway_output_state));
220 output_damage_whole(output);
221} 218}
222 219
223static void apply_workspace_state(struct sway_workspace *ws, 220static void apply_workspace_state(struct sway_workspace *ws,
224 struct sway_workspace_state *state) { 221 struct sway_workspace_state *state) {
225 output_damage_whole(ws->current.output);
226 list_free(ws->current.floating); 222 list_free(ws->current.floating);
227 list_free(ws->current.tiling); 223 list_free(ws->current.tiling);
228 memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); 224 memcpy(&ws->current, state, sizeof(struct sway_workspace_state));
229 output_damage_whole(ws->current.output);
230} 225}
231 226
232static void apply_container_state(struct sway_container *container, 227static void apply_container_state(struct sway_container *container,
233 struct sway_container_state *state) { 228 struct sway_container_state *state) {
234 struct sway_view *view = container->view; 229 struct sway_view *view = container->view;
235 // Damage the old location
236 desktop_damage_whole_container(container);
237 if (view && !wl_list_empty(&view->saved_buffers)) {
238 struct sway_saved_buffer *saved_buf;
239 wl_list_for_each(saved_buf, &view->saved_buffers, link) {
240 struct wlr_box box = {
241 .x = saved_buf->x - view->saved_geometry.x,
242 .y = saved_buf->y - view->saved_geometry.y,
243 .width = saved_buf->width,
244 .height = saved_buf->height,
245 };
246 desktop_damage_box(&box);
247 }
248 }
249
250 // There are separate children lists for each instruction state, the 230 // There are separate children lists for each instruction state, the
251 // container's current state and the container's pending state 231 // container's current state and the container's pending state
252 // (ie. con->children). The list itself needs to be freed here. 232 // (ie. con->children). The list itself needs to be freed here.
@@ -256,35 +236,443 @@ static void apply_container_state(struct sway_container *container,
256 236
257 memcpy(&container->current, state, sizeof(struct sway_container_state)); 237 memcpy(&container->current, state, sizeof(struct sway_container_state));
258 238
259 if (view && !wl_list_empty(&view->saved_buffers)) { 239 if (view) {
260 if (!container->node.destroying || container->node.ntxnrefs == 1) { 240 if (view->saved_surface_tree) {
261 view_remove_saved_buffer(view); 241 if (!container->node.destroying || container->node.ntxnrefs == 1) {
242 view_remove_saved_buffer(view);
243 }
244 }
245
246 // If the view hasn't responded to the configure, center it within
247 // the container. This is important for fullscreen views which
248 // refuse to resize to the size of the output.
249 if (view->surface) {
250 view_center_and_clip_surface(view);
251 }
252 }
253}
254
255static void arrange_title_bar(struct sway_container *con,
256 int x, int y, int width, int height) {
257 container_update(con);
258
259 bool has_title_bar = height > 0;
260 wlr_scene_node_set_enabled(&con->title_bar.tree->node, has_title_bar);
261 if (!has_title_bar) {
262 return;
263 }
264
265 wlr_scene_node_set_position(&con->title_bar.tree->node, x, y);
266
267 con->title_width = width;
268 container_arrange_title_bar(con);
269}
270
271static void disable_container(struct sway_container *con) {
272 if (con->view) {
273 wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree);
274 } else {
275 for (int i = 0; i < con->current.children->length; i++) {
276 struct sway_container *child = con->current.children->items[i];
277
278 wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree);
279
280 disable_container(child);
281 }
282 }
283}
284
285static void arrange_container(struct sway_container *con,
286 int width, int height, bool title_bar, int gaps);
287
288static void arrange_children(enum sway_container_layout layout, list_t *children,
289 struct sway_container *active, struct wlr_scene_tree *content,
290 int width, int height, int gaps) {
291 int title_bar_height = container_titlebar_height();
292
293 if (layout == L_TABBED) {
294 struct sway_container *first = children->length == 1 ?
295 ((struct sway_container *)children->items[0]) : NULL;
296 if (config->hide_lone_tab && first && first->view &&
297 first->current.border != B_NORMAL) {
298 title_bar_height = 0;
299 }
300
301 double w = (double) width / children->length;
302 int title_offset = 0;
303 for (int i = 0; i < children->length; i++) {
304 struct sway_container *child = children->items[i];
305 bool activated = child == active;
306 int next_title_offset = round(w * i + w);
307
308 arrange_title_bar(child, title_offset, -title_bar_height,
309 next_title_offset - title_offset, title_bar_height);
310 wlr_scene_node_set_enabled(&child->border.tree->node, activated);
311 wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height);
312 wlr_scene_node_reparent(&child->scene_tree->node, content);
313
314 if (activated) {
315 arrange_container(child, width, height - title_bar_height,
316 false, 0);
317 } else {
318 disable_container(child);
319 }
320
321 title_offset = next_title_offset;
322 }
323 } else if (layout == L_STACKED) {
324 struct sway_container *first = children->length == 1 ?
325 ((struct sway_container *)children->items[0]) : NULL;
326 if (config->hide_lone_tab && first && first->view &&
327 first->current.border != B_NORMAL) {
328 title_bar_height = 0;
329 }
330
331 int title_height = title_bar_height * children->length;
332
333 int y = 0;
334 for (int i = 0; i < children->length; i++) {
335 struct sway_container *child = children->items[i];
336 bool activated = child == active;
337
338 arrange_title_bar(child, 0, y - title_height, width, title_bar_height);
339 wlr_scene_node_set_enabled(&child->border.tree->node, activated);
340 wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height);
341 wlr_scene_node_reparent(&child->scene_tree->node, content);
342
343 if (activated) {
344 arrange_container(child, width, height - title_height,
345 false, 0);
346 } else {
347 disable_container(child);
348 }
349
350 y += title_bar_height;
351 }
352 } else if (layout == L_VERT) {
353 int off = 0;
354 for (int i = 0; i < children->length; i++) {
355 struct sway_container *child = children->items[i];
356 int cheight = child->current.height;
357
358 wlr_scene_node_set_enabled(&child->border.tree->node, true);
359 wlr_scene_node_set_position(&child->scene_tree->node, 0, off);
360 wlr_scene_node_reparent(&child->scene_tree->node, content);
361 arrange_container(child, width, cheight, true, gaps);
362 off += cheight + gaps;
363 }
364 } else if (layout == L_HORIZ) {
365 int off = 0;
366 for (int i = 0; i < children->length; i++) {
367 struct sway_container *child = children->items[i];
368 int cwidth = child->current.width;
369
370 wlr_scene_node_set_enabled(&child->border.tree->node, true);
371 wlr_scene_node_set_position(&child->scene_tree->node, off, 0);
372 wlr_scene_node_reparent(&child->scene_tree->node, content);
373 arrange_container(child, cwidth, height, true, gaps);
374 off += cwidth + gaps;
375 }
376 } else {
377 sway_assert(false, "unreachable");
378 }
379}
380
381static void arrange_container(struct sway_container *con,
382 int width, int height, bool title_bar, int gaps) {
383 // this container might have previously been in the scratchpad,
384 // make sure it's enabled for viewing
385 wlr_scene_node_set_enabled(&con->scene_tree->node, true);
386
387 if (con->output_handler) {
388 wlr_scene_buffer_set_dest_size(con->output_handler, width, height);
389 }
390
391 if (con->view) {
392 int border_top = container_titlebar_height();
393 int border_width = con->current.border_thickness;
394
395 if (title_bar && con->current.border != B_NORMAL) {
396 wlr_scene_node_set_enabled(&con->title_bar.tree->node, false);
397 wlr_scene_node_set_enabled(&con->border.top->node, true);
398 } else {
399 wlr_scene_node_set_enabled(&con->border.top->node, false);
400 }
401
402 if (con->current.border == B_NORMAL) {
403 if (title_bar) {
404 arrange_title_bar(con, 0, 0, width, border_top);
405 } else {
406 border_top = 0;
407 // should be handled by the parent container
408 }
409 } else if (con->current.border == B_PIXEL) {
410 container_update(con);
411 border_top = title_bar && con->current.border_top ? border_width : 0;
412 } else if (con->current.border == B_NONE) {
413 container_update(con);
414 border_top = 0;
415 border_width = 0;
416 } else if (con->current.border == B_CSD) {
417 border_top = 0;
418 border_width = 0;
419 } else {
420 sway_assert(false, "unreachable");
421 }
422
423 int border_bottom = con->current.border_bottom ? border_width : 0;
424 int border_left = con->current.border_left ? border_width : 0;
425 int border_right = con->current.border_right ? border_width : 0;
426
427 wlr_scene_rect_set_size(con->border.top, width, border_top);
428 wlr_scene_rect_set_size(con->border.bottom, width, border_bottom);
429 wlr_scene_rect_set_size(con->border.left,
430 border_left, height - border_top - border_bottom);
431 wlr_scene_rect_set_size(con->border.right,
432 border_right, height - border_top - border_bottom);
433
434 wlr_scene_node_set_position(&con->border.top->node, 0, 0);
435 wlr_scene_node_set_position(&con->border.bottom->node,
436 0, height - border_bottom);
437 wlr_scene_node_set_position(&con->border.left->node,
438 0, border_top);
439 wlr_scene_node_set_position(&con->border.right->node,
440 width - border_right, border_top);
441
442 // make sure to reparent, it's possible that the client just came out of
443 // fullscreen mode where the parent of the surface is not the container
444 wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree);
445 wlr_scene_node_set_position(&con->view->scene_tree->node,
446 border_left, border_top);
447 } else {
448 // make sure to disable the title bar if the parent is not managing it
449 if (title_bar) {
450 wlr_scene_node_set_enabled(&con->title_bar.tree->node, false);
451 }
452
453 arrange_children(con->current.layout, con->current.children,
454 con->current.focused_inactive_child, con->content_tree,
455 width, height, gaps);
456 }
457}
458
459static int container_get_gaps(struct sway_container *con) {
460 struct sway_workspace *ws = con->current.workspace;
461 struct sway_container *temp = con;
462 while (temp) {
463 enum sway_container_layout layout;
464 if (temp->current.parent) {
465 layout = temp->current.parent->current.layout;
466 } else {
467 layout = ws->current.layout;
468 }
469 if (layout == L_TABBED || layout == L_STACKED) {
470 return 0;
471 }
472 temp = temp->pending.parent;
473 }
474 return ws->gaps_inner;
475}
476
477static void arrange_fullscreen(struct wlr_scene_tree *tree,
478 struct sway_container *fs, struct sway_workspace *ws,
479 int width, int height) {
480 struct wlr_scene_node *fs_node;
481 if (fs->view) {
482 fs_node = &fs->view->scene_tree->node;
483
484 // if we only care about the view, disable any decorations
485 wlr_scene_node_set_enabled(&fs->scene_tree->node, false);
486 } else {
487 fs_node = &fs->scene_tree->node;
488 arrange_container(fs, width, height, true, container_get_gaps(fs));
489 }
490
491 wlr_scene_node_reparent(fs_node, tree);
492 wlr_scene_node_lower_to_bottom(fs_node);
493 wlr_scene_node_set_position(fs_node, 0, 0);
494}
495
496static void arrange_workspace_floating(struct sway_workspace *ws) {
497 for (int i = 0; i < ws->current.floating->length; i++) {
498 struct sway_container *floater = ws->current.floating->items[i];
499 struct wlr_scene_tree *layer = root->layers.floating;
500
501 if (floater->current.fullscreen_mode != FULLSCREEN_NONE) {
502 continue;
503 }
504
505 if (root->fullscreen_global) {
506 if (container_is_transient_for(floater, root->fullscreen_global)) {
507 layer = root->layers.fullscreen_global;
508 }
509 } else {
510 for (int i = 0; i < root->outputs->length; i++) {
511 struct sway_output *output = root->outputs->items[i];
512 struct sway_workspace *active = output->current.active_workspace;
513
514 if (active && active->fullscreen &&
515 container_is_transient_for(floater, active->fullscreen)) {
516 layer = root->layers.fullscreen;
517 }
518 }
519 }
520
521 wlr_scene_node_reparent(&floater->scene_tree->node, layer);
522 wlr_scene_node_set_position(&floater->scene_tree->node,
523 floater->current.x, floater->current.y);
524 wlr_scene_node_set_enabled(&floater->scene_tree->node, true);
525
526 arrange_container(floater, floater->current.width, floater->current.height,
527 true, ws->gaps_inner);
528 }
529}
530
531static void arrange_workspace_tiling(struct sway_workspace *ws,
532 int width, int height) {
533 arrange_children(ws->current.layout, ws->current.tiling,
534 ws->current.focused_inactive_child, ws->layers.tiling,
535 width, height, ws->gaps_inner);
536}
537
538static void disable_workspace(struct sway_workspace *ws) {
539 // if any containers were just moved to a disabled workspace it will
540 // have the parent of the old workspace. Move the workspace so that it won't
541 // be shown.
542 for (int i = 0; i < ws->current.tiling->length; i++) {
543 struct sway_container *child = ws->current.tiling->items[i];
544
545 wlr_scene_node_reparent(&child->scene_tree->node, ws->layers.tiling);
546 disable_container(child);
547 }
548
549 for (int i = 0; i < ws->current.floating->length; i++) {
550 struct sway_container *floater = ws->current.floating->items[i];
551 wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating);
552 disable_container(floater);
553 wlr_scene_node_set_enabled(&floater->scene_tree->node, false);
554 }
555}
556
557static void arrange_output(struct sway_output *output, int width, int height) {
558 for (int i = 0; i < output->current.workspaces->length; i++) {
559 struct sway_workspace *child = output->current.workspaces->items[i];
560
561 bool activated = output->current.active_workspace == child;
562
563 wlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling);
564 wlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen);
565
566 for (int i = 0; i < child->current.floating->length; i++) {
567 struct sway_container *floater = child->current.floating->items[i];
568 wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating);
569 wlr_scene_node_set_enabled(&floater->scene_tree->node, activated);
570 }
571
572 if (activated) {
573 struct sway_container *fs = child->current.fullscreen;
574 wlr_scene_node_set_enabled(&child->layers.tiling->node, !fs);
575 wlr_scene_node_set_enabled(&child->layers.fullscreen->node, fs);
576
577 arrange_workspace_floating(child);
578
579 wlr_scene_node_set_enabled(&output->layers.shell_background->node, !fs);
580 wlr_scene_node_set_enabled(&output->layers.shell_bottom->node, !fs);
581 wlr_scene_node_set_enabled(&output->layers.fullscreen->node, fs);
582
583 if (fs) {
584 wlr_scene_rect_set_size(output->fullscreen_background, width, height);
585
586 arrange_fullscreen(child->layers.fullscreen, fs, child,
587 width, height);
588 } else {
589 struct wlr_box *area = &output->usable_area;
590 struct side_gaps *gaps = &child->current_gaps;
591
592 wlr_scene_node_set_position(&child->layers.tiling->node,
593 gaps->left + area->x, gaps->top + area->y);
594
595 arrange_workspace_tiling(child,
596 area->width - gaps->left - gaps->right,
597 area->height - gaps->top - gaps->bottom);
598 }
599 } else {
600 wlr_scene_node_set_enabled(&child->layers.tiling->node, false);
601 wlr_scene_node_set_enabled(&child->layers.fullscreen->node, false);
602
603 disable_workspace(child);
262 } 604 }
263 } 605 }
606}
264 607
265 // If the view hasn't responded to the configure, center it within 608void arrange_popups(struct wlr_scene_tree *popups) {
266 // the container. This is important for fullscreen views which 609 struct wlr_scene_node *node;
267 // refuse to resize to the size of the output. 610 wl_list_for_each(node, &popups->children, link) {
268 if (view && view->surface) { 611 struct sway_popup_desc *popup = scene_descriptor_try_get(node,
269 view_center_surface(view); 612 SWAY_SCENE_DESC_POPUP);
613
614 int lx, ly;
615 wlr_scene_node_coords(popup->relative, &lx, &ly);
616 wlr_scene_node_set_position(node, lx, ly);
270 } 617 }
618}
619
620static void arrange_root(struct sway_root *root) {
621 struct sway_container *fs = root->fullscreen_global;
622
623 wlr_scene_node_set_enabled(&root->layers.shell_background->node, !fs);
624 wlr_scene_node_set_enabled(&root->layers.shell_bottom->node, !fs);
625 wlr_scene_node_set_enabled(&root->layers.tiling->node, !fs);
626 wlr_scene_node_set_enabled(&root->layers.floating->node, !fs);
627 wlr_scene_node_set_enabled(&root->layers.shell_top->node, !fs);
628 wlr_scene_node_set_enabled(&root->layers.fullscreen->node, !fs);
271 629
272 // Damage the new location 630 // hide all contents in the scratchpad
273 desktop_damage_whole_container(container); 631 for (int i = 0; i < root->scratchpad->length; i++) {
274 if (view && view->surface) { 632 struct sway_container *con = root->scratchpad->items[i];
275 struct wlr_surface *surface = view->surface; 633
276 struct wlr_box box = { 634 wlr_scene_node_set_enabled(&con->scene_tree->node, false);
277 .x = container->current.content_x - view->geometry.x,
278 .y = container->current.content_y - view->geometry.y,
279 .width = surface->current.width,
280 .height = surface->current.height,
281 };
282 desktop_damage_box(&box);
283 } 635 }
284 636
285 if (!container->node.destroying) { 637 if (fs) {
286 container_discover_outputs(container); 638 for (int i = 0; i < root->outputs->length; i++) {
639 struct sway_output *output = root->outputs->items[i];
640 struct sway_workspace *ws = output->current.active_workspace;
641
642 if (ws) {
643 arrange_workspace_floating(ws);
644 }
645 }
646
647 arrange_fullscreen(root->layers.fullscreen_global, fs, NULL,
648 root->width, root->height);
649 } else {
650 for (int i = 0; i < root->outputs->length; i++) {
651 struct sway_output *output = root->outputs->items[i];
652
653 wlr_scene_output_set_position(output->scene_output, output->lx, output->ly);
654
655 wlr_scene_node_reparent(&output->layers.shell_background->node, root->layers.shell_background);
656 wlr_scene_node_reparent(&output->layers.shell_bottom->node, root->layers.shell_bottom);
657 wlr_scene_node_reparent(&output->layers.tiling->node, root->layers.tiling);
658 wlr_scene_node_reparent(&output->layers.shell_top->node, root->layers.shell_top);
659 wlr_scene_node_reparent(&output->layers.shell_overlay->node, root->layers.shell_overlay);
660 wlr_scene_node_reparent(&output->layers.fullscreen->node, root->layers.fullscreen);
661 wlr_scene_node_reparent(&output->layers.session_lock->node, root->layers.session_lock);
662
663 wlr_scene_node_set_position(&output->layers.shell_background->node, output->lx, output->ly);
664 wlr_scene_node_set_position(&output->layers.shell_bottom->node, output->lx, output->ly);
665 wlr_scene_node_set_position(&output->layers.tiling->node, output->lx, output->ly);
666 wlr_scene_node_set_position(&output->layers.fullscreen->node, output->lx, output->ly);
667 wlr_scene_node_set_position(&output->layers.shell_top->node, output->lx, output->ly);
668 wlr_scene_node_set_position(&output->layers.shell_overlay->node, output->lx, output->ly);
669 wlr_scene_node_set_position(&output->layers.session_lock->node, output->lx, output->ly);
670
671 arrange_output(output, output->width, output->height);
672 }
287 } 673 }
674
675 arrange_popups(root->layers.popup);
288} 676}
289 677
290/** 678/**
@@ -326,8 +714,6 @@ static void transaction_apply(struct sway_transaction *transaction) {
326 714
327 node->instruction = NULL; 715 node->instruction = NULL;
328 } 716 }
329
330 cursor_rebase_all();
331} 717}
332 718
333static void transaction_commit_pending(void); 719static void transaction_commit_pending(void);
@@ -340,11 +726,13 @@ static void transaction_progress(void) {
340 return; 726 return;
341 } 727 }
342 transaction_apply(server.queued_transaction); 728 transaction_apply(server.queued_transaction);
729 arrange_root(root);
730 cursor_rebase_all();
343 transaction_destroy(server.queued_transaction); 731 transaction_destroy(server.queued_transaction);
344 server.queued_transaction = NULL; 732 server.queued_transaction = NULL;
345 733
346 if (!server.pending_transaction) { 734 if (!server.pending_transaction) {
347 sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); 735 sway_idle_inhibit_v1_check_active();
348 return; 736 return;
349 } 737 }
350 738
@@ -415,21 +803,11 @@ static void transaction_commit(struct sway_transaction *transaction) {
415 ++transaction->num_waiting; 803 ++transaction->num_waiting;
416 } 804 }
417 805
418 // From here on we are rendering a saved buffer of the view, which 806 view_send_frame_done(node->sway_container->view);
419 // means we can send a frame done event to make the client redraw it
420 // as soon as possible. Additionally, this is required if a view is
421 // mapping and its default geometry doesn't intersect an output.
422 struct timespec now;
423 clock_gettime(CLOCK_MONOTONIC, &now);
424 wlr_surface_send_frame_done(
425 node->sway_container->view->surface, &now);
426 } 807 }
427 if (!hidden && node_is_view(node) && 808 if (!hidden && node_is_view(node) &&
428 wl_list_empty(&node->sway_container->view->saved_buffers)) { 809 !node->sway_container->view->saved_surface_tree) {
429 view_save_buffer(node->sway_container->view); 810 view_save_buffer(node->sway_container->view);
430 memcpy(&node->sway_container->view->saved_geometry,
431 &node->sway_container->view->geometry,
432 sizeof(struct wlr_box));
433 } 811 }
434 node->instruction = instruction; 812 node->instruction = instruction;
435 } 813 }
@@ -499,16 +877,18 @@ static void set_instruction_ready(
499 transaction_progress(); 877 transaction_progress();
500} 878}
501 879
502void transaction_notify_view_ready_by_serial(struct sway_view *view, 880bool transaction_notify_view_ready_by_serial(struct sway_view *view,
503 uint32_t serial) { 881 uint32_t serial) {
504 struct sway_transaction_instruction *instruction = 882 struct sway_transaction_instruction *instruction =
505 view->container->node.instruction; 883 view->container->node.instruction;
506 if (instruction != NULL && instruction->serial == serial) { 884 if (instruction != NULL && instruction->serial == serial) {
507 set_instruction_ready(instruction); 885 set_instruction_ready(instruction);
886 return true;
508 } 887 }
888 return false;
509} 889}
510 890
511void transaction_notify_view_ready_by_geometry(struct sway_view *view, 891bool transaction_notify_view_ready_by_geometry(struct sway_view *view,
512 double x, double y, int width, int height) { 892 double x, double y, int width, int height) {
513 struct sway_transaction_instruction *instruction = 893 struct sway_transaction_instruction *instruction =
514 view->container->node.instruction; 894 view->container->node.instruction;
@@ -518,7 +898,9 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view,
518 instruction->container_state.content_width == width && 898 instruction->container_state.content_width == width &&
519 instruction->container_state.content_height == height) { 899 instruction->container_state.content_height == height) {
520 set_instruction_ready(instruction); 900 set_instruction_ready(instruction);
901 return true;
521 } 902 }
903 return false;
522} 904}
523 905
524static void _transaction_commit_dirty(bool server_request) { 906static void _transaction_commit_dirty(bool server_request) {
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 8da922d5..7c417891 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 199309L
2#include <float.h> 1#include <float.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -7,7 +6,7 @@
7#include <wlr/util/edges.h> 6#include <wlr/util/edges.h>
8#include "log.h" 7#include "log.h"
9#include "sway/decoration.h" 8#include "sway/decoration.h"
10#include "sway/desktop.h" 9#include "sway/scene_descriptor.h"
11#include "sway/desktop/transaction.h" 10#include "sway/desktop/transaction.h"
12#include "sway/input/cursor.h" 11#include "sway/input/cursor.h"
13#include "sway/input/input-manager.h" 12#include "sway/input/input-manager.h"
@@ -19,55 +18,39 @@
19#include "sway/tree/workspace.h" 18#include "sway/tree/workspace.h"
20#include "sway/xdg_decoration.h" 19#include "sway/xdg_decoration.h"
21 20
22static const struct sway_view_child_impl popup_impl;
23
24static void popup_get_view_coords(struct sway_view_child *child,
25 int *sx, int *sy) {
26 struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
27 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
28
29 wlr_xdg_popup_get_toplevel_coords(wlr_popup,
30 wlr_popup->current.geometry.x - wlr_popup->base->current.geometry.x,
31 wlr_popup->current.geometry.y - wlr_popup->base->current.geometry.y,
32 sx, sy);
33}
34
35static void popup_destroy(struct sway_view_child *child) {
36 if (!sway_assert(child->impl == &popup_impl,
37 "Expected an xdg_shell popup")) {
38 return;
39 }
40 struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
41 wl_list_remove(&popup->new_popup.link);
42 wl_list_remove(&popup->destroy.link);
43 free(popup);
44}
45
46static const struct sway_view_child_impl popup_impl = {
47 .get_view_coords = popup_get_view_coords,
48 .destroy = popup_destroy,
49};
50
51static struct sway_xdg_popup *popup_create( 21static struct sway_xdg_popup *popup_create(
52 struct wlr_xdg_popup *wlr_popup, struct sway_view *view); 22 struct wlr_xdg_popup *wlr_popup, struct sway_view *view,
23 struct wlr_scene_tree *parent);
53 24
54static void popup_handle_new_popup(struct wl_listener *listener, void *data) { 25static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
55 struct sway_xdg_popup *popup = 26 struct sway_xdg_popup *popup =
56 wl_container_of(listener, popup, new_popup); 27 wl_container_of(listener, popup, new_popup);
57 struct wlr_xdg_popup *wlr_popup = data; 28 struct wlr_xdg_popup *wlr_popup = data;
58 popup_create(wlr_popup, popup->child.view); 29 popup_create(wlr_popup, popup->view, popup->xdg_surface_tree);
59} 30}
60 31
61static void popup_handle_destroy(struct wl_listener *listener, void *data) { 32static void popup_handle_destroy(struct wl_listener *listener, void *data) {
62 struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); 33 struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy);
63 view_child_destroy(&popup->child); 34
35 wl_list_remove(&popup->new_popup.link);
36 wl_list_remove(&popup->destroy.link);
37 wl_list_remove(&popup->surface_commit.link);
38 wl_list_remove(&popup->reposition.link);
39 wlr_scene_node_destroy(&popup->scene_tree->node);
40 free(popup);
64} 41}
65 42
66static void popup_unconstrain(struct sway_xdg_popup *popup) { 43static void popup_unconstrain(struct sway_xdg_popup *popup) {
67 struct sway_view *view = popup->child.view; 44 struct sway_view *view = popup->view;
68 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; 45 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
69 46
70 struct sway_output *output = view->container->pending.workspace->output; 47 struct sway_workspace *workspace = view->container->pending.workspace;
48 if (!workspace) {
49 // is null if in the scratchpad
50 return;
51 }
52
53 struct sway_output *output = workspace->output;
71 54
72 // the output box expressed in the coordinate system of the toplevel parent 55 // the output box expressed in the coordinate system of the toplevel parent
73 // of the popup 56 // of the popup
@@ -81,32 +64,72 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) {
81 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 64 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
82} 65}
83 66
84static struct sway_xdg_popup *popup_create( 67static void popup_handle_surface_commit(struct wl_listener *listener, void *data) {
85 struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { 68 struct sway_xdg_popup *popup = wl_container_of(listener, popup, surface_commit);
69 if (popup->wlr_xdg_popup->base->initial_commit) {
70 popup_unconstrain(popup);
71 }
72}
73
74static void popup_handle_reposition(struct wl_listener *listener, void *data) {
75 struct sway_xdg_popup *popup = wl_container_of(listener, popup, reposition);
76 popup_unconstrain(popup);
77}
78
79static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup,
80 struct sway_view *view, struct wlr_scene_tree *parent) {
86 struct wlr_xdg_surface *xdg_surface = wlr_popup->base; 81 struct wlr_xdg_surface *xdg_surface = wlr_popup->base;
87 82
88 struct sway_xdg_popup *popup = 83 struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup));
89 calloc(1, sizeof(struct sway_xdg_popup)); 84 if (!popup) {
90 if (popup == NULL) { 85 return NULL;
86 }
87
88 popup->wlr_xdg_popup = wlr_popup;
89 popup->view = view;
90
91 popup->scene_tree = wlr_scene_tree_create(parent);
92 if (!popup->scene_tree) {
93 free(popup);
91 return NULL; 94 return NULL;
92 } 95 }
93 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); 96
97 popup->xdg_surface_tree = wlr_scene_xdg_surface_create(
98 popup->scene_tree, xdg_surface);
99 if (!popup->xdg_surface_tree) {
100 wlr_scene_node_destroy(&popup->scene_tree->node);
101 free(popup);
102 return NULL;
103 }
104
105 popup->desc.relative = &view->content_tree->node;
106 popup->desc.view = view;
107
108 if (!scene_descriptor_assign(&popup->scene_tree->node,
109 SWAY_SCENE_DESC_POPUP, &popup->desc)) {
110 sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor");
111 wlr_scene_node_destroy(&popup->scene_tree->node);
112 free(popup);
113 return NULL;
114 }
115
94 popup->wlr_xdg_popup = xdg_surface->popup; 116 popup->wlr_xdg_popup = xdg_surface->popup;
117 struct sway_xdg_shell_view *shell_view =
118 wl_container_of(view, shell_view, view);
119 xdg_surface->data = shell_view;
95 120
121 wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit);
122 popup->surface_commit.notify = popup_handle_surface_commit;
96 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); 123 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
97 popup->new_popup.notify = popup_handle_new_popup; 124 popup->new_popup.notify = popup_handle_new_popup;
98 wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); 125 wl_signal_add(&wlr_popup->events.reposition, &popup->reposition);
126 popup->reposition.notify = popup_handle_reposition;
127 wl_signal_add(&wlr_popup->events.destroy, &popup->destroy);
99 popup->destroy.notify = popup_handle_destroy; 128 popup->destroy.notify = popup_handle_destroy;
100 129
101 wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map);
102 wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap);
103
104 popup_unconstrain(popup);
105
106 return popup; 130 return popup;
107} 131}
108 132
109
110static struct sway_xdg_shell_view *xdg_shell_view_from_view( 133static struct sway_xdg_shell_view *xdg_shell_view_from_view(
111 struct sway_view *view) { 134 struct sway_view *view) {
112 if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, 135 if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL,
@@ -163,12 +186,19 @@ static void set_tiled(struct sway_view *view, bool tiled) {
163 if (xdg_shell_view_from_view(view) == NULL) { 186 if (xdg_shell_view_from_view(view) == NULL) {
164 return; 187 return;
165 } 188 }
166 enum wlr_edges edges = WLR_EDGE_NONE; 189 if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >=
167 if (tiled) { 190 XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) {
168 edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | 191 enum wlr_edges edges = WLR_EDGE_NONE;
169 WLR_EDGE_BOTTOM; 192 if (tiled) {
193 edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP |
194 WLR_EDGE_BOTTOM;
195 }
196 wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges);
197 } else {
198 // The version is too low for the tiled state; configure as maximized instead
199 // to stop the client from drawing decorations outside of the toplevel geometry.
200 wlr_xdg_toplevel_set_maximized(view->wlr_xdg_toplevel, tiled);
170 } 201 }
171 wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges);
172} 202}
173 203
174static void set_fullscreen(struct sway_view *view, bool fullscreen) { 204static void set_fullscreen(struct sway_view *view, bool fullscreen) {
@@ -194,24 +224,6 @@ static bool wants_floating(struct sway_view *view) {
194 || toplevel->parent; 224 || toplevel->parent;
195} 225}
196 226
197static void for_each_surface(struct sway_view *view,
198 wlr_surface_iterator_func_t iterator, void *user_data) {
199 if (xdg_shell_view_from_view(view) == NULL) {
200 return;
201 }
202 wlr_xdg_surface_for_each_surface(view->wlr_xdg_toplevel->base, iterator,
203 user_data);
204}
205
206static void for_each_popup_surface(struct sway_view *view,
207 wlr_surface_iterator_func_t iterator, void *user_data) {
208 if (xdg_shell_view_from_view(view) == NULL) {
209 return;
210 }
211 wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_toplevel->base,
212 iterator, user_data);
213}
214
215static bool is_transient_for(struct sway_view *child, 227static bool is_transient_for(struct sway_view *child,
216 struct sway_view *ancestor) { 228 struct sway_view *ancestor) {
217 if (xdg_shell_view_from_view(child) == NULL) { 229 if (xdg_shell_view_from_view(child) == NULL) {
@@ -259,8 +271,6 @@ static const struct sway_view_impl view_impl = {
259 .set_fullscreen = set_fullscreen, 271 .set_fullscreen = set_fullscreen,
260 .set_resizing = set_resizing, 272 .set_resizing = set_resizing,
261 .wants_floating = wants_floating, 273 .wants_floating = wants_floating,
262 .for_each_surface = for_each_surface,
263 .for_each_popup_surface = for_each_popup_surface,
264 .is_transient_for = is_transient_for, 274 .is_transient_for = is_transient_for,
265 .close = _close, 275 .close = _close,
266 .close_popups = close_popups, 276 .close_popups = close_popups,
@@ -273,6 +283,20 @@ static void handle_commit(struct wl_listener *listener, void *data) {
273 struct sway_view *view = &xdg_shell_view->view; 283 struct sway_view *view = &xdg_shell_view->view;
274 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; 284 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base;
275 285
286 if (xdg_surface->initial_commit) {
287 if (view->xdg_decoration != NULL) {
288 set_xdg_decoration_mode(view->xdg_decoration);
289 }
290 // XXX: https://github.com/swaywm/sway/issues/2176
291 wlr_xdg_surface_schedule_configure(xdg_surface);
292 // TODO: wlr_xdg_toplevel_set_bounds()
293 return;
294 }
295
296 if (!xdg_surface->surface->mapped) {
297 return;
298 }
299
276 struct wlr_box new_geo; 300 struct wlr_box new_geo;
277 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); 301 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo);
278 bool new_size = new_geo.width != view->geometry.width || 302 bool new_size = new_geo.width != view->geometry.width ||
@@ -284,23 +308,32 @@ static void handle_commit(struct wl_listener *listener, void *data) {
284 // The client changed its surface size in this commit. For floating 308 // The client changed its surface size in this commit. For floating
285 // containers, we resize the container to match. For tiling containers, 309 // containers, we resize the container to match. For tiling containers,
286 // we only recenter the surface. 310 // we only recenter the surface.
287 desktop_damage_view(view);
288 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); 311 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
289 if (container_is_floating(view->container)) { 312 if (container_is_floating(view->container)) {
290 view_update_size(view); 313 view_update_size(view);
314 // Only set the toplevel size the current container actually has a size.
315 if (view->container->current.width) {
316 wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, view->geometry.width,
317 view->geometry.height);
318 }
291 transaction_commit_dirty_client(); 319 transaction_commit_dirty_client();
292 } else {
293 view_center_surface(view);
294 } 320 }
295 desktop_damage_view(view); 321
322 view_center_and_clip_surface(view);
296 } 323 }
297 324
298 if (view->container->node.instruction) { 325 if (view->container->node.instruction) {
299 transaction_notify_view_ready_by_serial(view, 326 bool successful = transaction_notify_view_ready_by_serial(view,
300 xdg_surface->current.configure_serial); 327 xdg_surface->current.configure_serial);
301 }
302 328
303 view_damage_from(view); 329 // If we saved the view and this commit isn't what we're looking for
330 // that means the user will never actually see the buffers submitted to
331 // us here. Just send frame done events to these surfaces so they can
332 // commit another time for us.
333 if (view->saved_surface_tree && !successful) {
334 view_send_frame_done(view);
335 }
336 }
304} 337}
305 338
306static void handle_set_title(struct wl_listener *listener, void *data) { 339static void handle_set_title(struct wl_listener *listener, void *data) {
@@ -315,6 +348,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) {
315 struct sway_xdg_shell_view *xdg_shell_view = 348 struct sway_xdg_shell_view *xdg_shell_view =
316 wl_container_of(listener, xdg_shell_view, set_app_id); 349 wl_container_of(listener, xdg_shell_view, set_app_id);
317 struct sway_view *view = &xdg_shell_view->view; 350 struct sway_view *view = &xdg_shell_view->view;
351 view_update_app_id(view);
318 view_execute_criteria(view); 352 view_execute_criteria(view);
319} 353}
320 354
@@ -322,7 +356,16 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
322 struct sway_xdg_shell_view *xdg_shell_view = 356 struct sway_xdg_shell_view *xdg_shell_view =
323 wl_container_of(listener, xdg_shell_view, new_popup); 357 wl_container_of(listener, xdg_shell_view, new_popup);
324 struct wlr_xdg_popup *wlr_popup = data; 358 struct wlr_xdg_popup *wlr_popup = data;
325 popup_create(wlr_popup, &xdg_shell_view->view); 359
360 struct sway_xdg_popup *popup = popup_create(wlr_popup,
361 &xdg_shell_view->view, root->layers.popup);
362 if (!popup) {
363 return;
364 }
365
366 int lx, ly;
367 wlr_scene_node_coords(&popup->view->content_tree->node, &lx, &ly);
368 wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly);
326} 369}
327 370
328static void handle_request_maximize(struct wl_listener *listener, void *data) { 371static void handle_request_maximize(struct wl_listener *listener, void *data) {
@@ -338,7 +381,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
338 struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; 381 struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel;
339 struct sway_view *view = &xdg_shell_view->view; 382 struct sway_view *view = &xdg_shell_view->view;
340 383
341 if (!toplevel->base->mapped) { 384 if (!toplevel->base->surface->mapped) {
342 return; 385 return;
343 } 386 }
344 387
@@ -403,7 +446,6 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
403 446
404 view_unmap(view); 447 view_unmap(view);
405 448
406 wl_list_remove(&xdg_shell_view->commit.link);
407 wl_list_remove(&xdg_shell_view->new_popup.link); 449 wl_list_remove(&xdg_shell_view->new_popup.link);
408 wl_list_remove(&xdg_shell_view->request_maximize.link); 450 wl_list_remove(&xdg_shell_view->request_maximize.link);
409 wl_list_remove(&xdg_shell_view->request_fullscreen.link); 451 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
@@ -446,10 +488,6 @@ static void handle_map(struct wl_listener *listener, void *data) {
446 488
447 transaction_commit_dirty(); 489 transaction_commit_dirty();
448 490
449 xdg_shell_view->commit.notify = handle_commit;
450 wl_signal_add(&toplevel->base->surface->events.commit,
451 &xdg_shell_view->commit);
452
453 xdg_shell_view->new_popup.notify = handle_new_popup; 491 xdg_shell_view->new_popup.notify = handle_new_popup;
454 wl_signal_add(&toplevel->base->events.new_popup, 492 wl_signal_add(&toplevel->base->events.new_popup,
455 &xdg_shell_view->new_popup); 493 &xdg_shell_view->new_popup);
@@ -489,6 +527,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
489 wl_list_remove(&xdg_shell_view->destroy.link); 527 wl_list_remove(&xdg_shell_view->destroy.link);
490 wl_list_remove(&xdg_shell_view->map.link); 528 wl_list_remove(&xdg_shell_view->map.link);
491 wl_list_remove(&xdg_shell_view->unmap.link); 529 wl_list_remove(&xdg_shell_view->unmap.link);
530 wl_list_remove(&xdg_shell_view->commit.link);
492 view->wlr_xdg_toplevel = NULL; 531 view->wlr_xdg_toplevel = NULL;
493 if (view->xdg_decoration) { 532 if (view->xdg_decoration) {
494 view->xdg_decoration->view = NULL; 533 view->xdg_decoration->view = NULL;
@@ -501,17 +540,12 @@ struct sway_view *view_from_wlr_xdg_surface(
501 return xdg_surface->data; 540 return xdg_surface->data;
502} 541}
503 542
504void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { 543void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) {
505 struct wlr_xdg_surface *xdg_surface = data; 544 struct wlr_xdg_toplevel *xdg_toplevel = data;
506
507 if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
508 sway_log(SWAY_DEBUG, "New xdg_shell popup");
509 return;
510 }
511 545
512 sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", 546 sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'",
513 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); 547 xdg_toplevel->title, xdg_toplevel->app_id);
514 wlr_xdg_surface_ping(xdg_surface); 548 wlr_xdg_surface_ping(xdg_toplevel->base);
515 549
516 struct sway_xdg_shell_view *xdg_shell_view = 550 struct sway_xdg_shell_view *xdg_shell_view =
517 calloc(1, sizeof(struct sway_xdg_shell_view)); 551 calloc(1, sizeof(struct sway_xdg_shell_view));
@@ -519,17 +553,29 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
519 return; 553 return;
520 } 554 }
521 555
522 view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); 556 if (!view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl)) {
523 xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel; 557 free(xdg_shell_view);
558 return;
559 }
560 xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel;
524 561
525 xdg_shell_view->map.notify = handle_map; 562 xdg_shell_view->map.notify = handle_map;
526 wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); 563 wl_signal_add(&xdg_toplevel->base->surface->events.map, &xdg_shell_view->map);
527 564
528 xdg_shell_view->unmap.notify = handle_unmap; 565 xdg_shell_view->unmap.notify = handle_unmap;
529 wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap); 566 wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &xdg_shell_view->unmap);
567
568 xdg_shell_view->commit.notify = handle_commit;
569 wl_signal_add(&xdg_toplevel->base->surface->events.commit,
570 &xdg_shell_view->commit);
530 571
531 xdg_shell_view->destroy.notify = handle_destroy; 572 xdg_shell_view->destroy.notify = handle_destroy;
532 wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); 573 wl_signal_add(&xdg_toplevel->events.destroy, &xdg_shell_view->destroy);
574
575 wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base);
576
577 xdg_toplevel->base->data = xdg_shell_view;
533 578
534 xdg_surface->data = xdg_shell_view; 579 wlr_xdg_toplevel_set_wm_capabilities(xdg_toplevel,
580 XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
535} 581}
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 7c5dde53..270cf08f 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -1,21 +1,23 @@
1#define _POSIX_C_SOURCE 199309L
2#include <float.h> 1#include <float.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
5#include <wayland-server-core.h> 4#include <wayland-server-core.h>
6#include <wlr/types/wlr_output_layout.h> 5#include <wlr/types/wlr_output_layout.h>
7#include <wlr/types/wlr_output.h> 6#include <wlr/types/wlr_output.h>
7#include <wlr/types/wlr_xdg_activation_v1.h>
8#include <wlr/types/wlr_scene.h>
8#include <wlr/xwayland.h> 9#include <wlr/xwayland.h>
9#include <xcb/xcb_icccm.h> 10#include <xcb/xcb_icccm.h>
10#include "log.h" 11#include "log.h"
11#include "sway/desktop.h"
12#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
13#include "sway/input/cursor.h" 13#include "sway/input/cursor.h"
14#include "sway/input/input-manager.h" 14#include "sway/input/input-manager.h"
15#include "sway/input/seat.h" 15#include "sway/input/seat.h"
16#include "sway/output.h" 16#include "sway/output.h"
17#include "sway/scene_descriptor.h"
17#include "sway/tree/arrange.h" 18#include "sway/tree/arrange.h"
18#include "sway/tree/container.h" 19#include "sway/tree/container.h"
20#include "sway/server.h"
19#include "sway/tree/view.h" 21#include "sway/tree/view.h"
20#include "sway/tree/workspace.h" 22#include "sway/tree/workspace.h"
21 23
@@ -43,29 +45,12 @@ static void unmanaged_handle_request_configure(struct wl_listener *listener,
43 ev->width, ev->height); 45 ev->width, ev->height);
44} 46}
45 47
46static void unmanaged_handle_commit(struct wl_listener *listener, void *data) {
47 struct sway_xwayland_unmanaged *surface =
48 wl_container_of(listener, surface, commit);
49 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
50
51 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
52 false);
53}
54
55static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { 48static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) {
56 struct sway_xwayland_unmanaged *surface = 49 struct sway_xwayland_unmanaged *surface =
57 wl_container_of(listener, surface, set_geometry); 50 wl_container_of(listener, surface, set_geometry);
58 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 51 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
59 52
60 if (xsurface->x != surface->lx || xsurface->y != surface->ly) { 53 wlr_scene_node_set_position(&surface->surface_scene->buffer->node, xsurface->x, xsurface->y);
61 // Surface has moved
62 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
63 true);
64 surface->lx = xsurface->x;
65 surface->ly = xsurface->y;
66 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
67 true);
68 }
69} 54}
70 55
71static void unmanaged_handle_map(struct wl_listener *listener, void *data) { 56static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
@@ -73,17 +58,18 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
73 wl_container_of(listener, surface, map); 58 wl_container_of(listener, surface, map);
74 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 59 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
75 60
76 wl_list_insert(root->xwayland_unmanaged.prev, &surface->link); 61 surface->surface_scene = wlr_scene_surface_create(root->layers.unmanaged,
62 xsurface->surface);
77 63
78 wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); 64 if (surface->surface_scene) {
79 surface->set_geometry.notify = unmanaged_handle_set_geometry; 65 scene_descriptor_assign(&surface->surface_scene->buffer->node,
66 SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, surface);
67 wlr_scene_node_set_position(&surface->surface_scene->buffer->node,
68 xsurface->x, xsurface->y);
80 69
81 wl_signal_add(&xsurface->surface->events.commit, &surface->commit); 70 wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry);
82 surface->commit.notify = unmanaged_handle_commit; 71 surface->set_geometry.notify = unmanaged_handle_set_geometry;
83 72 }
84 surface->lx = xsurface->x;
85 surface->ly = xsurface->y;
86 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true);
87 73
88 if (wlr_xwayland_or_surface_wants_focus(xsurface)) { 74 if (wlr_xwayland_or_surface_wants_focus(xsurface)) {
89 struct sway_seat *seat = input_manager_current_seat(); 75 struct sway_seat *seat = input_manager_current_seat();
@@ -97,10 +83,13 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
97 struct sway_xwayland_unmanaged *surface = 83 struct sway_xwayland_unmanaged *surface =
98 wl_container_of(listener, surface, unmap); 84 wl_container_of(listener, surface, unmap);
99 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 85 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
100 desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); 86
101 wl_list_remove(&surface->link); 87 if (surface->surface_scene) {
102 wl_list_remove(&surface->set_geometry.link); 88 wl_list_remove(&surface->set_geometry.link);
103 wl_list_remove(&surface->commit.link); 89
90 wlr_scene_node_destroy(&surface->surface_scene->buffer->node);
91 surface->surface_scene = NULL;
92 }
104 93
105 struct sway_seat *seat = input_manager_current_seat(); 94 struct sway_seat *seat = input_manager_current_seat();
106 if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { 95 if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) {
@@ -123,8 +112,10 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
123} 112}
124 113
125static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) { 114static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) {
126 struct wlr_xwayland_surface *xsurface = data; 115 struct sway_xwayland_unmanaged *surface =
127 if (!xsurface->mapped) { 116 wl_container_of(listener, surface, request_activate);
117 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
118 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
128 return; 119 return;
129 } 120 }
130 struct sway_seat *seat = input_manager_current_seat(); 121 struct sway_seat *seat = input_manager_current_seat();
@@ -136,12 +127,29 @@ static void unmanaged_handle_request_activate(struct wl_listener *listener, void
136 seat_set_focus_surface(seat, xsurface->surface, false); 127 seat_set_focus_surface(seat, xsurface->surface, false);
137} 128}
138 129
130static void unmanaged_handle_associate(struct wl_listener *listener, void *data) {
131 struct sway_xwayland_unmanaged *surface =
132 wl_container_of(listener, surface, associate);
133 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
134 wl_signal_add(&xsurface->surface->events.map, &surface->map);
135 surface->map.notify = unmanaged_handle_map;
136 wl_signal_add(&xsurface->surface->events.unmap, &surface->unmap);
137 surface->unmap.notify = unmanaged_handle_unmap;
138}
139
140static void unmanaged_handle_dissociate(struct wl_listener *listener, void *data) {
141 struct sway_xwayland_unmanaged *surface =
142 wl_container_of(listener, surface, dissociate);
143 wl_list_remove(&surface->map.link);
144 wl_list_remove(&surface->unmap.link);
145}
146
139static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { 147static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
140 struct sway_xwayland_unmanaged *surface = 148 struct sway_xwayland_unmanaged *surface =
141 wl_container_of(listener, surface, destroy); 149 wl_container_of(listener, surface, destroy);
142 wl_list_remove(&surface->request_configure.link); 150 wl_list_remove(&surface->request_configure.link);
143 wl_list_remove(&surface->map.link); 151 wl_list_remove(&surface->associate.link);
144 wl_list_remove(&surface->unmap.link); 152 wl_list_remove(&surface->dissociate.link);
145 wl_list_remove(&surface->destroy.link); 153 wl_list_remove(&surface->destroy.link);
146 wl_list_remove(&surface->override_redirect.link); 154 wl_list_remove(&surface->override_redirect.link);
147 wl_list_remove(&surface->request_activate.link); 155 wl_list_remove(&surface->request_activate.link);
@@ -149,6 +157,7 @@ static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
149} 157}
150 158
151static void handle_map(struct wl_listener *listener, void *data); 159static void handle_map(struct wl_listener *listener, void *data);
160static void handle_associate(struct wl_listener *listener, void *data);
152 161
153struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface); 162struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface);
154 163
@@ -157,14 +166,22 @@ static void unmanaged_handle_override_redirect(struct wl_listener *listener, voi
157 wl_container_of(listener, surface, override_redirect); 166 wl_container_of(listener, surface, override_redirect);
158 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 167 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
159 168
160 bool mapped = xsurface->mapped; 169 bool associated = xsurface->surface != NULL;
170 bool mapped = associated && xsurface->surface->mapped;
161 if (mapped) { 171 if (mapped) {
162 unmanaged_handle_unmap(&surface->unmap, NULL); 172 unmanaged_handle_unmap(&surface->unmap, NULL);
163 } 173 }
174 if (associated) {
175 unmanaged_handle_dissociate(&surface->dissociate, NULL);
176 }
164 177
165 unmanaged_handle_destroy(&surface->destroy, NULL); 178 unmanaged_handle_destroy(&surface->destroy, NULL);
166 xsurface->data = NULL; 179 xsurface->data = NULL;
180
167 struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface); 181 struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface);
182 if (associated) {
183 handle_associate(&xwayland_view->associate, NULL);
184 }
168 if (mapped) { 185 if (mapped) {
169 handle_map(&xwayland_view->map, xsurface); 186 handle_map(&xwayland_view->map, xsurface);
170 } 187 }
@@ -184,10 +201,10 @@ static struct sway_xwayland_unmanaged *create_unmanaged(
184 wl_signal_add(&xsurface->events.request_configure, 201 wl_signal_add(&xsurface->events.request_configure,
185 &surface->request_configure); 202 &surface->request_configure);
186 surface->request_configure.notify = unmanaged_handle_request_configure; 203 surface->request_configure.notify = unmanaged_handle_request_configure;
187 wl_signal_add(&xsurface->events.map, &surface->map); 204 wl_signal_add(&xsurface->events.associate, &surface->associate);
188 surface->map.notify = unmanaged_handle_map; 205 surface->associate.notify = unmanaged_handle_associate;
189 wl_signal_add(&xsurface->events.unmap, &surface->unmap); 206 wl_signal_add(&xsurface->events.dissociate, &surface->dissociate);
190 surface->unmap.notify = unmanaged_handle_unmap; 207 surface->dissociate.notify = unmanaged_handle_dissociate;
191 wl_signal_add(&xsurface->events.destroy, &surface->destroy); 208 wl_signal_add(&xsurface->events.destroy, &surface->destroy);
192 surface->destroy.notify = unmanaged_handle_destroy; 209 surface->destroy.notify = unmanaged_handle_destroy;
193 wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); 210 wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect);
@@ -396,17 +413,6 @@ static const struct sway_view_impl view_impl = {
396 .destroy = destroy, 413 .destroy = destroy,
397}; 414};
398 415
399static void get_geometry(struct sway_view *view, struct wlr_box *box) {
400 box->x = box->y = 0;
401 if (view->surface) {
402 box->width = view->surface->current.width;
403 box->height = view->surface->current.height;
404 } else {
405 box->width = 0;
406 box->height = 0;
407 }
408}
409
410static void handle_commit(struct wl_listener *listener, void *data) { 416static void handle_commit(struct wl_listener *listener, void *data) {
411 struct sway_xwayland_view *xwayland_view = 417 struct sway_xwayland_view *xwayland_view =
412 wl_container_of(listener, xwayland_view, commit); 418 wl_container_of(listener, xwayland_view, commit);
@@ -414,34 +420,38 @@ static void handle_commit(struct wl_listener *listener, void *data) {
414 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 420 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
415 struct wlr_surface_state *state = &xsurface->surface->current; 421 struct wlr_surface_state *state = &xsurface->surface->current;
416 422
417 struct wlr_box new_geo; 423 struct wlr_box new_geo = {0};
418 get_geometry(view, &new_geo); 424 new_geo.width = state->width;
425 new_geo.height = state->height;
426
419 bool new_size = new_geo.width != view->geometry.width || 427 bool new_size = new_geo.width != view->geometry.width ||
420 new_geo.height != view->geometry.height || 428 new_geo.height != view->geometry.height;
421 new_geo.x != view->geometry.x ||
422 new_geo.y != view->geometry.y;
423 429
424 if (new_size) { 430 if (new_size) {
425 // The client changed its surface size in this commit. For floating 431 // The client changed its surface size in this commit. For floating
426 // containers, we resize the container to match. For tiling containers, 432 // containers, we resize the container to match. For tiling containers,
427 // we only recenter the surface. 433 // we only recenter the surface.
428 desktop_damage_view(view);
429 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); 434 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
430 if (container_is_floating(view->container)) { 435 if (container_is_floating(view->container)) {
431 view_update_size(view); 436 view_update_size(view);
432 transaction_commit_dirty_client(); 437 transaction_commit_dirty_client();
433 } else {
434 view_center_surface(view);
435 } 438 }
436 desktop_damage_view(view); 439
440 view_center_and_clip_surface(view);
437 } 441 }
438 442
439 if (view->container->node.instruction) { 443 if (view->container->node.instruction) {
440 transaction_notify_view_ready_by_geometry(view, 444 bool successful = transaction_notify_view_ready_by_geometry(view,
441 xsurface->x, xsurface->y, state->width, state->height); 445 xsurface->x, xsurface->y, state->width, state->height);
442 }
443 446
444 view_damage_from(view); 447 // If we saved the view and this commit isn't what we're looking for
448 // that means the user will never actually see the buffers submitted to
449 // us here. Just send frame done events to these surfaces so they can
450 // commit another time for us.
451 if (view->saved_surface_tree && !successful) {
452 view_send_frame_done(view);
453 }
454 }
445} 455}
446 456
447static void handle_destroy(struct wl_listener *listener, void *data) { 457static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -466,11 +476,12 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
466 wl_list_remove(&xwayland_view->set_title.link); 476 wl_list_remove(&xwayland_view->set_title.link);
467 wl_list_remove(&xwayland_view->set_class.link); 477 wl_list_remove(&xwayland_view->set_class.link);
468 wl_list_remove(&xwayland_view->set_role.link); 478 wl_list_remove(&xwayland_view->set_role.link);
479 wl_list_remove(&xwayland_view->set_startup_id.link);
469 wl_list_remove(&xwayland_view->set_window_type.link); 480 wl_list_remove(&xwayland_view->set_window_type.link);
470 wl_list_remove(&xwayland_view->set_hints.link); 481 wl_list_remove(&xwayland_view->set_hints.link);
471 wl_list_remove(&xwayland_view->set_decorations.link); 482 wl_list_remove(&xwayland_view->set_decorations.link);
472 wl_list_remove(&xwayland_view->map.link); 483 wl_list_remove(&xwayland_view->associate.link);
473 wl_list_remove(&xwayland_view->unmap.link); 484 wl_list_remove(&xwayland_view->dissociate.link);
474 wl_list_remove(&xwayland_view->override_redirect.link); 485 wl_list_remove(&xwayland_view->override_redirect.link);
475 view_begin_destroy(&xwayland_view->view); 486 view_begin_destroy(&xwayland_view->view);
476} 487}
@@ -484,16 +495,28 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
484 return; 495 return;
485 } 496 }
486 497
498 wl_list_remove(&xwayland_view->commit.link);
499 wl_list_remove(&xwayland_view->surface_tree_destroy.link);
500
501 if (xwayland_view->surface_tree) {
502 wlr_scene_node_destroy(&xwayland_view->surface_tree->node);
503 xwayland_view->surface_tree = NULL;
504 }
505
487 view_unmap(view); 506 view_unmap(view);
507}
488 508
489 wl_list_remove(&xwayland_view->commit.link); 509static void handle_surface_tree_destroy(struct wl_listener *listener, void *data) {
510 struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view,
511 surface_tree_destroy);
512 xwayland_view->surface_tree = NULL;
490} 513}
491 514
492static void handle_map(struct wl_listener *listener, void *data) { 515static void handle_map(struct wl_listener *listener, void *data) {
493 struct sway_xwayland_view *xwayland_view = 516 struct sway_xwayland_view *xwayland_view =
494 wl_container_of(listener, xwayland_view, map); 517 wl_container_of(listener, xwayland_view, map);
495 struct wlr_xwayland_surface *xsurface = data;
496 struct sway_view *view = &xwayland_view->view; 518 struct sway_view *view = &xwayland_view->view;
519 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
497 520
498 view->natural_width = xsurface->width; 521 view->natural_width = xsurface->width;
499 view->natural_height = xsurface->height; 522 view->natural_height = xsurface->height;
@@ -506,23 +529,42 @@ static void handle_map(struct wl_listener *listener, void *data) {
506 // Put it back into the tree 529 // Put it back into the tree
507 view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); 530 view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false);
508 531
532 xwayland_view->surface_tree = wlr_scene_subsurface_tree_create(
533 xwayland_view->view.content_tree, xsurface->surface);
534
535 if (xwayland_view->surface_tree) {
536 xwayland_view->surface_tree_destroy.notify = handle_surface_tree_destroy;
537 wl_signal_add(&xwayland_view->surface_tree->node.events.destroy,
538 &xwayland_view->surface_tree_destroy);
539 }
540
509 transaction_commit_dirty(); 541 transaction_commit_dirty();
510} 542}
511 543
544static void handle_dissociate(struct wl_listener *listener, void *data);
545
512static void handle_override_redirect(struct wl_listener *listener, void *data) { 546static void handle_override_redirect(struct wl_listener *listener, void *data) {
513 struct sway_xwayland_view *xwayland_view = 547 struct sway_xwayland_view *xwayland_view =
514 wl_container_of(listener, xwayland_view, override_redirect); 548 wl_container_of(listener, xwayland_view, override_redirect);
515 struct wlr_xwayland_surface *xsurface = data;
516 struct sway_view *view = &xwayland_view->view; 549 struct sway_view *view = &xwayland_view->view;
550 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
517 551
518 bool mapped = xsurface->mapped; 552 bool associated = xsurface->surface != NULL;
553 bool mapped = associated && xsurface->surface->mapped;
519 if (mapped) { 554 if (mapped) {
520 handle_unmap(&xwayland_view->unmap, NULL); 555 handle_unmap(&xwayland_view->unmap, NULL);
521 } 556 }
557 if (associated) {
558 handle_dissociate(&xwayland_view->dissociate, NULL);
559 }
522 560
523 handle_destroy(&xwayland_view->destroy, view); 561 handle_destroy(&xwayland_view->destroy, view);
524 xsurface->data = NULL; 562 xsurface->data = NULL;
563
525 struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); 564 struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface);
565 if (associated) {
566 unmanaged_handle_associate(&unmanaged->associate, NULL);
567 }
526 if (mapped) { 568 if (mapped) {
527 unmanaged_handle_map(&unmanaged->map, xsurface); 569 unmanaged_handle_map(&unmanaged->map, xsurface);
528 } 570 }
@@ -534,7 +576,7 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
534 struct wlr_xwayland_surface_configure_event *ev = data; 576 struct wlr_xwayland_surface_configure_event *ev = data;
535 struct sway_view *view = &xwayland_view->view; 577 struct sway_view *view = &xwayland_view->view;
536 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 578 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
537 if (!xsurface->mapped) { 579 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
538 wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, 580 wlr_xwayland_surface_configure(xsurface, ev->x, ev->y,
539 ev->width, ev->height); 581 ev->width, ev->height);
540 return; 582 return;
@@ -563,7 +605,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
563 wl_container_of(listener, xwayland_view, request_fullscreen); 605 wl_container_of(listener, xwayland_view, request_fullscreen);
564 struct sway_view *view = &xwayland_view->view; 606 struct sway_view *view = &xwayland_view->view;
565 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 607 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
566 if (!xsurface->mapped) { 608 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
567 return; 609 return;
568 } 610 }
569 container_set_fullscreen(view->container, xsurface->fullscreen); 611 container_set_fullscreen(view->container, xsurface->fullscreen);
@@ -577,7 +619,7 @@ static void handle_request_minimize(struct wl_listener *listener, void *data) {
577 wl_container_of(listener, xwayland_view, request_minimize); 619 wl_container_of(listener, xwayland_view, request_minimize);
578 struct sway_view *view = &xwayland_view->view; 620 struct sway_view *view = &xwayland_view->view;
579 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 621 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
580 if (!xsurface->mapped) { 622 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
581 return; 623 return;
582 } 624 }
583 625
@@ -592,7 +634,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
592 wl_container_of(listener, xwayland_view, request_move); 634 wl_container_of(listener, xwayland_view, request_move);
593 struct sway_view *view = &xwayland_view->view; 635 struct sway_view *view = &xwayland_view->view;
594 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 636 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
595 if (!xsurface->mapped) { 637 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
596 return; 638 return;
597 } 639 }
598 if (!container_is_floating(view->container) || 640 if (!container_is_floating(view->container) ||
@@ -608,7 +650,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
608 wl_container_of(listener, xwayland_view, request_resize); 650 wl_container_of(listener, xwayland_view, request_resize);
609 struct sway_view *view = &xwayland_view->view; 651 struct sway_view *view = &xwayland_view->view;
610 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 652 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
611 if (!xsurface->mapped) { 653 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
612 return; 654 return;
613 } 655 }
614 if (!container_is_floating(view->container)) { 656 if (!container_is_floating(view->container)) {
@@ -624,10 +666,10 @@ static void handle_request_activate(struct wl_listener *listener, void *data) {
624 wl_container_of(listener, xwayland_view, request_activate); 666 wl_container_of(listener, xwayland_view, request_activate);
625 struct sway_view *view = &xwayland_view->view; 667 struct sway_view *view = &xwayland_view->view;
626 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 668 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
627 if (!xsurface->mapped) { 669 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
628 return; 670 return;
629 } 671 }
630 view_request_activate(view); 672 view_request_activate(view, NULL);
631 673
632 transaction_commit_dirty(); 674 transaction_commit_dirty();
633} 675}
@@ -637,7 +679,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) {
637 wl_container_of(listener, xwayland_view, set_title); 679 wl_container_of(listener, xwayland_view, set_title);
638 struct sway_view *view = &xwayland_view->view; 680 struct sway_view *view = &xwayland_view->view;
639 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 681 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
640 if (!xsurface->mapped) { 682 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
641 return; 683 return;
642 } 684 }
643 view_update_title(view, false); 685 view_update_title(view, false);
@@ -649,7 +691,7 @@ static void handle_set_class(struct wl_listener *listener, void *data) {
649 wl_container_of(listener, xwayland_view, set_class); 691 wl_container_of(listener, xwayland_view, set_class);
650 struct sway_view *view = &xwayland_view->view; 692 struct sway_view *view = &xwayland_view->view;
651 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 693 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
652 if (!xsurface->mapped) { 694 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
653 return; 695 return;
654 } 696 }
655 view_execute_criteria(view); 697 view_execute_criteria(view);
@@ -660,18 +702,43 @@ static void handle_set_role(struct wl_listener *listener, void *data) {
660 wl_container_of(listener, xwayland_view, set_role); 702 wl_container_of(listener, xwayland_view, set_role);
661 struct sway_view *view = &xwayland_view->view; 703 struct sway_view *view = &xwayland_view->view;
662 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 704 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
663 if (!xsurface->mapped) { 705 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
664 return; 706 return;
665 } 707 }
666 view_execute_criteria(view); 708 view_execute_criteria(view);
667} 709}
668 710
711static void handle_set_startup_id(struct wl_listener *listener, void *data) {
712 struct sway_xwayland_view *xwayland_view =
713 wl_container_of(listener, xwayland_view, set_startup_id);
714 struct sway_view *view = &xwayland_view->view;
715 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
716 if (xsurface->startup_id == NULL) {
717 return;
718 }
719
720 struct wlr_xdg_activation_token_v1 *token =
721 wlr_xdg_activation_v1_find_token(
722 server.xdg_activation_v1, xsurface->startup_id);
723 if (token == NULL) {
724 // Tried to activate with an unknown or expired token
725 return;
726 }
727
728 struct launcher_ctx *ctx = token->data;
729 if (token->data == NULL) {
730 // TODO: support external launchers in X
731 return;
732 }
733 view_assign_ctx(view, ctx);
734}
735
669static void handle_set_window_type(struct wl_listener *listener, void *data) { 736static void handle_set_window_type(struct wl_listener *listener, void *data) {
670 struct sway_xwayland_view *xwayland_view = 737 struct sway_xwayland_view *xwayland_view =
671 wl_container_of(listener, xwayland_view, set_window_type); 738 wl_container_of(listener, xwayland_view, set_window_type);
672 struct sway_view *view = &xwayland_view->view; 739 struct sway_view *view = &xwayland_view->view;
673 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 740 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
674 if (!xsurface->mapped) { 741 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
675 return; 742 return;
676 } 743 }
677 view_execute_criteria(view); 744 view_execute_criteria(view);
@@ -682,7 +749,7 @@ static void handle_set_hints(struct wl_listener *listener, void *data) {
682 wl_container_of(listener, xwayland_view, set_hints); 749 wl_container_of(listener, xwayland_view, set_hints);
683 struct sway_view *view = &xwayland_view->view; 750 struct sway_view *view = &xwayland_view->view;
684 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 751 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
685 if (!xsurface->mapped) { 752 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
686 return; 753 return;
687 } 754 }
688 const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); 755 const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints);
@@ -697,6 +764,24 @@ static void handle_set_hints(struct wl_listener *listener, void *data) {
697 } 764 }
698} 765}
699 766
767static void handle_associate(struct wl_listener *listener, void *data) {
768 struct sway_xwayland_view *xwayland_view =
769 wl_container_of(listener, xwayland_view, associate);
770 struct wlr_xwayland_surface *xsurface =
771 xwayland_view->view.wlr_xwayland_surface;
772 wl_signal_add(&xsurface->surface->events.unmap, &xwayland_view->unmap);
773 xwayland_view->unmap.notify = handle_unmap;
774 wl_signal_add(&xsurface->surface->events.map, &xwayland_view->map);
775 xwayland_view->map.notify = handle_map;
776}
777
778static void handle_dissociate(struct wl_listener *listener, void *data) {
779 struct sway_xwayland_view *xwayland_view =
780 wl_container_of(listener, xwayland_view, dissociate);
781 wl_list_remove(&xwayland_view->map.link);
782 wl_list_remove(&xwayland_view->unmap.link);
783}
784
700struct sway_view *view_from_wlr_xwayland_surface( 785struct sway_view *view_from_wlr_xwayland_surface(
701 struct wlr_xwayland_surface *xsurface) { 786 struct wlr_xwayland_surface *xsurface) {
702 return xsurface->data; 787 return xsurface->data;
@@ -712,7 +797,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
712 return NULL; 797 return NULL;
713 } 798 }
714 799
715 view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); 800 if (!view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl)) {
801 free(xwayland_view);
802 return NULL;
803 }
716 xwayland_view->view.wlr_xwayland_surface = xsurface; 804 xwayland_view->view.wlr_xwayland_surface = xsurface;
717 805
718 wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); 806 wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy);
@@ -751,6 +839,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
751 wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); 839 wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role);
752 xwayland_view->set_role.notify = handle_set_role; 840 xwayland_view->set_role.notify = handle_set_role;
753 841
842 wl_signal_add(&xsurface->events.set_startup_id,
843 &xwayland_view->set_startup_id);
844 xwayland_view->set_startup_id.notify = handle_set_startup_id;
845
754 wl_signal_add(&xsurface->events.set_window_type, 846 wl_signal_add(&xsurface->events.set_window_type,
755 &xwayland_view->set_window_type); 847 &xwayland_view->set_window_type);
756 xwayland_view->set_window_type.notify = handle_set_window_type; 848 xwayland_view->set_window_type.notify = handle_set_window_type;
@@ -762,11 +854,11 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
762 &xwayland_view->set_decorations); 854 &xwayland_view->set_decorations);
763 xwayland_view->set_decorations.notify = handle_set_decorations; 855 xwayland_view->set_decorations.notify = handle_set_decorations;
764 856
765 wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); 857 wl_signal_add(&xsurface->events.associate, &xwayland_view->associate);
766 xwayland_view->unmap.notify = handle_unmap; 858 xwayland_view->associate.notify = handle_associate;
767 859
768 wl_signal_add(&xsurface->events.map, &xwayland_view->map); 860 wl_signal_add(&xsurface->events.dissociate, &xwayland_view->dissociate);
769 xwayland_view->map.notify = handle_map; 861 xwayland_view->dissociate.notify = handle_dissociate;
770 862
771 wl_signal_add(&xsurface->events.set_override_redirect, 863 wl_signal_add(&xsurface->events.set_override_redirect,
772 &xwayland_view->override_redirect); 864 &xwayland_view->override_redirect);