aboutsummaryrefslogtreecommitdiffstats
path: root/sway/desktop
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2018-07-23 20:27:56 -0400
committerLibravatar Drew DeVault <sir@cmpwn.com>2018-07-23 20:31:11 -0400
commitf4b882475eee7a81c206c7825616cc4656b2f60b (patch)
tree38e6ebf81b235424f105dcbcbb194e5e9eac70c0 /sway/desktop
parentImplement pid->workspace tracking (diff)
parentMerge pull request #2342 from RyanDwyer/update-cursor (diff)
downloadsway-f4b882475eee7a81c206c7825616cc4656b2f60b.tar.gz
sway-f4b882475eee7a81c206c7825616cc4656b2f60b.tar.zst
sway-f4b882475eee7a81c206c7825616cc4656b2f60b.zip
Merge branch 'master' into pid-workspaces
Diffstat (limited to 'sway/desktop')
-rw-r--r--sway/desktop/desktop.c9
-rw-r--r--sway/desktop/idle_inhibit_v1.c80
-rw-r--r--sway/desktop/layer_shell.c21
-rw-r--r--sway/desktop/output.c957
-rw-r--r--sway/desktop/render.c919
-rw-r--r--sway/desktop/transaction.c107
-rw-r--r--sway/desktop/xdg_shell.c115
-rw-r--r--sway/desktop/xdg_shell_v6.c115
-rw-r--r--sway/desktop/xwayland.c189
9 files changed, 1509 insertions, 1003 deletions
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c
index e495790c..6575519d 100644
--- a/sway/desktop/desktop.c
+++ b/sway/desktop/desktop.c
@@ -13,3 +13,12 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
13 } 13 }
14 } 14 }
15} 15}
16
17void desktop_damage_whole_container(struct sway_container *con) {
18 for (int i = 0; i < root_container.children->length; ++i) {
19 struct sway_container *cont = root_container.children->items[i];
20 if (cont->type == C_OUTPUT) {
21 output_damage_whole_container(cont->sway_output, con);
22 }
23 }
24}
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c
new file mode 100644
index 00000000..da17d0f2
--- /dev/null
+++ b/sway/desktop/idle_inhibit_v1.c
@@ -0,0 +1,80 @@
1#include <stdlib.h>
2#include <wlr/types/wlr_idle.h>
3#include "log.h"
4#include "sway/desktop/idle_inhibit_v1.h"
5#include "sway/tree/view.h"
6#include "sway/server.h"
7
8
9static void handle_destroy(struct wl_listener *listener, void *data) {
10 struct sway_idle_inhibitor_v1 *inhibitor =
11 wl_container_of(listener, inhibitor, destroy);
12 wlr_log(WLR_DEBUG, "Sway idle inhibitor destroyed");
13 wl_list_remove(&inhibitor->link);
14 wl_list_remove(&inhibitor->destroy.link);
15 idle_inhibit_v1_check_active(inhibitor->manager);
16 free(inhibitor);
17}
18
19void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) {
20 struct wlr_idle_inhibitor_v1 *wlr_inhibitor = data;
21 struct sway_idle_inhibit_manager_v1 *manager =
22 wl_container_of(listener, manager, new_idle_inhibitor_v1);
23 wlr_log(WLR_DEBUG, "New sway idle inhibitor");
24
25 struct sway_idle_inhibitor_v1 *inhibitor =
26 calloc(1, sizeof(struct sway_idle_inhibitor_v1));
27 if (!inhibitor) {
28 return;
29 }
30
31 inhibitor->manager = manager;
32 inhibitor->view = view_from_wlr_surface(wlr_inhibitor->surface);
33 wl_list_insert(&manager->inhibitors, &inhibitor->link);
34
35
36 inhibitor->destroy.notify = handle_destroy;
37 wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy);
38
39 idle_inhibit_v1_check_active(manager);
40}
41
42void idle_inhibit_v1_check_active(
43 struct sway_idle_inhibit_manager_v1 *manager) {
44 struct sway_idle_inhibitor_v1 *inhibitor;
45 bool inhibited = false;
46 wl_list_for_each(inhibitor, &manager->inhibitors, link) {
47 if (!inhibitor->view) {
48 /* Cannot guess if view is visible so assume it is */
49 inhibited = true;
50 break;
51 }
52 if (view_is_visible(inhibitor->view)) {
53 inhibited = true;
54 break;
55 }
56 }
57 wlr_idle_set_enabled(manager->idle, NULL, !inhibited);
58}
59
60struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create(
61 struct wl_display *wl_display, struct wlr_idle *idle) {
62 struct sway_idle_inhibit_manager_v1 *manager =
63 calloc(1, sizeof(struct sway_idle_inhibit_manager_v1));
64 if (!manager) {
65 return NULL;
66 }
67
68 manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display);
69 if (!manager->wlr_manager) {
70 free(manager);
71 return NULL;
72 }
73 manager->idle = idle;
74 wl_signal_add(&manager->wlr_manager->events.new_inhibitor,
75 &manager->new_idle_inhibitor_v1);
76 manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1;
77 wl_list_init(&manager->inhibitors);
78
79 return manager;
80}
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index de1fe349..a7d96717 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -12,7 +12,6 @@
12#include "sway/layers.h" 12#include "sway/layers.h"
13#include "sway/output.h" 13#include "sway/output.h"
14#include "sway/server.h" 14#include "sway/server.h"
15#include "sway/tree/arrange.h"
16#include "sway/tree/layout.h" 15#include "sway/tree/layout.h"
17#include "log.h" 16#include "log.h"
18 17
@@ -174,9 +173,9 @@ void arrange_layers(struct sway_output *output) {
174 173
175 if (memcmp(&usable_area, &output->usable_area, 174 if (memcmp(&usable_area, &output->usable_area,
176 sizeof(struct wlr_box)) != 0) { 175 sizeof(struct wlr_box)) != 0) {
177 wlr_log(L_DEBUG, "Usable area changed, rearranging output"); 176 wlr_log(WLR_DEBUG, "Usable area changed, rearranging output");
178 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); 177 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
179 arrange_and_commit(output->swayc); 178 container_set_dirty(output->swayc);
180 } 179 }
181 180
182 // Arrange non-exlusive surfaces from top->bottom 181 // Arrange non-exlusive surfaces from top->bottom
@@ -269,7 +268,7 @@ static void unmap(struct sway_layer_surface *sway_layer) {
269static void handle_destroy(struct wl_listener *listener, void *data) { 268static void handle_destroy(struct wl_listener *listener, void *data) {
270 struct sway_layer_surface *sway_layer = 269 struct sway_layer_surface *sway_layer =
271 wl_container_of(listener, sway_layer, destroy); 270 wl_container_of(listener, sway_layer, destroy);
272 wlr_log(L_DEBUG, "Layer surface destroyed (%s)", 271 wlr_log(WLR_DEBUG, "Layer surface destroyed (%s)",
273 sway_layer->layer_surface->namespace); 272 sway_layer->layer_surface->namespace);
274 if (sway_layer->layer_surface->mapped) { 273 if (sway_layer->layer_surface->mapped) {
275 unmap(sway_layer); 274 unmap(sway_layer);
@@ -316,7 +315,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
316 struct wlr_layer_surface *layer_surface = data; 315 struct wlr_layer_surface *layer_surface = data;
317 struct sway_server *server = 316 struct sway_server *server =
318 wl_container_of(listener, server, layer_shell_surface); 317 wl_container_of(listener, server, layer_shell_surface);
319 wlr_log(L_DEBUG, "new layer surface: namespace %s layer %d anchor %d " 318 wlr_log(WLR_DEBUG, "new layer surface: namespace %s layer %d anchor %d "
320 "size %dx%d margin %d,%d,%d,%d", 319 "size %dx%d margin %d,%d,%d,%d",
321 layer_surface->namespace, layer_surface->layer, layer_surface->layer, 320 layer_surface->namespace, layer_surface->layer, layer_surface->layer,
322 layer_surface->client_pending.desired_width, 321 layer_surface->client_pending.desired_width,
@@ -326,12 +325,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
326 layer_surface->client_pending.margin.bottom, 325 layer_surface->client_pending.margin.bottom,
327 layer_surface->client_pending.margin.left); 326 layer_surface->client_pending.margin.left);
328 327
329 struct sway_layer_surface *sway_layer =
330 calloc(1, sizeof(struct sway_layer_surface));
331 if (!sway_layer) {
332 return;
333 }
334
335 if (!layer_surface->output) { 328 if (!layer_surface->output) {
336 // Assign last active output 329 // Assign last active output
337 struct sway_container *output = NULL; 330 struct sway_container *output = NULL;
@@ -353,6 +346,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
353 layer_surface->output = output->sway_output->wlr_output; 346 layer_surface->output = output->sway_output->wlr_output;
354 } 347 }
355 348
349 struct sway_layer_surface *sway_layer =
350 calloc(1, sizeof(struct sway_layer_surface));
351 if (!sway_layer) {
352 return;
353 }
354
356 sway_layer->surface_commit.notify = handle_surface_commit; 355 sway_layer->surface_commit.notify = handle_surface_commit;
357 wl_signal_add(&layer_surface->surface->events.commit, 356 wl_signal_add(&layer_surface->surface->events.commit,
358 &sway_layer->surface_commit); 357 &sway_layer->surface_commit);
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 1e7494b3..a206ac6b 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -56,33 +56,15 @@ static void rotate_child_position(double *sx, double *sy, double sw, double sh,
56 *sy = ry + ph/2 - sh/2; 56 *sy = ry + ph/2 - sh/2;
57} 57}
58 58
59/** 59bool output_get_surface_box(struct root_geometry *geo,
60 * Contains a surface's root geometry information. For instance, when rendering
61 * a popup, this will contain the parent view's position and size.
62 */
63struct root_geometry {
64 double x, y;
65 int width, height;
66 float rotation;
67};
68
69struct render_data {
70 struct root_geometry root_geo;
71 struct sway_output *output;
72 pixman_region32_t *damage;
73 struct sway_view *view;
74 float alpha;
75};
76
77static bool get_surface_box(struct root_geometry *geo,
78 struct sway_output *output, struct wlr_surface *surface, int sx, int sy, 60 struct sway_output *output, struct wlr_surface *surface, int sx, int sy,
79 struct wlr_box *surface_box) { 61 struct wlr_box *surface_box) {
80 if (!wlr_surface_has_buffer(surface)) { 62 if (!wlr_surface_has_buffer(surface)) {
81 return false; 63 return false;
82 } 64 }
83 65
84 int sw = surface->current->width; 66 int sw = surface->current.width;
85 int sh = surface->current->height; 67 int sh = surface->current.height;
86 68
87 double _sx = sx, _sy = sy; 69 double _sx = sx, _sy = sy;
88 rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height, 70 rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height,
@@ -110,24 +92,23 @@ static bool get_surface_box(struct root_geometry *geo,
110 return wlr_box_intersection(&output_box, &rotated_box, &intersection); 92 return wlr_box_intersection(&output_box, &rotated_box, &intersection);
111} 93}
112 94
113static void surface_for_each_surface(struct wlr_surface *surface, 95void output_surface_for_each_surface(struct wlr_surface *surface,
114 double ox, double oy, struct root_geometry *geo, 96 double ox, double oy, struct root_geometry *geo,
115 wlr_surface_iterator_func_t iterator, void *user_data) { 97 wlr_surface_iterator_func_t iterator, void *user_data) {
116 geo->x = ox; 98 geo->x = ox;
117 geo->y = oy; 99 geo->y = oy;
118 geo->width = surface->current->width; 100 geo->width = surface->current.width;
119 geo->height = surface->current->height; 101 geo->height = surface->current.height;
120 geo->rotation = 0; 102 geo->rotation = 0;
121 103
122 wlr_surface_for_each_surface(surface, iterator, user_data); 104 wlr_surface_for_each_surface(surface, iterator, user_data);
123} 105}
124 106
125static void output_view_for_each_surface(struct sway_view *view, 107void output_view_for_each_surface(struct sway_view *view,
126 struct root_geometry *geo, wlr_surface_iterator_func_t iterator, 108 struct sway_output *output, struct root_geometry *geo,
127 void *user_data) { 109 wlr_surface_iterator_func_t iterator, void *user_data) {
128 struct render_data *data = user_data; 110 geo->x = view->swayc->current.view_x - output->swayc->current.swayc_x;
129 geo->x = view->swayc->current.view_x - data->output->swayc->current.swayc_x; 111 geo->y = view->swayc->current.view_y - output->swayc->current.swayc_y;
130 geo->y = view->swayc->current.view_y - data->output->swayc->current.swayc_y;
131 geo->width = view->swayc->current.view_width; 112 geo->width = view->swayc->current.view_width;
132 geo->height = view->swayc->current.view_height; 113 geo->height = view->swayc->current.view_height;
133 geo->rotation = 0; // TODO 114 geo->rotation = 0; // TODO
@@ -135,20 +116,20 @@ static void output_view_for_each_surface(struct sway_view *view,
135 view_for_each_surface(view, iterator, user_data); 116 view_for_each_surface(view, iterator, user_data);
136} 117}
137 118
138static void layer_for_each_surface(struct wl_list *layer_surfaces, 119void output_layer_for_each_surface(struct wl_list *layer_surfaces,
139 struct root_geometry *geo, wlr_surface_iterator_func_t iterator, 120 struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
140 void *user_data) { 121 void *user_data) {
141 struct sway_layer_surface *layer_surface; 122 struct sway_layer_surface *layer_surface;
142 wl_list_for_each(layer_surface, layer_surfaces, link) { 123 wl_list_for_each(layer_surface, layer_surfaces, link) {
143 struct wlr_layer_surface *wlr_layer_surface = 124 struct wlr_layer_surface *wlr_layer_surface =
144 layer_surface->layer_surface; 125 layer_surface->layer_surface;
145 surface_for_each_surface(wlr_layer_surface->surface, 126 output_surface_for_each_surface(wlr_layer_surface->surface,
146 layer_surface->geo.x, layer_surface->geo.y, geo, iterator, 127 layer_surface->geo.x, layer_surface->geo.y, geo, iterator,
147 user_data); 128 user_data);
148 } 129 }
149} 130}
150 131
151static void unmanaged_for_each_surface(struct wl_list *unmanaged, 132void output_unmanaged_for_each_surface(struct wl_list *unmanaged,
152 struct sway_output *output, struct root_geometry *geo, 133 struct sway_output *output, struct root_geometry *geo,
153 wlr_surface_iterator_func_t iterator, void *user_data) { 134 wlr_surface_iterator_func_t iterator, void *user_data) {
154 struct sway_xwayland_unmanaged *unmanaged_surface; 135 struct sway_xwayland_unmanaged *unmanaged_surface;
@@ -158,12 +139,12 @@ static void unmanaged_for_each_surface(struct wl_list *unmanaged,
158 double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; 139 double ox = unmanaged_surface->lx - output->swayc->current.swayc_x;
159 double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; 140 double oy = unmanaged_surface->ly - output->swayc->current.swayc_y;
160 141
161 surface_for_each_surface(xsurface->surface, ox, oy, geo, 142 output_surface_for_each_surface(xsurface->surface, ox, oy, geo,
162 iterator, user_data); 143 iterator, user_data);
163 } 144 }
164} 145}
165 146
166static void drag_icons_for_each_surface(struct wl_list *drag_icons, 147void output_drag_icons_for_each_surface(struct wl_list *drag_icons,
167 struct sway_output *output, struct root_geometry *geo, 148 struct sway_output *output, struct root_geometry *geo,
168 wlr_surface_iterator_func_t iterator, void *user_data) { 149 wlr_surface_iterator_func_t iterator, void *user_data) {
169 struct sway_drag_icon *drag_icon; 150 struct sway_drag_icon *drag_icon;
@@ -172,7 +153,7 @@ static void drag_icons_for_each_surface(struct wl_list *drag_icons,
172 double oy = drag_icon->y - output->swayc->y; 153 double oy = drag_icon->y - output->swayc->y;
173 154
174 if (drag_icon->wlr_drag_icon->mapped) { 155 if (drag_icon->wlr_drag_icon->mapped) {
175 surface_for_each_surface(drag_icon->wlr_drag_icon->surface, 156 output_surface_for_each_surface(drag_icon->wlr_drag_icon->surface,
176 ox, oy, geo, iterator, user_data); 157 ox, oy, geo, iterator, user_data);
177 } 158 }
178 } 159 }
@@ -185,722 +166,7 @@ static void scale_box(struct wlr_box *box, float scale) {
185 box->height *= scale; 166 box->height *= scale;
186} 167}
187 168
188static void scissor_output(struct wlr_output *wlr_output, 169struct sway_container *output_get_active_workspace(struct sway_output *output) {
189 pixman_box32_t *rect) {
190 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
191 assert(renderer);
192
193 struct wlr_box box = {
194 .x = rect->x1,
195 .y = rect->y1,
196 .width = rect->x2 - rect->x1,
197 .height = rect->y2 - rect->y1,
198 };
199
200 int ow, oh;
201 wlr_output_transformed_resolution(wlr_output, &ow, &oh);
202
203 enum wl_output_transform transform =
204 wlr_output_transform_invert(wlr_output->transform);
205 wlr_box_transform(&box, transform, ow, oh, &box);
206
207 wlr_renderer_scissor(renderer, &box);
208}
209
210static void render_texture(struct wlr_output *wlr_output,
211 pixman_region32_t *output_damage, struct wlr_texture *texture,
212 const struct wlr_box *box, const float matrix[static 9], float alpha) {
213 struct wlr_renderer *renderer =
214 wlr_backend_get_renderer(wlr_output->backend);
215
216 pixman_region32_t damage;
217 pixman_region32_init(&damage);
218 pixman_region32_union_rect(&damage, &damage, box->x, box->y,
219 box->width, box->height);
220 pixman_region32_intersect(&damage, &damage, output_damage);
221 bool damaged = pixman_region32_not_empty(&damage);
222 if (!damaged) {
223 goto damage_finish;
224 }
225
226 int nrects;
227 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
228 for (int i = 0; i < nrects; ++i) {
229 scissor_output(wlr_output, &rects[i]);
230 wlr_render_texture_with_matrix(renderer, texture, matrix, alpha);
231 }
232
233damage_finish:
234 pixman_region32_fini(&damage);
235}
236
237static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy,
238 void *_data) {
239 struct render_data *data = _data;
240 struct wlr_output *wlr_output = data->output->wlr_output;
241 float rotation = data->root_geo.rotation;
242 pixman_region32_t *output_damage = data->damage;
243 float alpha = data->alpha;
244
245 struct wlr_texture *texture = wlr_surface_get_texture(surface);
246 if (!texture) {
247 return;
248 }
249
250 struct wlr_box box;
251 bool intersects = get_surface_box(&data->root_geo, data->output, surface,
252 sx, sy, &box);
253 if (!intersects) {
254 return;
255 }
256
257 scale_box(&box, wlr_output->scale);
258
259 float matrix[9];
260 enum wl_output_transform transform =
261 wlr_output_transform_invert(surface->current->transform);
262 wlr_matrix_project_box(matrix, &box, transform, rotation,
263 wlr_output->transform_matrix);
264
265 render_texture(wlr_output, output_damage, texture, &box, matrix, alpha);
266}
267
268static void render_layer(struct sway_output *output,
269 pixman_region32_t *damage, struct wl_list *layer_surfaces) {
270 struct render_data data = {
271 .output = output,
272 .damage = damage,
273 .alpha = 1.0f,
274 };
275 layer_for_each_surface(layer_surfaces, &data.root_geo,
276 render_surface_iterator, &data);
277}
278
279static void render_unmanaged(struct sway_output *output,
280 pixman_region32_t *damage, struct wl_list *unmanaged) {
281 struct render_data data = {
282 .output = output,
283 .damage = damage,
284 .alpha = 1.0f,
285 };
286 unmanaged_for_each_surface(unmanaged, output, &data.root_geo,
287 render_surface_iterator, &data);
288}
289
290static void render_drag_icons(struct sway_output *output,
291 pixman_region32_t *damage, struct wl_list *drag_icons) {
292 struct render_data data = {
293 .output = output,
294 .damage = damage,
295 .alpha = 1.0f,
296 };
297 drag_icons_for_each_surface(drag_icons, output, &data.root_geo,
298 render_surface_iterator, &data);
299}
300
301static void render_rect(struct wlr_output *wlr_output,
302 pixman_region32_t *output_damage, const struct wlr_box *_box,
303 float color[static 4]) {
304 struct wlr_renderer *renderer =
305 wlr_backend_get_renderer(wlr_output->backend);
306
307 struct wlr_box box;
308 memcpy(&box, _box, sizeof(struct wlr_box));
309 box.x -= wlr_output->lx * wlr_output->scale;
310 box.y -= wlr_output->ly * wlr_output->scale;
311
312 pixman_region32_t damage;
313 pixman_region32_init(&damage);
314 pixman_region32_union_rect(&damage, &damage, box.x, box.y,
315 box.width, box.height);
316 pixman_region32_intersect(&damage, &damage, output_damage);
317 bool damaged = pixman_region32_not_empty(&damage);
318 if (!damaged) {
319 goto damage_finish;
320 }
321
322 int nrects;
323 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
324 for (int i = 0; i < nrects; ++i) {
325 scissor_output(wlr_output, &rects[i]);
326 wlr_render_rect(renderer, &box, color,
327 wlr_output->transform_matrix);
328 }
329
330damage_finish:
331 pixman_region32_fini(&damage);
332}
333
334static void premultiply_alpha(float color[4], float opacity) {
335 color[3] *= opacity;
336 color[0] *= color[3];
337 color[1] *= color[3];
338 color[2] *= color[3];
339}
340
341static void render_view_surfaces(struct sway_view *view,
342 struct sway_output *output, pixman_region32_t *damage, float alpha) {
343 struct render_data data = {
344 .output = output,
345 .damage = damage,
346 .view = view,
347 .alpha = alpha,
348 };
349 output_view_for_each_surface(
350 view, &data.root_geo, render_surface_iterator, &data);
351}
352
353static void render_saved_view(struct sway_view *view,
354 struct sway_output *output, pixman_region32_t *damage, float alpha) {
355 struct wlr_output *wlr_output = output->wlr_output;
356
357 int width, height;
358 struct wlr_texture *texture =
359 transaction_get_saved_texture(view, &width, &height);
360 if (!texture) {
361 return;
362 }
363 struct wlr_box box = {
364 .x = view->swayc->current.view_x - output->swayc->current.swayc_x,
365 .y = view->swayc->current.view_y - output->swayc->current.swayc_y,
366 .width = width,
367 .height = height,
368 };
369
370 struct wlr_box output_box = {
371 .width = output->swayc->current.swayc_width,
372 .height = output->swayc->current.swayc_height,
373 };
374
375 struct wlr_box intersection;
376 bool intersects = wlr_box_intersection(&output_box, &box, &intersection);
377 if (!intersects) {
378 return;
379 }
380
381 scale_box(&box, wlr_output->scale);
382
383 float matrix[9];
384 wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
385 wlr_output->transform_matrix);
386
387 render_texture(wlr_output, damage, texture, &box, matrix, alpha);
388}
389
390/**
391 * Render a view's surface and left/bottom/right borders.
392 */
393static void render_view(struct sway_output *output, pixman_region32_t *damage,
394 struct sway_container *con, struct border_colors *colors) {
395 struct sway_view *view = con->sway_view;
396 if (view->swayc->instructions->length) {
397 render_saved_view(view, output, damage, view->swayc->alpha);
398 } else {
399 render_view_surfaces(view, output, damage, view->swayc->alpha);
400 }
401
402 struct wlr_box box;
403 float output_scale = output->wlr_output->scale;
404 float color[4];
405 struct sway_container_state *state = &con->current;
406
407 if (state->border != B_NONE) {
408 if (state->border_left) {
409 memcpy(&color, colors->child_border, sizeof(float) * 4);
410 premultiply_alpha(color, con->alpha);
411 box.x = state->swayc_x;
412 box.y = state->view_y;
413 box.width = state->border_thickness;
414 box.height = state->view_height;
415 scale_box(&box, output_scale);
416 render_rect(output->wlr_output, damage, &box, color);
417 }
418
419 if (state->border_right) {
420 if (state->parent->current.children->length == 1
421 && state->parent->current.layout == L_HORIZ) {
422 memcpy(&color, colors->indicator, sizeof(float) * 4);
423 } else {
424 memcpy(&color, colors->child_border, sizeof(float) * 4);
425 }
426 premultiply_alpha(color, con->alpha);
427 box.x = state->view_x + state->view_width;
428 box.y = state->view_y;
429 box.width = state->border_thickness;
430 box.height = state->view_height;
431 scale_box(&box, output_scale);
432 render_rect(output->wlr_output, damage, &box, color);
433 }
434
435 if (state->border_bottom) {
436 if (state->parent->current.children->length == 1
437 && con->current.parent->current.layout == L_VERT) {
438 memcpy(&color, colors->indicator, sizeof(float) * 4);
439 } else {
440 memcpy(&color, colors->child_border, sizeof(float) * 4);
441 }
442 premultiply_alpha(color, con->alpha);
443 box.x = state->swayc_x;
444 box.y = state->view_y + state->view_height;
445 box.width = state->swayc_width;
446 box.height = state->border_thickness;
447 scale_box(&box, output_scale);
448 render_rect(output->wlr_output, damage, &box, color);
449 }
450 }
451}
452
453/**
454 * Render a titlebar.
455 *
456 * Care must be taken not to render over the same pixel multiple times,
457 * otherwise the colors will be incorrect when using opacity.
458 *
459 * The height is: 1px border, 3px padding, font height, 3px padding, 1px border
460 * The left side for L_TABBED is: 1px border, 2px padding, title
461 * The left side for other layouts is: 3px padding, title
462 */
463static void render_titlebar(struct sway_output *output,
464 pixman_region32_t *output_damage, struct sway_container *con,
465 int x, int y, int width,
466 struct border_colors *colors, struct wlr_texture *title_texture,
467 struct wlr_texture *marks_texture) {
468 struct wlr_box box;
469 float color[4];
470 struct sway_container_state *state = &con->current;
471 float output_scale = output->wlr_output->scale;
472 enum sway_container_layout layout = state->parent->current.layout;
473 list_t *children = state->parent->current.children;
474 bool is_last_child = children->items[children->length - 1] == con;
475 double output_x = output->swayc->current.swayc_x;
476 double output_y = output->swayc->current.swayc_y;
477
478 // Single pixel bar above title
479 memcpy(&color, colors->border, sizeof(float) * 4);
480 premultiply_alpha(color, con->alpha);
481 box.x = x;
482 box.y = y;
483 box.width = width;
484 box.height = TITLEBAR_BORDER_THICKNESS;
485 scale_box(&box, output_scale);
486 render_rect(output->wlr_output, output_damage, &box, color);
487
488 // Single pixel bar below title
489 size_t left_offset = 0, right_offset = 0;
490 bool connects_sides = false;
491 if (layout == L_HORIZ || layout == L_VERT ||
492 (layout == L_STACKED && is_last_child)) {
493 if (con->type == C_VIEW) {
494 left_offset = state->border_left * state->border_thickness;
495 right_offset = state->border_right * state->border_thickness;
496 connects_sides = true;
497 }
498 }
499 box.x = x + left_offset;
500 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
501 box.width = width - left_offset - right_offset;
502 box.height = TITLEBAR_BORDER_THICKNESS;
503 scale_box(&box, output_scale);
504 render_rect(output->wlr_output, output_damage, &box, color);
505
506 if (layout == L_TABBED) {
507 // Single pixel left edge
508 box.x = x;
509 box.y = y + TITLEBAR_BORDER_THICKNESS;
510 box.width = TITLEBAR_BORDER_THICKNESS;
511 box.height =
512 container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2;
513 scale_box(&box, output_scale);
514 render_rect(output->wlr_output, output_damage, &box, color);
515
516 // Single pixel right edge
517 box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale;
518 render_rect(output->wlr_output, output_damage, &box, color);
519 }
520
521 size_t inner_width = width - TITLEBAR_H_PADDING * 2;
522
523 // Marks
524 size_t marks_width = 0;
525 if (config->show_marks && marks_texture) {
526 struct wlr_box texture_box;
527 wlr_texture_get_size(marks_texture,
528 &texture_box.width, &texture_box.height);
529 texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING)
530 * output_scale - texture_box.width;
531 texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
532
533 float matrix[9];
534 wlr_matrix_project_box(matrix, &texture_box,
535 WL_OUTPUT_TRANSFORM_NORMAL,
536 0.0, output->wlr_output->transform_matrix);
537
538 if (inner_width * output_scale < texture_box.width) {
539 texture_box.width = inner_width * output_scale;
540 }
541 render_texture(output->wlr_output, output_damage, marks_texture,
542 &texture_box, matrix, con->alpha);
543 marks_width = texture_box.width;
544 }
545
546 // Title text
547 size_t title_width = 0;
548 if (title_texture) {
549 struct wlr_box texture_box;
550 wlr_texture_get_size(title_texture,
551 &texture_box.width, &texture_box.height);
552 texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale;
553 texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
554
555 float matrix[9];
556 wlr_matrix_project_box(matrix, &texture_box,
557 WL_OUTPUT_TRANSFORM_NORMAL,
558 0.0, output->wlr_output->transform_matrix);
559
560 if (inner_width * output_scale - marks_width < texture_box.width) {
561 texture_box.width = inner_width * output_scale - marks_width;
562 }
563 render_texture(output->wlr_output, output_damage, title_texture,
564 &texture_box, matrix, con->alpha);
565 title_width = texture_box.width;
566 }
567
568 // Padding above title
569 memcpy(&color, colors->background, sizeof(float) * 4);
570 premultiply_alpha(color, con->alpha);
571 box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
572 box.y = y + TITLEBAR_BORDER_THICKNESS;
573 box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2;
574 box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS;
575 scale_box(&box, output_scale);
576 render_rect(output->wlr_output, output_damage, &box, color);
577
578 // Padding below title
579 box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale;
580 render_rect(output->wlr_output, output_damage, &box, color);
581
582 // Filler between title and marks
583 box.width = inner_width * output_scale - title_width - marks_width;
584 if (box.width > 0) {
585 box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_width;
586 box.y = (y + TITLEBAR_V_PADDING) * output_scale;
587 box.height = config->font_height * output_scale;
588 render_rect(output->wlr_output, output_damage, &box, color);
589 }
590
591 // Padding left of title
592 left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
593 box.x = x + left_offset;
594 box.y = y + TITLEBAR_V_PADDING;
595 box.width = TITLEBAR_H_PADDING - left_offset;
596 box.height = config->font_height;
597 scale_box(&box, output_scale);
598 render_rect(output->wlr_output, output_damage, &box, color);
599
600 // Padding right of marks
601 right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
602 box.x = x + width - TITLEBAR_H_PADDING;
603 box.y = y + TITLEBAR_V_PADDING;
604 box.width = TITLEBAR_H_PADDING - right_offset;
605 box.height = config->font_height;
606 scale_box(&box, output_scale);
607 render_rect(output->wlr_output, output_damage, &box, color);
608
609 if (connects_sides) {
610 // Left pixel in line with bottom bar
611 box.x = x;
612 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
613 box.width = state->border_thickness * state->border_left;
614 box.height = TITLEBAR_BORDER_THICKNESS;
615 scale_box(&box, output_scale);
616 render_rect(output->wlr_output, output_damage, &box, color);
617
618 // Right pixel in line with bottom bar
619 box.x = x + width - state->border_thickness * state->border_right;
620 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
621 box.width = state->border_thickness * state->border_right;
622 box.height = TITLEBAR_BORDER_THICKNESS;
623 scale_box(&box, output_scale);
624 render_rect(output->wlr_output, output_damage, &box, color);
625 }
626}
627
628/**
629 * Render the top border line for a view using "border pixel".
630 */
631static void render_top_border(struct sway_output *output,
632 pixman_region32_t *output_damage, struct sway_container *con,
633 struct border_colors *colors) {
634 struct sway_container_state *state = &con->current;
635 if (!state->border_top) {
636 return;
637 }
638 struct wlr_box box;
639 float color[4];
640 float output_scale = output->wlr_output->scale;
641
642 // Child border - top edge
643 memcpy(&color, colors->child_border, sizeof(float) * 4);
644 premultiply_alpha(color, con->alpha);
645 box.x = state->swayc_x;
646 box.y = state->swayc_y;
647 box.width = state->swayc_width;
648 box.height = state->border_thickness;
649 scale_box(&box, output_scale);
650 render_rect(output->wlr_output, output_damage, &box, color);
651}
652
653static void render_container(struct sway_output *output,
654 pixman_region32_t *damage, struct sway_container *con, bool parent_focused);
655
656/**
657 * Render a container's children using a L_HORIZ or L_VERT layout.
658 *
659 * Wrap child views in borders and leave child containers borderless because
660 * they'll apply their own borders to their children.
661 */
662static void render_container_simple(struct sway_output *output,
663 pixman_region32_t *damage, struct sway_container *con,
664 bool parent_focused) {
665 struct sway_seat *seat = input_manager_current_seat(input_manager);
666 struct sway_container *focus = seat_get_focus(seat);
667
668 for (int i = 0; i < con->current.children->length; ++i) {
669 struct sway_container *child = con->current.children->items[i];
670
671 if (child->type == C_VIEW) {
672 struct sway_view *view = child->sway_view;
673 struct border_colors *colors;
674 struct wlr_texture *title_texture;
675 struct wlr_texture *marks_texture;
676 struct sway_container_state *state = &child->current;
677
678 if (focus == child || parent_focused) {
679 colors = &config->border_colors.focused;
680 title_texture = child->title_focused;
681 marks_texture = view->marks_focused;
682 } else if (seat_get_focus_inactive(seat, con) == child) {
683 colors = &config->border_colors.focused_inactive;
684 title_texture = child->title_focused_inactive;
685 marks_texture = view->marks_focused_inactive;
686 } else {
687 colors = &config->border_colors.unfocused;
688 title_texture = child->title_unfocused;
689 marks_texture = view->marks_unfocused;
690 }
691
692 if (state->border == B_NORMAL) {
693 render_titlebar(output, damage, child, state->swayc_x,
694 state->swayc_y, state->swayc_width, colors,
695 title_texture, marks_texture);
696 } else {
697 render_top_border(output, damage, child, colors);
698 }
699 render_view(output, damage, child, colors);
700 } else {
701 render_container(output, damage, child,
702 parent_focused || focus == child);
703 }
704 }
705}
706
707/**
708 * Render a container's children using the L_TABBED layout.
709 */
710static void render_container_tabbed(struct sway_output *output,
711 pixman_region32_t *damage, struct sway_container *con,
712 bool parent_focused) {
713 if (!con->current.children->length) {
714 return;
715 }
716 struct sway_seat *seat = input_manager_current_seat(input_manager);
717 struct sway_container *focus = seat_get_focus(seat);
718 struct sway_container *current = seat_get_active_current_child(seat, con);
719 struct border_colors *current_colors = &config->border_colors.unfocused;
720 struct sway_container_state *pstate = &con->current;
721
722 // Render tabs
723 for (int i = 0; i < con->current.children->length; ++i) {
724 struct sway_container *child = con->current.children->items[i];
725 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
726 struct sway_container_state *cstate = &child->current;
727 struct border_colors *colors;
728 struct wlr_texture *title_texture;
729 struct wlr_texture *marks_texture;
730
731 if (focus == child || parent_focused) {
732 colors = &config->border_colors.focused;
733 title_texture = child->title_focused;
734 marks_texture = view ? view->marks_focused : NULL;
735 } else if (child == current) {
736 colors = &config->border_colors.focused_inactive;
737 title_texture = child->title_focused_inactive;
738 marks_texture = view ? view->marks_focused_inactive : NULL;
739 } else {
740 colors = &config->border_colors.unfocused;
741 title_texture = child->title_unfocused;
742 marks_texture = view ? view->marks_unfocused : NULL;
743 }
744
745 int tab_width = pstate->swayc_width / pstate->children->length;
746 int x = pstate->swayc_x + tab_width * i;
747 // Make last tab use the remaining width of the parent
748 if (i == pstate->children->length - 1) {
749 tab_width = pstate->swayc_width - tab_width * i;
750 }
751
752 render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width,
753 colors, title_texture, marks_texture);
754
755 if (child == current) {
756 current_colors = colors;
757 }
758 }
759
760 // Render surface and left/right/bottom borders
761 if (current) {
762 if (current->type == C_VIEW) {
763 render_view(output, damage, current, current_colors);
764 } else {
765 render_container(output, damage, current,
766 parent_focused || current == focus);
767 }
768 }
769}
770
771/**
772 * Render a container's children using the L_STACKED layout.
773 */
774static void render_container_stacked(struct sway_output *output,
775 pixman_region32_t *damage, struct sway_container *con,
776 bool parent_focused) {
777 if (!con->current.children->length) {
778 return;
779 }
780 struct sway_seat *seat = input_manager_current_seat(input_manager);
781 struct sway_container *focus = seat_get_focus(seat);
782 struct sway_container *current = seat_get_active_current_child(seat, con);
783 struct border_colors *current_colors = &config->border_colors.unfocused;
784 struct sway_container_state *pstate = &con->current;
785
786 // Render titles
787 for (int i = 0; i < con->current.children->length; ++i) {
788 struct sway_container *child = con->current.children->items[i];
789 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
790 struct sway_container_state *cstate = &child->current;
791 struct border_colors *colors;
792 struct wlr_texture *title_texture;
793 struct wlr_texture *marks_texture;
794
795 if (focus == child || parent_focused) {
796 colors = &config->border_colors.focused;
797 title_texture = child->title_focused;
798 marks_texture = view ? view->marks_focused : NULL;
799 } else if (child == current) {
800 colors = &config->border_colors.focused_inactive;
801 title_texture = child->title_focused_inactive;
802 marks_texture = view ? view->marks_focused_inactive : NULL;
803 } else {
804 colors = &config->border_colors.unfocused;
805 title_texture = child->title_unfocused;
806 marks_texture = view ? view->marks_unfocused : NULL;
807 }
808
809 int y = pstate->swayc_y + container_titlebar_height() * i;
810 render_titlebar(output, damage, child, cstate->swayc_x, y,
811 cstate->swayc_width, colors, title_texture, marks_texture);
812
813 if (child == current) {
814 current_colors = colors;
815 }
816 }
817
818 // Render surface and left/right/bottom borders
819 if (current) {
820 if (current->type == C_VIEW) {
821 render_view(output, damage, current, current_colors);
822 } else {
823 render_container(output, damage, current,
824 parent_focused || current == focus);
825 }
826 }
827}
828
829static void render_container(struct sway_output *output,
830 pixman_region32_t *damage, struct sway_container *con,
831 bool parent_focused) {
832 switch (con->current.layout) {
833 case L_NONE:
834 case L_HORIZ:
835 case L_VERT:
836 render_container_simple(output, damage, con, parent_focused);
837 break;
838 case L_STACKED:
839 render_container_stacked(output, damage, con, parent_focused);
840 break;
841 case L_TABBED:
842 render_container_tabbed(output, damage, con, parent_focused);
843 break;
844 case L_FLOATING:
845 sway_assert(false, "Didn't expect to see floating here");
846 }
847}
848
849static void render_floating_container(struct sway_output *soutput,
850 pixman_region32_t *damage, struct sway_container *con) {
851 if (con->type == C_VIEW) {
852 struct sway_view *view = con->sway_view;
853 struct sway_seat *seat = input_manager_current_seat(input_manager);
854 struct sway_container *focus = seat_get_focus(seat);
855 struct border_colors *colors;
856 struct wlr_texture *title_texture;
857 struct wlr_texture *marks_texture;
858
859 if (focus == con) {
860 colors = &config->border_colors.focused;
861 title_texture = con->title_focused;
862 marks_texture = view->marks_focused;
863 } else {
864 colors = &config->border_colors.unfocused;
865 title_texture = con->title_unfocused;
866 marks_texture = view->marks_unfocused;
867 }
868
869 if (con->current.border == B_NORMAL) {
870 render_titlebar(soutput, damage, con, con->current.swayc_x,
871 con->current.swayc_y, con->current.swayc_width, colors,
872 title_texture, marks_texture);
873 } else if (con->current.border != B_NONE) {
874 render_top_border(soutput, damage, con, colors);
875 }
876 render_view(soutput, damage, con, colors);
877 } else {
878 render_container(soutput, damage, con, false);
879 }
880}
881
882static void render_floating(struct sway_output *soutput,
883 pixman_region32_t *damage) {
884 for (int i = 0; i < root_container.current.children->length; ++i) {
885 struct sway_container *output =
886 root_container.current.children->items[i];
887 for (int j = 0; j < output->current.children->length; ++j) {
888 struct sway_container *ws = output->current.children->items[j];
889 if (!workspace_is_visible(ws)) {
890 continue;
891 }
892 list_t *floating =
893 ws->current.ws_floating->current.children;
894 for (int k = 0; k < floating->length; ++k) {
895 struct sway_container *floater = floating->items[k];
896 render_floating_container(soutput, damage, floater);
897 }
898 }
899 }
900}
901
902static struct sway_container *output_get_active_workspace(
903 struct sway_output *output) {
904 struct sway_seat *seat = input_manager_current_seat(input_manager); 170 struct sway_seat *seat = input_manager_current_seat(input_manager);
905 struct sway_container *focus = 171 struct sway_container *focus =
906 seat_get_focus_inactive(seat, output->swayc); 172 seat_get_focus_inactive(seat, output->swayc);
@@ -915,117 +181,58 @@ static struct sway_container *output_get_active_workspace(
915 return workspace; 181 return workspace;
916} 182}
917 183
918static void render_output(struct sway_output *output, struct timespec *when, 184bool output_has_opaque_lockscreen(struct sway_output *output,
919 pixman_region32_t *damage) { 185 struct sway_seat *seat) {
920 struct wlr_output *wlr_output = output->wlr_output; 186 if (!seat->exclusive_client) {
921 187 return false;
922 struct wlr_renderer *renderer =
923 wlr_backend_get_renderer(wlr_output->backend);
924 if (!sway_assert(renderer != NULL,
925 "expected the output backend to have a renderer")) {
926 return;
927 }
928
929 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
930
931 bool damage_whole_before_swap = false;
932 if (!pixman_region32_not_empty(damage)) {
933 // Output isn't damaged but needs buffer swap
934 goto renderer_end;
935 }
936
937 const char *damage_debug = getenv("SWAY_DAMAGE_DEBUG");
938 if (damage_debug != NULL) {
939 if (strcmp(damage_debug, "highlight") == 0) {
940 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
941 damage_whole_before_swap = true;
942 } else if (strcmp(damage_debug, "rerender") == 0) {
943 int width, height;
944 wlr_output_transformed_resolution(wlr_output, &width, &height);
945 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
946 }
947 } 188 }
948 189
949 struct sway_container *workspace = output_get_active_workspace(output); 190 struct wlr_layer_surface *wlr_layer_surface;
950 struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; 191 wl_list_for_each(wlr_layer_surface, &server.layer_shell->surfaces, link) {
951 192 if (wlr_layer_surface->output != output->wlr_output) {
952 if (fullscreen_view) { 193 continue;
953 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
954
955 int nrects;
956 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
957 for (int i = 0; i < nrects; ++i) {
958 scissor_output(wlr_output, &rects[i]);
959 wlr_renderer_clear(renderer, clear_color);
960 } 194 }
961 195 struct wlr_surface *wlr_surface = wlr_layer_surface->surface;
962 // TODO: handle views smaller than the output 196 if (wlr_surface->resource->client != seat->exclusive_client) {
963 render_view_surfaces(fullscreen_view, output, damage, 1.0f); 197 continue;
964
965 if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) {
966 render_unmanaged(output, damage,
967 &root_container.sway_root->xwayland_unmanaged);
968 } 198 }
969 } else { 199 struct sway_layer_surface *sway_layer_surface =
970 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; 200 layer_from_wlr_layer_surface(wlr_layer_surface);
971 201 pixman_box32_t output_box = {
972 int nrects; 202 .x2 = output->swayc->current.swayc_width,
973 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); 203 .y2 = output->swayc->current.swayc_height,
974 for (int i = 0; i < nrects; ++i) { 204 };
975 scissor_output(wlr_output, &rects[i]); 205 pixman_region32_t surface_opaque_box;
976 wlr_renderer_clear(renderer, clear_color); 206 pixman_region32_init(&surface_opaque_box);
207 pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region);
208 pixman_region32_translate(&surface_opaque_box,
209 sway_layer_surface->geo.x, sway_layer_surface->geo.y);
210 bool contains = pixman_region32_contains_rectangle(&surface_opaque_box,
211 &output_box);
212 pixman_region32_fini(&surface_opaque_box);
213 if (contains) {
214 return true;
977 } 215 }
978
979 render_layer(output, damage,
980 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
981 render_layer(output, damage,
982 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
983
984 struct sway_seat *seat = input_manager_current_seat(input_manager);
985 struct sway_container *focus = seat_get_focus(seat);
986 render_container(output, damage, workspace, focus == workspace);
987 render_floating(output, damage);
988
989 render_unmanaged(output, damage,
990 &root_container.sway_root->xwayland_unmanaged);
991 render_layer(output, damage,
992 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
993 }
994 render_layer(output, damage,
995 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
996 render_drag_icons(output, damage, &root_container.sway_root->drag_icons);
997
998renderer_end:
999 if (root_container.sway_root->debug_tree) {
1000 wlr_render_texture(renderer, root_container.sway_root->debug_tree,
1001 wlr_output->transform_matrix, 0, 0, 1);
1002 }
1003
1004 if (damage_whole_before_swap || root_container.sway_root->debug_tree) {
1005 int width, height;
1006 wlr_output_transformed_resolution(wlr_output, &width, &height);
1007 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1008 } 216 }
1009 217 return false;
1010 wlr_renderer_scissor(renderer, NULL);
1011 wlr_renderer_end(renderer);
1012 if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) {
1013 return;
1014 }
1015 output->last_frame = *when;
1016} 218}
1017 219
1018struct send_frame_done_data { 220struct send_frame_done_data {
1019 struct root_geometry root_geo; 221 struct root_geometry root_geo;
1020 struct sway_output *output; 222 struct sway_output *output;
1021 struct timespec *when; 223 struct timespec *when;
224 struct wl_client *exclusive_client;
1022}; 225};
1023 226
1024static void send_frame_done_iterator(struct wlr_surface *surface, 227static void send_frame_done_iterator(struct wlr_surface *surface,
1025 int sx, int sy, void *_data) { 228 int sx, int sy, void *_data) {
1026 struct send_frame_done_data *data = _data; 229 struct send_frame_done_data *data = _data;
230 if (data->exclusive_client &&
231 data->exclusive_client != surface->resource->client) {
232 return;
233 }
1027 234
1028 bool intersects = get_surface_box(&data->root_geo, data->output, surface, 235 bool intersects = output_get_surface_box(&data->root_geo, data->output, surface,
1029 sx, sy, NULL); 236 sx, sy, NULL);
1030 if (intersects) { 237 if (intersects) {
1031 wlr_surface_send_frame_done(surface, data->when); 238 wlr_surface_send_frame_done(surface, data->when);
@@ -1034,19 +241,19 @@ static void send_frame_done_iterator(struct wlr_surface *surface,
1034 241
1035static void send_frame_done_layer(struct send_frame_done_data *data, 242static void send_frame_done_layer(struct send_frame_done_data *data,
1036 struct wl_list *layer_surfaces) { 243 struct wl_list *layer_surfaces) {
1037 layer_for_each_surface(layer_surfaces, &data->root_geo, 244 output_layer_for_each_surface(layer_surfaces, &data->root_geo,
1038 send_frame_done_iterator, data); 245 send_frame_done_iterator, data);
1039} 246}
1040 247
1041static void send_frame_done_unmanaged(struct send_frame_done_data *data, 248static void send_frame_done_unmanaged(struct send_frame_done_data *data,
1042 struct wl_list *unmanaged) { 249 struct wl_list *unmanaged) {
1043 unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo, 250 output_unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo,
1044 send_frame_done_iterator, data); 251 send_frame_done_iterator, data);
1045} 252}
1046 253
1047static void send_frame_done_drag_icons(struct send_frame_done_data *data, 254static void send_frame_done_drag_icons(struct send_frame_done_data *data,
1048 struct wl_list *drag_icons) { 255 struct wl_list *drag_icons) {
1049 drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo, 256 output_drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo,
1050 send_frame_done_iterator, data); 257 send_frame_done_iterator, data);
1051} 258}
1052 259
@@ -1061,7 +268,7 @@ static void send_frame_done_container_iterator(struct sway_container *con,
1061 return; 268 return;
1062 } 269 }
1063 270
1064 output_view_for_each_surface(con->sway_view, &data->root_geo, 271 output_view_for_each_surface(con->sway_view, data->output, &data->root_geo,
1065 send_frame_done_iterator, data); 272 send_frame_done_iterator, data);
1066} 273}
1067 274
@@ -1072,9 +279,12 @@ static void send_frame_done_container(struct send_frame_done_data *data,
1072} 279}
1073 280
1074static void send_frame_done(struct sway_output *output, struct timespec *when) { 281static void send_frame_done(struct sway_output *output, struct timespec *when) {
282 struct sway_seat *seat = input_manager_current_seat(input_manager);
1075 struct send_frame_done_data data = { 283 struct send_frame_done_data data = {
1076 .output = output, 284 .output = output,
1077 .when = when, 285 .when = when,
286 .exclusive_client = output_has_opaque_lockscreen(output, seat) ?
287 seat->exclusive_client : NULL,
1078 }; 288 };
1079 289
1080 struct sway_container *workspace = output_get_active_workspace(output); 290 struct sway_container *workspace = output_get_active_workspace(output);
@@ -1125,7 +335,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) {
1125 } 335 }
1126 336
1127 if (needs_swap) { 337 if (needs_swap) {
1128 render_output(output, &now, &damage); 338 output_render(output, &now, &damage);
1129 } 339 }
1130 340
1131 pixman_region32_fini(&damage); 341 pixman_region32_fini(&damage);
@@ -1152,7 +362,7 @@ static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy,
1152 bool whole = data->whole; 362 bool whole = data->whole;
1153 363
1154 struct wlr_box box; 364 struct wlr_box box;
1155 bool intersects = get_surface_box(&data->root_geo, data->output, surface, 365 bool intersects = output_get_surface_box(&data->root_geo, data->output, surface,
1156 sx, sy, &box); 366 sx, sy, &box);
1157 if (!intersects) { 367 if (!intersects) {
1158 return; 368 return;
@@ -1163,16 +373,22 @@ static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy,
1163 int center_x = box.x + box.width/2; 373 int center_x = box.x + box.width/2;
1164 int center_y = box.y + box.height/2; 374 int center_y = box.y + box.height/2;
1165 375
1166 if (pixman_region32_not_empty(&surface->current->surface_damage)) { 376 if (pixman_region32_not_empty(&surface->buffer_damage)) {
377 enum wl_output_transform transform =
378 wlr_output_transform_invert(surface->current.transform);
379
1167 pixman_region32_t damage; 380 pixman_region32_t damage;
1168 pixman_region32_init(&damage); 381 pixman_region32_init(&damage);
1169 pixman_region32_copy(&damage, &surface->current->surface_damage); 382 pixman_region32_copy(&damage, &surface->buffer_damage);
1170 wlr_region_scale(&damage, &damage, output->wlr_output->scale); 383 wlr_region_transform(&damage, &damage, transform,
1171 if (ceil(output->wlr_output->scale) > surface->current->scale) { 384 surface->current.buffer_width, surface->current.buffer_height);
385 wlr_region_scale(&damage, &damage,
386 output->wlr_output->scale / (float)surface->current.scale);
387 if (ceil(output->wlr_output->scale) > surface->current.scale) {
1172 // When scaling up a surface, it'll become blurry so we need to 388 // When scaling up a surface, it'll become blurry so we need to
1173 // expand the damage region 389 // expand the damage region
1174 wlr_region_expand(&damage, &damage, 390 wlr_region_expand(&damage, &damage,
1175 ceil(output->wlr_output->scale) - surface->current->scale); 391 ceil(output->wlr_output->scale) - surface->current.scale);
1176 } 392 }
1177 pixman_region32_translate(&damage, box.x, box.y); 393 pixman_region32_translate(&damage, box.x, box.y);
1178 wlr_region_rotated_bounds(&damage, &damage, rotation, 394 wlr_region_rotated_bounds(&damage, &damage, rotation,
@@ -1196,7 +412,7 @@ void output_damage_surface(struct sway_output *output, double ox, double oy,
1196 .whole = whole, 412 .whole = whole,
1197 }; 413 };
1198 414
1199 surface_for_each_surface(surface, ox, oy, &data.root_geo, 415 output_surface_for_each_surface(surface, ox, oy, &data.root_geo,
1200 damage_surface_iterator, &data); 416 damage_surface_iterator, &data);
1201} 417}
1202 418
@@ -1215,7 +431,7 @@ static void output_damage_view(struct sway_output *output,
1215 .whole = whole, 431 .whole = whole,
1216 }; 432 };
1217 433
1218 output_view_for_each_surface(view, &data.root_geo, 434 output_view_for_each_surface(view, output, &data.root_geo,
1219 damage_surface_iterator, &data); 435 damage_surface_iterator, &data);
1220} 436}
1221 437
@@ -1247,11 +463,12 @@ static void output_damage_whole_container_iterator(struct sway_container *con,
1247 463
1248void output_damage_whole_container(struct sway_output *output, 464void output_damage_whole_container(struct sway_output *output,
1249 struct sway_container *con) { 465 struct sway_container *con) {
466 // Pad the box by 1px, because the width is a double and might be a fraction
1250 struct wlr_box box = { 467 struct wlr_box box = {
1251 .x = con->current.swayc_x - output->wlr_output->lx, 468 .x = con->current.swayc_x - output->wlr_output->lx - 1,
1252 .y = con->current.swayc_y - output->wlr_output->ly, 469 .y = con->current.swayc_y - output->wlr_output->ly - 1,
1253 .width = con->current.swayc_width, 470 .width = con->current.swayc_width + 2,
1254 .height = con->current.swayc_height, 471 .height = con->current.swayc_height + 2,
1255 }; 472 };
1256 scale_box(&box, output->wlr_output->scale); 473 scale_box(&box, output->wlr_output->scale);
1257 wlr_output_damage_add_box(output->damage, &box); 474 wlr_output_damage_add_box(output->damage, &box);
@@ -1276,19 +493,21 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
1276 output->wlr_output->data = NULL; 493 output->wlr_output->data = NULL;
1277 free(output); 494 free(output);
1278 495
1279 arrange_and_commit(&root_container); 496 arrange_windows(&root_container);
1280} 497}
1281 498
1282static void handle_mode(struct wl_listener *listener, void *data) { 499static void handle_mode(struct wl_listener *listener, void *data) {
1283 struct sway_output *output = wl_container_of(listener, output, mode); 500 struct sway_output *output = wl_container_of(listener, output, mode);
1284 arrange_layers(output); 501 arrange_layers(output);
1285 arrange_and_commit(output->swayc); 502 arrange_windows(output->swayc);
503 transaction_commit_dirty();
1286} 504}
1287 505
1288static void handle_transform(struct wl_listener *listener, void *data) { 506static void handle_transform(struct wl_listener *listener, void *data) {
1289 struct sway_output *output = wl_container_of(listener, output, transform); 507 struct sway_output *output = wl_container_of(listener, output, transform);
1290 arrange_layers(output); 508 arrange_layers(output);
1291 arrange_and_commit(output->swayc); 509 arrange_windows(output->swayc);
510 transaction_commit_dirty();
1292} 511}
1293 512
1294static void handle_scale_iterator(struct sway_container *view, void *data) { 513static void handle_scale_iterator(struct sway_container *view, void *data) {
@@ -1299,7 +518,8 @@ static void handle_scale(struct wl_listener *listener, void *data) {
1299 struct sway_output *output = wl_container_of(listener, output, scale); 518 struct sway_output *output = wl_container_of(listener, output, scale);
1300 arrange_layers(output); 519 arrange_layers(output);
1301 container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); 520 container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL);
1302 arrange_and_commit(output->swayc); 521 arrange_windows(output->swayc);
522 transaction_commit_dirty();
1303} 523}
1304 524
1305struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { 525struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) {
@@ -1309,7 +529,7 @@ struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) {
1309void handle_new_output(struct wl_listener *listener, void *data) { 529void handle_new_output(struct wl_listener *listener, void *data) {
1310 struct sway_server *server = wl_container_of(listener, server, new_output); 530 struct sway_server *server = wl_container_of(listener, server, new_output);
1311 struct wlr_output *wlr_output = data; 531 struct wlr_output *wlr_output = data;
1312 wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); 532 wlr_log(WLR_DEBUG, "New output %p: %s", wlr_output, wlr_output->name);
1313 533
1314 struct sway_output *output = calloc(1, sizeof(struct sway_output)); 534 struct sway_output *output = calloc(1, sizeof(struct sway_output));
1315 if (!output) { 535 if (!output) {
@@ -1368,5 +588,6 @@ void output_enable(struct sway_output *output) {
1368 output->damage_destroy.notify = damage_handle_destroy; 588 output->damage_destroy.notify = damage_handle_destroy;
1369 589
1370 arrange_layers(output); 590 arrange_layers(output);
1371 arrange_and_commit(&root_container); 591 arrange_windows(&root_container);
592 transaction_commit_dirty();
1372} 593}
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
new file mode 100644
index 00000000..7da54594
--- /dev/null
+++ b/sway/desktop/render.c
@@ -0,0 +1,919 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
3#include <stdlib.h>
4#include <strings.h>
5#include <time.h>
6#include <wayland-server.h>
7#include <wlr/render/wlr_renderer.h>
8#include <wlr/types/wlr_box.h>
9#include <wlr/types/wlr_buffer.h>
10#include <wlr/types/wlr_matrix.h>
11#include <wlr/types/wlr_output_damage.h>
12#include <wlr/types/wlr_output_layout.h>
13#include <wlr/types/wlr_output.h>
14#include <wlr/types/wlr_surface.h>
15#include <wlr/util/region.h>
16#include "log.h"
17#include "sway/config.h"
18#include "sway/debug.h"
19#include "sway/input/input-manager.h"
20#include "sway/input/seat.h"
21#include "sway/layers.h"
22#include "sway/output.h"
23#include "sway/server.h"
24#include "sway/tree/arrange.h"
25#include "sway/tree/container.h"
26#include "sway/tree/layout.h"
27#include "sway/tree/view.h"
28#include "sway/tree/workspace.h"
29
30struct render_data {
31 struct root_geometry root_geo;
32 struct sway_output *output;
33 pixman_region32_t *damage;
34 struct sway_view *view;
35 float alpha;
36};
37
38static void scale_box(struct wlr_box *box, float scale) {
39 box->x *= scale;
40 box->y *= scale;
41 box->width *= scale;
42 box->height *= scale;
43}
44
45static void scissor_output(struct wlr_output *wlr_output,
46 pixman_box32_t *rect) {
47 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
48 assert(renderer);
49
50 struct wlr_box box = {
51 .x = rect->x1,
52 .y = rect->y1,
53 .width = rect->x2 - rect->x1,
54 .height = rect->y2 - rect->y1,
55 };
56
57 int ow, oh;
58 wlr_output_transformed_resolution(wlr_output, &ow, &oh);
59
60 enum wl_output_transform transform =
61 wlr_output_transform_invert(wlr_output->transform);
62 wlr_box_transform(&box, transform, ow, oh, &box);
63
64 wlr_renderer_scissor(renderer, &box);
65}
66
67static void render_texture(struct wlr_output *wlr_output,
68 pixman_region32_t *output_damage, struct wlr_texture *texture,
69 const struct wlr_box *box, const float matrix[static 9], float alpha) {
70 struct wlr_renderer *renderer =
71 wlr_backend_get_renderer(wlr_output->backend);
72
73 pixman_region32_t damage;
74 pixman_region32_init(&damage);
75 pixman_region32_union_rect(&damage, &damage, box->x, box->y,
76 box->width, box->height);
77 pixman_region32_intersect(&damage, &damage, output_damage);
78 bool damaged = pixman_region32_not_empty(&damage);
79 if (!damaged) {
80 goto damage_finish;
81 }
82
83 int nrects;
84 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
85 for (int i = 0; i < nrects; ++i) {
86 scissor_output(wlr_output, &rects[i]);
87 wlr_render_texture_with_matrix(renderer, texture, matrix, alpha);
88 }
89
90damage_finish:
91 pixman_region32_fini(&damage);
92}
93
94static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy,
95 void *_data) {
96 struct render_data *data = _data;
97 struct wlr_output *wlr_output = data->output->wlr_output;
98 float rotation = data->root_geo.rotation;
99 pixman_region32_t *output_damage = data->damage;
100 float alpha = data->alpha;
101
102 struct wlr_texture *texture = wlr_surface_get_texture(surface);
103 if (!texture) {
104 return;
105 }
106
107 struct wlr_box box;
108 bool intersects = output_get_surface_box(&data->root_geo, data->output,
109 surface, sx, sy, &box);
110 if (!intersects) {
111 return;
112 }
113
114 scale_box(&box, wlr_output->scale);
115
116 float matrix[9];
117 enum wl_output_transform transform =
118 wlr_output_transform_invert(surface->current.transform);
119 wlr_matrix_project_box(matrix, &box, transform, rotation,
120 wlr_output->transform_matrix);
121
122 render_texture(wlr_output, output_damage, texture, &box, matrix, alpha);
123}
124
125static void render_layer(struct sway_output *output,
126 pixman_region32_t *damage, struct wl_list *layer_surfaces) {
127 struct render_data data = {
128 .output = output,
129 .damage = damage,
130 .alpha = 1.0f,
131 };
132 output_layer_for_each_surface(layer_surfaces, &data.root_geo,
133 render_surface_iterator, &data);
134}
135
136static void render_unmanaged(struct sway_output *output,
137 pixman_region32_t *damage, struct wl_list *unmanaged) {
138 struct render_data data = {
139 .output = output,
140 .damage = damage,
141 .alpha = 1.0f,
142 };
143 output_unmanaged_for_each_surface(unmanaged, output, &data.root_geo,
144 render_surface_iterator, &data);
145}
146
147static void render_drag_icons(struct sway_output *output,
148 pixman_region32_t *damage, struct wl_list *drag_icons) {
149 struct render_data data = {
150 .output = output,
151 .damage = damage,
152 .alpha = 1.0f,
153 };
154 output_drag_icons_for_each_surface(drag_icons, output, &data.root_geo,
155 render_surface_iterator, &data);
156}
157
158static void render_rect(struct wlr_output *wlr_output,
159 pixman_region32_t *output_damage, const struct wlr_box *_box,
160 float color[static 4]) {
161 struct wlr_renderer *renderer =
162 wlr_backend_get_renderer(wlr_output->backend);
163
164 struct wlr_box box;
165 memcpy(&box, _box, sizeof(struct wlr_box));
166 box.x -= wlr_output->lx * wlr_output->scale;
167 box.y -= wlr_output->ly * wlr_output->scale;
168
169 pixman_region32_t damage;
170 pixman_region32_init(&damage);
171 pixman_region32_union_rect(&damage, &damage, box.x, box.y,
172 box.width, box.height);
173 pixman_region32_intersect(&damage, &damage, output_damage);
174 bool damaged = pixman_region32_not_empty(&damage);
175 if (!damaged) {
176 goto damage_finish;
177 }
178
179 int nrects;
180 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
181 for (int i = 0; i < nrects; ++i) {
182 scissor_output(wlr_output, &rects[i]);
183 wlr_render_rect(renderer, &box, color,
184 wlr_output->transform_matrix);
185 }
186
187damage_finish:
188 pixman_region32_fini(&damage);
189}
190
191static void premultiply_alpha(float color[4], float opacity) {
192 color[3] *= opacity;
193 color[0] *= color[3];
194 color[1] *= color[3];
195 color[2] *= color[3];
196}
197
198static void render_view_surfaces(struct sway_view *view,
199 struct sway_output *output, pixman_region32_t *damage, float alpha) {
200 struct render_data data = {
201 .output = output,
202 .damage = damage,
203 .view = view,
204 .alpha = alpha,
205 };
206 output_view_for_each_surface(view, output, &data.root_geo,
207 render_surface_iterator, &data);
208}
209
210static void render_saved_view(struct sway_view *view,
211 struct sway_output *output, pixman_region32_t *damage, float alpha) {
212 struct wlr_output *wlr_output = output->wlr_output;
213
214 int width, height;
215 struct wlr_texture *texture =
216 transaction_get_saved_texture(view, &width, &height);
217 if (!texture) {
218 return;
219 }
220 struct wlr_box box = {
221 .x = view->swayc->current.view_x - output->swayc->current.swayc_x,
222 .y = view->swayc->current.view_y - output->swayc->current.swayc_y,
223 .width = width,
224 .height = height,
225 };
226
227 struct wlr_box output_box = {
228 .width = output->swayc->current.swayc_width,
229 .height = output->swayc->current.swayc_height,
230 };
231
232 struct wlr_box intersection;
233 bool intersects = wlr_box_intersection(&output_box, &box, &intersection);
234 if (!intersects) {
235 return;
236 }
237
238 scale_box(&box, wlr_output->scale);
239
240 float matrix[9];
241 wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
242 wlr_output->transform_matrix);
243
244 render_texture(wlr_output, damage, texture, &box, matrix, alpha);
245}
246
247/**
248 * Render a view's surface and left/bottom/right borders.
249 */
250static void render_view(struct sway_output *output, pixman_region32_t *damage,
251 struct sway_container *con, struct border_colors *colors) {
252 struct sway_view *view = con->sway_view;
253 if (view->swayc->instructions->length) {
254 render_saved_view(view, output, damage, view->swayc->alpha);
255 } else {
256 render_view_surfaces(view, output, damage, view->swayc->alpha);
257 }
258
259 if (view->using_csd) {
260 return;
261 }
262
263 struct wlr_box box;
264 float output_scale = output->wlr_output->scale;
265 float color[4];
266 struct sway_container_state *state = &con->current;
267
268 if (state->border != B_NONE) {
269 if (state->border_left) {
270 memcpy(&color, colors->child_border, sizeof(float) * 4);
271 premultiply_alpha(color, con->alpha);
272 box.x = state->swayc_x;
273 box.y = state->view_y;
274 box.width = state->border_thickness;
275 box.height = state->view_height;
276 scale_box(&box, output_scale);
277 render_rect(output->wlr_output, damage, &box, color);
278 }
279
280 if (state->border_right) {
281 if (state->parent->current.children->length == 1
282 && state->parent->current.layout == L_HORIZ) {
283 memcpy(&color, colors->indicator, sizeof(float) * 4);
284 } else {
285 memcpy(&color, colors->child_border, sizeof(float) * 4);
286 }
287 premultiply_alpha(color, con->alpha);
288 box.x = state->view_x + state->view_width;
289 box.y = state->view_y;
290 box.width = state->border_thickness;
291 box.height = state->view_height;
292 scale_box(&box, output_scale);
293 render_rect(output->wlr_output, damage, &box, color);
294 }
295
296 if (state->border_bottom) {
297 if (state->parent->current.children->length == 1
298 && con->current.parent->current.layout == L_VERT) {
299 memcpy(&color, colors->indicator, sizeof(float) * 4);
300 } else {
301 memcpy(&color, colors->child_border, sizeof(float) * 4);
302 }
303 premultiply_alpha(color, con->alpha);
304 box.x = state->swayc_x;
305 box.y = state->view_y + state->view_height;
306 box.width = state->swayc_width;
307 box.height = state->border_thickness;
308 scale_box(&box, output_scale);
309 render_rect(output->wlr_output, damage, &box, color);
310 }
311 }
312}
313
314/**
315 * Render a titlebar.
316 *
317 * Care must be taken not to render over the same pixel multiple times,
318 * otherwise the colors will be incorrect when using opacity.
319 *
320 * The height is: 1px border, 3px padding, font height, 3px padding, 1px border
321 * The left side for L_TABBED is: 1px border, 2px padding, title
322 * The left side for other layouts is: 3px padding, title
323 */
324static void render_titlebar(struct sway_output *output,
325 pixman_region32_t *output_damage, struct sway_container *con,
326 int x, int y, int width,
327 struct border_colors *colors, struct wlr_texture *title_texture,
328 struct wlr_texture *marks_texture) {
329 struct wlr_box box;
330 float color[4];
331 struct sway_container_state *state = &con->current;
332 float output_scale = output->wlr_output->scale;
333 enum sway_container_layout layout = state->parent->current.layout;
334 list_t *children = state->parent->current.children;
335 bool is_last_child = children->items[children->length - 1] == con;
336 double output_x = output->swayc->current.swayc_x;
337 double output_y = output->swayc->current.swayc_y;
338
339 // Single pixel bar above title
340 memcpy(&color, colors->border, sizeof(float) * 4);
341 premultiply_alpha(color, con->alpha);
342 box.x = x;
343 box.y = y;
344 box.width = width;
345 box.height = TITLEBAR_BORDER_THICKNESS;
346 scale_box(&box, output_scale);
347 render_rect(output->wlr_output, output_damage, &box, color);
348
349 // Single pixel bar below title
350 size_t left_offset = 0, right_offset = 0;
351 bool connects_sides = false;
352 if (layout == L_HORIZ || layout == L_VERT ||
353 (layout == L_STACKED && is_last_child)) {
354 if (con->type == C_VIEW) {
355 left_offset = state->border_left * state->border_thickness;
356 right_offset = state->border_right * state->border_thickness;
357 connects_sides = true;
358 }
359 }
360 box.x = x + left_offset;
361 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
362 box.width = width - left_offset - right_offset;
363 box.height = TITLEBAR_BORDER_THICKNESS;
364 scale_box(&box, output_scale);
365 render_rect(output->wlr_output, output_damage, &box, color);
366
367 if (layout == L_TABBED) {
368 // Single pixel left edge
369 box.x = x;
370 box.y = y + TITLEBAR_BORDER_THICKNESS;
371 box.width = TITLEBAR_BORDER_THICKNESS;
372 box.height =
373 container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2;
374 scale_box(&box, output_scale);
375 render_rect(output->wlr_output, output_damage, &box, color);
376
377 // Single pixel right edge
378 box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale;
379 render_rect(output->wlr_output, output_damage, &box, color);
380 }
381
382 size_t inner_width = width - TITLEBAR_H_PADDING * 2;
383
384 // Marks
385 size_t marks_ob_width = 0; // output-buffer-local
386 if (config->show_marks && marks_texture) {
387 struct wlr_box texture_box;
388 wlr_texture_get_size(marks_texture,
389 &texture_box.width, &texture_box.height);
390 texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING)
391 * output_scale - texture_box.width;
392 texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
393
394 float matrix[9];
395 wlr_matrix_project_box(matrix, &texture_box,
396 WL_OUTPUT_TRANSFORM_NORMAL,
397 0.0, output->wlr_output->transform_matrix);
398
399 if (inner_width * output_scale < texture_box.width) {
400 texture_box.width = inner_width * output_scale;
401 }
402 render_texture(output->wlr_output, output_damage, marks_texture,
403 &texture_box, matrix, con->alpha);
404 marks_ob_width = texture_box.width;
405
406 // Gap between the marks and bottom padding, for when the marks texture
407 // height is smaller than the config's font height
408 memcpy(&color, colors->background, sizeof(float) * 4);
409 premultiply_alpha(color, con->alpha);
410 box.x = texture_box.x;
411 box.y = texture_box.y + texture_box.height;
412 box.width = texture_box.width;
413 box.height = config->font_height * output_scale - texture_box.height;
414 if (box.height > 0) {
415 render_rect(output->wlr_output, output_damage, &box, color);
416 }
417 }
418
419 // Title text
420 size_t title_ob_width = 0; // output-buffer-local
421 if (title_texture) {
422 struct wlr_box texture_box;
423 wlr_texture_get_size(title_texture,
424 &texture_box.width, &texture_box.height);
425 texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale;
426 texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
427
428 float matrix[9];
429 wlr_matrix_project_box(matrix, &texture_box,
430 WL_OUTPUT_TRANSFORM_NORMAL,
431 0.0, output->wlr_output->transform_matrix);
432
433 if (inner_width * output_scale - marks_ob_width < texture_box.width) {
434 texture_box.width = inner_width * output_scale - marks_ob_width;
435 }
436 render_texture(output->wlr_output, output_damage, title_texture,
437 &texture_box, matrix, con->alpha);
438 title_ob_width = texture_box.width;
439
440 // Gap between the title and bottom padding, for when the title texture
441 // height is smaller than the config's font height
442 memcpy(&color, colors->background, sizeof(float) * 4);
443 premultiply_alpha(color, con->alpha);
444 box.x = texture_box.x;
445 box.y = texture_box.y + texture_box.height;
446 box.width = texture_box.width;
447 box.height = config->font_height * output_scale - texture_box.height;
448 if (box.height > 0) {
449 render_rect(output->wlr_output, output_damage, &box, color);
450 }
451 }
452
453 // Padding above title
454 memcpy(&color, colors->background, sizeof(float) * 4);
455 premultiply_alpha(color, con->alpha);
456 box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
457 box.y = y + TITLEBAR_BORDER_THICKNESS;
458 box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2;
459 box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS;
460 scale_box(&box, output_scale);
461 render_rect(output->wlr_output, output_damage, &box, color);
462
463 // Padding below title
464 box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale;
465 render_rect(output->wlr_output, output_damage, &box, color);
466
467 // Filler between title and marks
468 box.width = inner_width * output_scale - title_ob_width - marks_ob_width;
469 if (box.width > 0) {
470 box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_ob_width;
471 box.y = (y + TITLEBAR_V_PADDING) * output_scale;
472 box.height = config->font_height * output_scale;
473 render_rect(output->wlr_output, output_damage, &box, color);
474 }
475
476 // Padding left of title
477 left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
478 box.x = x + left_offset;
479 box.y = y + TITLEBAR_V_PADDING;
480 box.width = TITLEBAR_H_PADDING - left_offset;
481 box.height = config->font_height;
482 scale_box(&box, output_scale);
483 render_rect(output->wlr_output, output_damage, &box, color);
484
485 // Padding right of marks
486 right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
487 box.x = x + width - TITLEBAR_H_PADDING;
488 box.y = y + TITLEBAR_V_PADDING;
489 box.width = TITLEBAR_H_PADDING - right_offset;
490 box.height = config->font_height;
491 scale_box(&box, output_scale);
492 render_rect(output->wlr_output, output_damage, &box, color);
493
494 if (connects_sides) {
495 // Left pixel in line with bottom bar
496 box.x = x;
497 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
498 box.width = state->border_thickness * state->border_left;
499 box.height = TITLEBAR_BORDER_THICKNESS;
500 scale_box(&box, output_scale);
501 render_rect(output->wlr_output, output_damage, &box, color);
502
503 // Right pixel in line with bottom bar
504 box.x = x + width - state->border_thickness * state->border_right;
505 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
506 box.width = state->border_thickness * state->border_right;
507 box.height = TITLEBAR_BORDER_THICKNESS;
508 scale_box(&box, output_scale);
509 render_rect(output->wlr_output, output_damage, &box, color);
510 }
511}
512
513/**
514 * Render the top border line for a view using "border pixel".
515 */
516static void render_top_border(struct sway_output *output,
517 pixman_region32_t *output_damage, struct sway_container *con,
518 struct border_colors *colors) {
519 struct sway_container_state *state = &con->current;
520 if (!state->border_top) {
521 return;
522 }
523 struct wlr_box box;
524 float color[4];
525 float output_scale = output->wlr_output->scale;
526
527 // Child border - top edge
528 memcpy(&color, colors->child_border, sizeof(float) * 4);
529 premultiply_alpha(color, con->alpha);
530 box.x = state->swayc_x;
531 box.y = state->swayc_y;
532 box.width = state->swayc_width;
533 box.height = state->border_thickness;
534 scale_box(&box, output_scale);
535 render_rect(output->wlr_output, output_damage, &box, color);
536}
537
538static void render_container(struct sway_output *output,
539 pixman_region32_t *damage, struct sway_container *con, bool parent_focused);
540
541/**
542 * Render a container's children using a L_HORIZ or L_VERT layout.
543 *
544 * Wrap child views in borders and leave child containers borderless because
545 * they'll apply their own borders to their children.
546 */
547static void render_container_simple(struct sway_output *output,
548 pixman_region32_t *damage, struct sway_container *con,
549 bool parent_focused) {
550 for (int i = 0; i < con->current.children->length; ++i) {
551 struct sway_container *child = con->current.children->items[i];
552
553 if (child->type == C_VIEW) {
554 struct sway_view *view = child->sway_view;
555 struct border_colors *colors;
556 struct wlr_texture *title_texture;
557 struct wlr_texture *marks_texture;
558 struct sway_container_state *state = &child->current;
559
560 if (view_is_urgent(view)) {
561 colors = &config->border_colors.urgent;
562 title_texture = child->title_urgent;
563 marks_texture = view->marks_urgent;
564 } else if (state->focused || parent_focused) {
565 colors = &config->border_colors.focused;
566 title_texture = child->title_focused;
567 marks_texture = view->marks_focused;
568 } else if (con->current.focused_inactive_child == child) {
569 colors = &config->border_colors.focused_inactive;
570 title_texture = child->title_focused_inactive;
571 marks_texture = view->marks_focused_inactive;
572 } else {
573 colors = &config->border_colors.unfocused;
574 title_texture = child->title_unfocused;
575 marks_texture = view->marks_unfocused;
576 }
577
578 if (!view->using_csd) {
579 if (state->border == B_NORMAL) {
580 render_titlebar(output, damage, child, state->swayc_x,
581 state->swayc_y, state->swayc_width, colors,
582 title_texture, marks_texture);
583 } else {
584 render_top_border(output, damage, child, colors);
585 }
586 }
587 render_view(output, damage, child, colors);
588 } else {
589 render_container(output, damage, child,
590 parent_focused || child->current.focused);
591 }
592 }
593}
594
595/**
596 * Render a container's children using the L_TABBED layout.
597 */
598static void render_container_tabbed(struct sway_output *output,
599 pixman_region32_t *damage, struct sway_container *con,
600 bool parent_focused) {
601 if (!con->current.children->length) {
602 return;
603 }
604 struct sway_container_state *pstate = &con->current;
605 struct sway_container *current = pstate->focused_inactive_child;
606 struct border_colors *current_colors = &config->border_colors.unfocused;
607
608 double width_gap_adjustment = 2 * pstate->current_gaps;
609 int tab_width =
610 (pstate->swayc_width - width_gap_adjustment) / pstate->children->length;
611
612 // Render tabs
613 for (int i = 0; i < pstate->children->length; ++i) {
614 struct sway_container *child = pstate->children->items[i];
615 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
616 struct sway_container_state *cstate = &child->current;
617 struct border_colors *colors;
618 struct wlr_texture *title_texture;
619 struct wlr_texture *marks_texture;
620 bool urgent = view ?
621 view_is_urgent(view) : container_has_urgent_child(child);
622
623 if (urgent) {
624 colors = &config->border_colors.urgent;
625 title_texture = child->title_urgent;
626 marks_texture = view ? view->marks_urgent : NULL;
627 } else if (cstate->focused || parent_focused) {
628 colors = &config->border_colors.focused;
629 title_texture = child->title_focused;
630 marks_texture = view ? view->marks_focused : NULL;
631 } else if (child == pstate->focused_inactive_child) {
632 colors = &config->border_colors.focused_inactive;
633 title_texture = child->title_focused_inactive;
634 marks_texture = view ? view->marks_focused_inactive : NULL;
635 } else {
636 colors = &config->border_colors.unfocused;
637 title_texture = child->title_unfocused;
638 marks_texture = view ? view->marks_unfocused : NULL;
639 }
640
641 int x = cstate->swayc_x + tab_width * i;
642
643 // Make last tab use the remaining width of the parent
644 if (i == pstate->children->length - 1) {
645 tab_width =
646 pstate->swayc_width - width_gap_adjustment - tab_width * i;
647 }
648
649 render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width,
650 colors, title_texture, marks_texture);
651
652 if (child == current) {
653 current_colors = colors;
654 }
655 }
656
657 // Render surface and left/right/bottom borders
658 if (current->type == C_VIEW) {
659 render_view(output, damage, current, current_colors);
660 } else {
661 render_container(output, damage, current,
662 parent_focused || current->current.focused);
663 }
664}
665
666/**
667 * Render a container's children using the L_STACKED layout.
668 */
669static void render_container_stacked(struct sway_output *output,
670 pixman_region32_t *damage, struct sway_container *con,
671 bool parent_focused) {
672 if (!con->current.children->length) {
673 return;
674 }
675 struct sway_container_state *pstate = &con->current;
676 struct sway_container *current = pstate->focused_inactive_child;
677 struct border_colors *current_colors = &config->border_colors.unfocused;
678
679 size_t titlebar_height = container_titlebar_height();
680
681 // Render titles
682 for (int i = 0; i < pstate->children->length; ++i) {
683 struct sway_container *child = pstate->children->items[i];
684 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
685 struct sway_container_state *cstate = &child->current;
686 struct border_colors *colors;
687 struct wlr_texture *title_texture;
688 struct wlr_texture *marks_texture;
689 bool urgent = view ?
690 view_is_urgent(view) : container_has_urgent_child(child);
691
692 if (urgent) {
693 colors = &config->border_colors.urgent;
694 title_texture = child->title_urgent;
695 marks_texture = view ? view->marks_urgent : NULL;
696 } else if (cstate->focused || parent_focused) {
697 colors = &config->border_colors.focused;
698 title_texture = child->title_focused;
699 marks_texture = view ? view->marks_focused : NULL;
700 } else if (child == pstate->focused_inactive_child) {
701 colors = &config->border_colors.focused_inactive;
702 title_texture = child->title_focused_inactive;
703 marks_texture = view ? view->marks_focused_inactive : NULL;
704 } else {
705 colors = &config->border_colors.unfocused;
706 title_texture = child->title_unfocused;
707 marks_texture = view ? view->marks_unfocused : NULL;
708 }
709
710 int y = cstate->swayc_y + titlebar_height * i;
711 render_titlebar(output, damage, child, cstate->swayc_x, y,
712 cstate->swayc_width, colors, title_texture, marks_texture);
713
714 if (child == current) {
715 current_colors = colors;
716 }
717 }
718
719 // Render surface and left/right/bottom borders
720 if (current->type == C_VIEW) {
721 render_view(output, damage, current, current_colors);
722 } else {
723 render_container(output, damage, current,
724 parent_focused || current->current.focused);
725 }
726}
727
728static void render_container(struct sway_output *output,
729 pixman_region32_t *damage, struct sway_container *con,
730 bool parent_focused) {
731 switch (con->current.layout) {
732 case L_NONE:
733 case L_HORIZ:
734 case L_VERT:
735 render_container_simple(output, damage, con, parent_focused);
736 break;
737 case L_STACKED:
738 render_container_stacked(output, damage, con, parent_focused);
739 break;
740 case L_TABBED:
741 render_container_tabbed(output, damage, con, parent_focused);
742 break;
743 case L_FLOATING:
744 sway_assert(false, "Didn't expect to see floating here");
745 }
746}
747
748static void render_floating_container(struct sway_output *soutput,
749 pixman_region32_t *damage, struct sway_container *con) {
750 if (con->type == C_VIEW) {
751 struct sway_view *view = con->sway_view;
752 struct border_colors *colors;
753 struct wlr_texture *title_texture;
754 struct wlr_texture *marks_texture;
755
756 if (view_is_urgent(view)) {
757 colors = &config->border_colors.urgent;
758 title_texture = con->title_urgent;
759 marks_texture = view->marks_urgent;
760 } else if (con->current.focused) {
761 colors = &config->border_colors.focused;
762 title_texture = con->title_focused;
763 marks_texture = view->marks_focused;
764 } else {
765 colors = &config->border_colors.unfocused;
766 title_texture = con->title_unfocused;
767 marks_texture = view->marks_unfocused;
768 }
769
770 if (!view->using_csd) {
771 if (con->current.border == B_NORMAL) {
772 render_titlebar(soutput, damage, con, con->current.swayc_x,
773 con->current.swayc_y, con->current.swayc_width, colors,
774 title_texture, marks_texture);
775 } else if (con->current.border != B_NONE) {
776 render_top_border(soutput, damage, con, colors);
777 }
778 }
779 render_view(soutput, damage, con, colors);
780 } else {
781 render_container(soutput, damage, con, false);
782 }
783}
784
785static void render_floating(struct sway_output *soutput,
786 pixman_region32_t *damage) {
787 for (int i = 0; i < root_container.current.children->length; ++i) {
788 struct sway_container *output =
789 root_container.current.children->items[i];
790 for (int j = 0; j < output->current.children->length; ++j) {
791 struct sway_container *ws = output->current.children->items[j];
792 if (!workspace_is_visible(ws)) {
793 continue;
794 }
795 list_t *floating =
796 ws->current.ws_floating->current.children;
797 for (int k = 0; k < floating->length; ++k) {
798 struct sway_container *floater = floating->items[k];
799 render_floating_container(soutput, damage, floater);
800 }
801 }
802 }
803}
804
805const char *damage_debug = NULL;
806
807void output_render(struct sway_output *output, struct timespec *when,
808 pixman_region32_t *damage) {
809 struct wlr_output *wlr_output = output->wlr_output;
810
811 struct wlr_renderer *renderer =
812 wlr_backend_get_renderer(wlr_output->backend);
813 if (!sway_assert(renderer != NULL,
814 "expected the output backend to have a renderer")) {
815 return;
816 }
817
818 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
819
820 bool damage_whole_before_swap = false;
821 if (!pixman_region32_not_empty(damage)) {
822 // Output isn't damaged but needs buffer swap
823 goto renderer_end;
824 }
825
826 if (damage_debug != NULL) {
827 if (strcmp(damage_debug, "highlight") == 0) {
828 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
829 damage_whole_before_swap = true;
830 } else if (strcmp(damage_debug, "rerender") == 0) {
831 int width, height;
832 wlr_output_transformed_resolution(wlr_output, &width, &height);
833 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
834 }
835 }
836
837 struct sway_container *workspace = output_get_active_workspace(output);
838 struct sway_view *fullscreen_view = workspace->current.ws_fullscreen;
839 struct sway_seat *seat = input_manager_current_seat(input_manager);
840
841 if (output_has_opaque_lockscreen(output, seat) && seat->focused_layer) {
842 struct wlr_layer_surface *wlr_layer_surface = seat->focused_layer;
843 struct sway_layer_surface *sway_layer_surface =
844 layer_from_wlr_layer_surface(seat->focused_layer);
845 struct render_data data = {
846 .output = output,
847 .damage = damage,
848 .alpha = 1.0f,
849 };
850 output_surface_for_each_surface(wlr_layer_surface->surface,
851 sway_layer_surface->geo.x, sway_layer_surface->geo.y,
852 &data.root_geo, render_surface_iterator, &data);
853 } else if (fullscreen_view) {
854 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
855
856 int nrects;
857 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
858 for (int i = 0; i < nrects; ++i) {
859 scissor_output(wlr_output, &rects[i]);
860 wlr_renderer_clear(renderer, clear_color);
861 }
862
863 // TODO: handle views smaller than the output
864 if (fullscreen_view->swayc->instructions->length) {
865 render_saved_view(fullscreen_view, output, damage, 1.0f);
866 } else {
867 render_view_surfaces(fullscreen_view, output, damage, 1.0f);
868 }
869
870 if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) {
871 render_unmanaged(output, damage,
872 &root_container.sway_root->xwayland_unmanaged);
873 }
874 } else {
875 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
876
877 int nrects;
878 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
879 for (int i = 0; i < nrects; ++i) {
880 scissor_output(wlr_output, &rects[i]);
881 wlr_renderer_clear(renderer, clear_color);
882 }
883
884 render_layer(output, damage,
885 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
886 render_layer(output, damage,
887 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
888
889 render_container(output, damage, workspace, workspace->current.focused);
890 render_floating(output, damage);
891
892 render_unmanaged(output, damage,
893 &root_container.sway_root->xwayland_unmanaged);
894 render_layer(output, damage,
895 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
896 }
897 render_layer(output, damage,
898 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
899 render_drag_icons(output, damage, &root_container.sway_root->drag_icons);
900
901renderer_end:
902 if (root_container.sway_root->debug_tree) {
903 wlr_render_texture(renderer, root_container.sway_root->debug_tree,
904 wlr_output->transform_matrix, 0, 0, 1);
905 }
906
907 if (damage_whole_before_swap || root_container.sway_root->debug_tree) {
908 int width, height;
909 wlr_output_transformed_resolution(wlr_output, &width, &height);
910 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
911 }
912
913 wlr_renderer_scissor(renderer, NULL);
914 wlr_renderer_end(renderer);
915 if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) {
916 return;
917 }
918 output->last_frame = *when;
919}
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index d2932c87..2a89880a 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -4,8 +4,8 @@
4#include <string.h> 4#include <string.h>
5#include <time.h> 5#include <time.h>
6#include <wlr/types/wlr_buffer.h> 6#include <wlr/types/wlr_buffer.h>
7#include <wlr/types/wlr_linux_dmabuf.h>
8#include "sway/debug.h" 7#include "sway/debug.h"
8#include "sway/desktop/idle_inhibit_v1.h"
9#include "sway/desktop/transaction.h" 9#include "sway/desktop/transaction.h"
10#include "sway/output.h" 10#include "sway/output.h"
11#include "sway/tree/container.h" 11#include "sway/tree/container.h"
@@ -18,14 +18,14 @@
18 * How long we should wait for views to respond to the configure before giving 18 * How long we should wait for views to respond to the configure before giving
19 * up and applying the transaction anyway. 19 * up and applying the transaction anyway.
20 */ 20 */
21#define TIMEOUT_MS 200 21int txn_timeout_ms = 200;
22 22
23/** 23/**
24 * If enabled, sway will always wait for the transaction timeout before 24 * If enabled, sway will always wait for the transaction timeout before
25 * applying it, rather than applying it when the views are ready. This allows us 25 * applying it, rather than applying it when the views are ready. This allows us
26 * to observe the rendered state while a transaction is in progress. 26 * to observe the rendered state while a transaction is in progress.
27 */ 27 */
28#define TRANSACTION_DEBUG false 28bool txn_debug = false;
29 29
30struct sway_transaction { 30struct sway_transaction {
31 struct wl_event_source *timer; 31 struct wl_event_source *timer;
@@ -46,7 +46,7 @@ struct sway_transaction_instruction {
46 bool ready; 46 bool ready;
47}; 47};
48 48
49struct sway_transaction *transaction_create() { 49static struct sway_transaction *transaction_create() {
50 struct sway_transaction *transaction = 50 struct sway_transaction *transaction =
51 calloc(1, sizeof(struct sway_transaction)); 51 calloc(1, sizeof(struct sway_transaction));
52 transaction->instructions = create_list(); 52 transaction->instructions = create_list();
@@ -72,8 +72,8 @@ static void save_view_buffer(struct sway_view *view,
72 } 72 }
73 if (view->surface && wlr_surface_has_buffer(view->surface)) { 73 if (view->surface && wlr_surface_has_buffer(view->surface)) {
74 instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer); 74 instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer);
75 instruction->saved_buffer_width = view->surface->current->width; 75 instruction->saved_buffer_width = view->surface->current.width;
76 instruction->saved_buffer_height = view->surface->current->height; 76 instruction->saved_buffer_height = view->surface->current.height;
77 } 77 }
78} 78}
79 79
@@ -138,25 +138,18 @@ static void copy_pending_state(struct sway_container *container,
138 state->children = create_list(); 138 state->children = create_list();
139 list_cat(state->children, container->children); 139 list_cat(state->children, container->children);
140 } 140 }
141}
142 141
143static bool transaction_has_container(struct sway_transaction *transaction, 142 struct sway_seat *seat = input_manager_current_seat(input_manager);
144 struct sway_container *container) { 143 state->focused = seat_get_focus(seat) == container;
145 for (int i = 0; i < transaction->instructions->length; ++i) { 144
146 struct sway_transaction_instruction *instruction = 145 if (container->type != C_VIEW) {
147 transaction->instructions->items[i]; 146 state->focused_inactive_child =
148 if (instruction->container == container) { 147 seat_get_active_child(seat, container);
149 return true;
150 }
151 } 148 }
152 return false;
153} 149}
154 150
155void transaction_add_container(struct sway_transaction *transaction, 151static void transaction_add_container(struct sway_transaction *transaction,
156 struct sway_container *container) { 152 struct sway_container *container) {
157 if (transaction_has_container(transaction, container)) {
158 return;
159 }
160 struct sway_transaction_instruction *instruction = 153 struct sway_transaction_instruction *instruction =
161 calloc(1, sizeof(struct sway_transaction_instruction)); 154 calloc(1, sizeof(struct sway_transaction_instruction));
162 instruction->transaction = transaction; 155 instruction->transaction = transaction;
@@ -174,7 +167,7 @@ void transaction_add_container(struct sway_transaction *transaction,
174 * Apply a transaction to the "current" state of the tree. 167 * Apply a transaction to the "current" state of the tree.
175 */ 168 */
176static void transaction_apply(struct sway_transaction *transaction) { 169static void transaction_apply(struct sway_transaction *transaction) {
177 wlr_log(L_DEBUG, "Applying transaction %p", transaction); 170 wlr_log(WLR_DEBUG, "Applying transaction %p", transaction);
178 if (server.debug_txn_timings) { 171 if (server.debug_txn_timings) {
179 struct timespec now; 172 struct timespec now;
180 clock_gettime(CLOCK_MONOTONIC, &now); 173 clock_gettime(CLOCK_MONOTONIC, &now);
@@ -185,9 +178,9 @@ static void transaction_apply(struct sway_transaction *transaction) {
185 float ms_waiting = (now.tv_sec - commit->tv_sec) * 1000 + 178 float ms_waiting = (now.tv_sec - commit->tv_sec) * 1000 +
186 (now.tv_nsec - commit->tv_nsec) / 1000000.0; 179 (now.tv_nsec - commit->tv_nsec) / 1000000.0;
187 float ms_total = ms_arranging + ms_waiting; 180 float ms_total = ms_arranging + ms_waiting;
188 wlr_log(L_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, " 181 wlr_log(WLR_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, "
189 "%.1fms total (%.1f frames if 60Hz)", transaction, 182 "%.1fms total (%.1f frames if 60Hz)", transaction,
190 ms_arranging, ms_waiting, ms_total, ms_total / (1000 / 60)); 183 ms_arranging, ms_waiting, ms_total, ms_total / (1000.0f / 60));
191 } 184 }
192 185
193 // Apply the instruction state to the container's current state 186 // Apply the instruction state to the container's current state
@@ -209,10 +202,12 @@ static void transaction_apply(struct sway_transaction *transaction) {
209 .width = instruction->state.swayc_width, 202 .width = instruction->state.swayc_width,
210 .height = instruction->state.swayc_height, 203 .height = instruction->state.swayc_height,
211 }; 204 };
212 for (int j = 0; j < root_container.children->length; ++j) { 205 for (int j = 0; j < root_container.current.children->length; ++j) {
213 struct sway_container *output = root_container.children->items[j]; 206 struct sway_container *output = root_container.current.children->items[j];
214 output_damage_box(output->sway_output, &old_box); 207 if (output->sway_output) {
215 output_damage_box(output->sway_output, &new_box); 208 output_damage_box(output->sway_output, &old_box);
209 output_damage_box(output->sway_output, &new_box);
210 }
216 } 211 }
217 212
218 // There are separate children lists for each instruction state, the 213 // There are separate children lists for each instruction state, the
@@ -227,29 +222,22 @@ static void transaction_apply(struct sway_transaction *transaction) {
227 } 222 }
228} 223}
229 224
230/**
231 * For simplicity, we only progress the queue if it can be completely flushed.
232 */
233static void transaction_progress_queue() { 225static void transaction_progress_queue() {
234 // We iterate this list in reverse because we're more likely to find a 226 while (server.transactions->length) {
235 // waiting transactions at the end of the list. 227 struct sway_transaction *transaction = server.transactions->items[0];
236 for (int i = server.transactions->length - 1; i >= 0; --i) {
237 struct sway_transaction *transaction = server.transactions->items[i];
238 if (transaction->num_waiting) { 228 if (transaction->num_waiting) {
239 return; 229 return;
240 } 230 }
241 }
242 for (int i = 0; i < server.transactions->length; ++i) {
243 struct sway_transaction *transaction = server.transactions->items[i];
244 transaction_apply(transaction); 231 transaction_apply(transaction);
245 transaction_destroy(transaction); 232 transaction_destroy(transaction);
233 list_del(server.transactions, 0);
246 } 234 }
247 server.transactions->length = 0; 235 idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1);
248} 236}
249 237
250static int handle_timeout(void *data) { 238static int handle_timeout(void *data) {
251 struct sway_transaction *transaction = data; 239 struct sway_transaction *transaction = data;
252 wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting)", 240 wlr_log(WLR_DEBUG, "Transaction %p timed out (%li waiting)",
253 transaction, transaction->num_waiting); 241 transaction, transaction->num_waiting);
254 transaction->num_waiting = 0; 242 transaction->num_waiting = 0;
255 transaction_progress_queue(); 243 transaction_progress_queue();
@@ -283,8 +271,8 @@ static bool should_configure(struct sway_container *con,
283 return true; 271 return true;
284} 272}
285 273
286void transaction_commit(struct sway_transaction *transaction) { 274static void transaction_commit(struct sway_transaction *transaction) {
287 wlr_log(L_DEBUG, "Transaction %p committing with %i instructions", 275 wlr_log(WLR_DEBUG, "Transaction %p committing with %i instructions",
288 transaction, transaction->instructions->length); 276 transaction, transaction->instructions->length);
289 transaction->num_waiting = 0; 277 transaction->num_waiting = 0;
290 for (int i = 0; i < transaction->instructions->length; ++i) { 278 for (int i = 0; i < transaction->instructions->length; ++i) {
@@ -317,9 +305,10 @@ void transaction_commit(struct sway_transaction *transaction) {
317 } else { 305 } else {
318 // There are no other transactions in progress, and this one has nothing 306 // There are no other transactions in progress, and this one has nothing
319 // to wait for, so we can skip the queue. 307 // to wait for, so we can skip the queue.
320 wlr_log(L_DEBUG, "Transaction %p has nothing to wait for", transaction); 308 wlr_log(WLR_DEBUG, "Transaction %p has nothing to wait for", transaction);
321 transaction_apply(transaction); 309 transaction_apply(transaction);
322 transaction_destroy(transaction); 310 transaction_destroy(transaction);
311 idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1);
323 return; 312 return;
324 } 313 }
325 314
@@ -327,7 +316,7 @@ void transaction_commit(struct sway_transaction *transaction) {
327 // Set up a timer which the views must respond within 316 // Set up a timer which the views must respond within
328 transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, 317 transaction->timer = wl_event_loop_add_timer(server.wl_event_loop,
329 handle_timeout, transaction); 318 handle_timeout, transaction);
330 wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); 319 wl_event_source_timer_update(transaction->timer, txn_timeout_ms);
331 } 320 }
332 321
333 // The debug tree shows the pending/live tree. Here is a good place to 322 // The debug tree shows the pending/live tree. Here is a good place to
@@ -347,7 +336,7 @@ static void set_instruction_ready(
347 struct timespec *start = &transaction->commit_time; 336 struct timespec *start = &transaction->commit_time;
348 float ms = (now.tv_sec - start->tv_sec) * 1000 + 337 float ms = (now.tv_sec - start->tv_sec) * 1000 +
349 (now.tv_nsec - start->tv_nsec) / 1000000.0; 338 (now.tv_nsec - start->tv_nsec) / 1000000.0;
350 wlr_log(L_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)", 339 wlr_log(WLR_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)",
351 transaction, 340 transaction,
352 transaction->num_configures - transaction->num_waiting + 1, 341 transaction->num_configures - transaction->num_waiting + 1,
353 transaction->num_configures, ms, 342 transaction->num_configures, ms,
@@ -358,11 +347,11 @@ static void set_instruction_ready(
358 // If all views are ready, apply the transaction. 347 // If all views are ready, apply the transaction.
359 // If the transaction has timed out then its num_waiting will be 0 already. 348 // If the transaction has timed out then its num_waiting will be 0 already.
360 if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { 349 if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) {
361#if !TRANSACTION_DEBUG 350 if (!txn_debug) {
362 wlr_log(L_DEBUG, "Transaction %p is ready", transaction); 351 wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction);
363 wl_event_source_timer_update(transaction->timer, 0); 352 wl_event_source_timer_update(transaction->timer, 0);
364 transaction_progress_queue(); 353 transaction_progress_queue();
365#endif 354 }
366 } 355 }
367} 356}
368 357
@@ -374,7 +363,9 @@ static void set_instructions_ready(struct sway_view *view, int index) {
374 for (int i = 0; i <= index; ++i) { 363 for (int i = 0; i <= index; ++i) {
375 struct sway_transaction_instruction *instruction = 364 struct sway_transaction_instruction *instruction =
376 view->swayc->instructions->items[i]; 365 view->swayc->instructions->items[i];
377 set_instruction_ready(instruction); 366 if (!instruction->ready) {
367 set_instruction_ready(instruction);
368 }
378 } 369 }
379} 370}
380 371
@@ -413,3 +404,17 @@ struct wlr_texture *transaction_get_saved_texture(struct sway_view *view,
413 *height = instruction->saved_buffer_height; 404 *height = instruction->saved_buffer_height;
414 return instruction->saved_buffer->texture; 405 return instruction->saved_buffer->texture;
415} 406}
407
408void transaction_commit_dirty(void) {
409 if (!server.dirty_containers->length) {
410 return;
411 }
412 struct sway_transaction *transaction = transaction_create();
413 for (int i = 0; i < server.dirty_containers->length; ++i) {
414 struct sway_container *container = server.dirty_containers->items[i];
415 transaction_add_container(transaction, container);
416 container->dirty = false;
417 }
418 server.dirty_containers->length = 0;
419 transaction_commit(transaction);
420}
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 47604c31..f3e4fef8 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -1,4 +1,5 @@
1#define _POSIX_C_SOURCE 199309L 1#define _POSIX_C_SOURCE 199309L
2#include <float.h>
2#include <stdbool.h> 3#include <stdbool.h>
3#include <stdlib.h> 4#include <stdlib.h>
4#include <wayland-server.h> 5#include <wayland-server.h>
@@ -45,6 +46,24 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) {
45 view_child_destroy(&popup->child); 46 view_child_destroy(&popup->child);
46} 47}
47 48
49static void popup_unconstrain(struct sway_xdg_popup *popup) {
50 struct sway_view *view = popup->child.view;
51 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup;
52
53 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
54
55 // the output box expressed in the coordinate system of the toplevel parent
56 // of the popup
57 struct wlr_box output_toplevel_sx_box = {
58 .x = output->x - view->x,
59 .y = output->y - view->y,
60 .width = output->width,
61 .height = output->height,
62 };
63
64 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
65}
66
48static struct sway_xdg_popup *popup_create( 67static struct sway_xdg_popup *popup_create(
49 struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { 68 struct wlr_xdg_popup *wlr_popup, struct sway_view *view) {
50 struct wlr_xdg_surface *xdg_surface = wlr_popup->base; 69 struct wlr_xdg_surface *xdg_surface = wlr_popup->base;
@@ -55,12 +74,15 @@ static struct sway_xdg_popup *popup_create(
55 return NULL; 74 return NULL;
56 } 75 }
57 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); 76 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
77 popup->wlr_xdg_surface = xdg_surface;
58 78
59 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); 79 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
60 popup->new_popup.notify = popup_handle_new_popup; 80 popup->new_popup.notify = popup_handle_new_popup;
61 wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); 81 wl_signal_add(&xdg_surface->events.destroy, &popup->destroy);
62 popup->destroy.notify = popup_handle_destroy; 82 popup->destroy.notify = popup_handle_destroy;
63 83
84 popup_unconstrain(popup);
85
64 return popup; 86 return popup;
65} 87}
66 88
@@ -74,6 +96,16 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view(
74 return (struct sway_xdg_shell_view *)view; 96 return (struct sway_xdg_shell_view *)view;
75} 97}
76 98
99static void get_constraints(struct sway_view *view, double *min_width,
100 double *max_width, double *min_height, double *max_height) {
101 struct wlr_xdg_toplevel_state *state =
102 &view->wlr_xdg_surface->toplevel->current;
103 *min_width = state->min_width > 0 ? state->min_width : DBL_MIN;
104 *max_width = state->max_width > 0 ? state->max_width : DBL_MAX;
105 *min_height = state->min_height > 0 ? state->min_height : DBL_MIN;
106 *max_height = state->max_height > 0 ? state->max_height : DBL_MAX;
107}
108
77static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { 109static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) {
78 if (xdg_shell_view_from_view(view) == NULL) { 110 if (xdg_shell_view_from_view(view) == NULL) {
79 return NULL; 111 return NULL;
@@ -167,6 +199,7 @@ static void destroy(struct sway_view *view) {
167} 199}
168 200
169static const struct sway_view_impl view_impl = { 201static const struct sway_view_impl view_impl = {
202 .get_constraints = get_constraints,
170 .get_string_prop = get_string_prop, 203 .get_string_prop = get_string_prop,
171 .configure = configure, 204 .configure = configure,
172 .set_activated = set_activated, 205 .set_activated = set_activated,
@@ -192,10 +225,24 @@ static void handle_commit(struct wl_listener *listener, void *data) {
192 transaction_notify_view_ready(view, xdg_surface->configure_serial); 225 transaction_notify_view_ready(view, xdg_surface->configure_serial);
193 } 226 }
194 227
195 view_update_title(view, false);
196 view_damage_from(view); 228 view_damage_from(view);
197} 229}
198 230
231static void handle_set_title(struct wl_listener *listener, void *data) {
232 struct sway_xdg_shell_view *xdg_shell_view =
233 wl_container_of(listener, xdg_shell_view, set_title);
234 struct sway_view *view = &xdg_shell_view->view;
235 view_update_title(view, false);
236 view_execute_criteria(view);
237}
238
239static void handle_set_app_id(struct wl_listener *listener, void *data) {
240 struct sway_xdg_shell_view *xdg_shell_view =
241 wl_container_of(listener, xdg_shell_view, set_app_id);
242 struct sway_view *view = &xdg_shell_view->view;
243 view_execute_criteria(view);
244}
245
199static void handle_new_popup(struct wl_listener *listener, void *data) { 246static void handle_new_popup(struct wl_listener *listener, void *data) {
200 struct sway_xdg_shell_view *xdg_shell_view = 247 struct sway_xdg_shell_view *xdg_shell_view =
201 wl_container_of(listener, xdg_shell_view, new_popup); 248 wl_container_of(listener, xdg_shell_view, new_popup);
@@ -222,8 +269,37 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
222 269
223 view_set_fullscreen(view, e->fullscreen); 270 view_set_fullscreen(view, e->fullscreen);
224 271
225 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 272 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
226 arrange_and_commit(ws); 273 arrange_windows(output);
274 transaction_commit_dirty();
275}
276
277static void handle_request_move(struct wl_listener *listener, void *data) {
278 struct sway_xdg_shell_view *xdg_shell_view =
279 wl_container_of(listener, xdg_shell_view, request_move);
280 struct sway_view *view = &xdg_shell_view->view;
281 if (!container_is_floating(view->swayc)) {
282 return;
283 }
284 struct wlr_xdg_toplevel_move_event *e = data;
285 struct sway_seat *seat = e->seat->seat->data;
286 if (e->serial == seat->last_button_serial) {
287 seat_begin_move(seat, view->swayc, seat->last_button);
288 }
289}
290
291static void handle_request_resize(struct wl_listener *listener, void *data) {
292 struct sway_xdg_shell_view *xdg_shell_view =
293 wl_container_of(listener, xdg_shell_view, request_resize);
294 struct sway_view *view = &xdg_shell_view->view;
295 if (!container_is_floating(view->swayc)) {
296 return;
297 }
298 struct wlr_xdg_toplevel_resize_event *e = data;
299 struct sway_seat *seat = e->seat->seat->data;
300 if (e->serial == seat->last_button_serial) {
301 seat_begin_resize(seat, view->swayc, seat->last_button, e->edges);
302 }
227} 303}
228 304
229static void handle_unmap(struct wl_listener *listener, void *data) { 305static void handle_unmap(struct wl_listener *listener, void *data) {
@@ -240,6 +316,10 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
240 wl_list_remove(&xdg_shell_view->commit.link); 316 wl_list_remove(&xdg_shell_view->commit.link);
241 wl_list_remove(&xdg_shell_view->new_popup.link); 317 wl_list_remove(&xdg_shell_view->new_popup.link);
242 wl_list_remove(&xdg_shell_view->request_fullscreen.link); 318 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
319 wl_list_remove(&xdg_shell_view->request_move.link);
320 wl_list_remove(&xdg_shell_view->request_resize.link);
321 wl_list_remove(&xdg_shell_view->set_title.link);
322 wl_list_remove(&xdg_shell_view->set_app_id.link);
243} 323}
244 324
245static void handle_map(struct wl_listener *listener, void *data) { 325static void handle_map(struct wl_listener *listener, void *data) {
@@ -251,8 +331,8 @@ static void handle_map(struct wl_listener *listener, void *data) {
251 view->natural_width = view->wlr_xdg_surface->geometry.width; 331 view->natural_width = view->wlr_xdg_surface->geometry.width;
252 view->natural_height = view->wlr_xdg_surface->geometry.height; 332 view->natural_height = view->wlr_xdg_surface->geometry.height;
253 if (!view->natural_width && !view->natural_height) { 333 if (!view->natural_width && !view->natural_height) {
254 view->natural_width = view->wlr_xdg_surface->surface->current->width; 334 view->natural_width = view->wlr_xdg_surface->surface->current.width;
255 view->natural_height = view->wlr_xdg_surface->surface->current->height; 335 view->natural_height = view->wlr_xdg_surface->surface->current.height;
256 } 336 }
257 337
258 view_map(view, view->wlr_xdg_surface->surface); 338 view_map(view, view->wlr_xdg_surface->surface);
@@ -260,10 +340,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
260 if (xdg_surface->toplevel->client_pending.fullscreen) { 340 if (xdg_surface->toplevel->client_pending.fullscreen) {
261 view_set_fullscreen(view, true); 341 view_set_fullscreen(view, true);
262 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 342 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
263 arrange_and_commit(ws); 343 arrange_windows(ws);
264 } else { 344 } else {
265 arrange_and_commit(view->swayc->parent); 345 arrange_windows(view->swayc->parent);
266 } 346 }
347 transaction_commit_dirty();
267 348
268 xdg_shell_view->commit.notify = handle_commit; 349 xdg_shell_view->commit.notify = handle_commit;
269 wl_signal_add(&xdg_surface->surface->events.commit, 350 wl_signal_add(&xdg_surface->surface->events.commit,
@@ -276,6 +357,22 @@ static void handle_map(struct wl_listener *listener, void *data) {
276 xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; 357 xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen;
277 wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, 358 wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen,
278 &xdg_shell_view->request_fullscreen); 359 &xdg_shell_view->request_fullscreen);
360
361 xdg_shell_view->request_move.notify = handle_request_move;
362 wl_signal_add(&xdg_surface->toplevel->events.request_move,
363 &xdg_shell_view->request_move);
364
365 xdg_shell_view->request_resize.notify = handle_request_resize;
366 wl_signal_add(&xdg_surface->toplevel->events.request_resize,
367 &xdg_shell_view->request_resize);
368
369 xdg_shell_view->set_title.notify = handle_set_title;
370 wl_signal_add(&xdg_surface->toplevel->events.set_title,
371 &xdg_shell_view->set_title);
372
373 xdg_shell_view->set_app_id.notify = handle_set_app_id;
374 wl_signal_add(&xdg_surface->toplevel->events.set_app_id,
375 &xdg_shell_view->set_app_id);
279} 376}
280 377
281static void handle_destroy(struct wl_listener *listener, void *data) { 378static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -304,11 +401,11 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
304 struct wlr_xdg_surface *xdg_surface = data; 401 struct wlr_xdg_surface *xdg_surface = data;
305 402
306 if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { 403 if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
307 wlr_log(L_DEBUG, "New xdg_shell popup"); 404 wlr_log(WLR_DEBUG, "New xdg_shell popup");
308 return; 405 return;
309 } 406 }
310 407
311 wlr_log(L_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", 408 wlr_log(WLR_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'",
312 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); 409 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id);
313 wlr_xdg_surface_ping(xdg_surface); 410 wlr_xdg_surface_ping(xdg_surface);
314 411
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index b28c4b9c..46fd4769 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -1,4 +1,5 @@
1#define _POSIX_C_SOURCE 199309L 1#define _POSIX_C_SOURCE 199309L
2#include <float.h>
2#include <stdbool.h> 3#include <stdbool.h>
3#include <stdlib.h> 4#include <stdlib.h>
4#include <wayland-server.h> 5#include <wayland-server.h>
@@ -44,6 +45,24 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) {
44 view_child_destroy(&popup->child); 45 view_child_destroy(&popup->child);
45} 46}
46 47
48static void popup_unconstrain(struct sway_xdg_popup_v6 *popup) {
49 struct sway_view *view = popup->child.view;
50 struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup;
51
52 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
53
54 // the output box expressed in the coordinate system of the toplevel parent
55 // of the popup
56 struct wlr_box output_toplevel_sx_box = {
57 .x = output->x - view->x,
58 .y = output->y - view->y,
59 .width = output->width,
60 .height = output->height,
61 };
62
63 wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
64}
65
47static struct sway_xdg_popup_v6 *popup_create( 66static struct sway_xdg_popup_v6 *popup_create(
48 struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) { 67 struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) {
49 struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base; 68 struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base;
@@ -54,12 +73,15 @@ static struct sway_xdg_popup_v6 *popup_create(
54 return NULL; 73 return NULL;
55 } 74 }
56 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); 75 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
76 popup->wlr_xdg_surface_v6 = xdg_surface;
57 77
58 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); 78 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
59 popup->new_popup.notify = popup_handle_new_popup; 79 popup->new_popup.notify = popup_handle_new_popup;
60 wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); 80 wl_signal_add(&xdg_surface->events.destroy, &popup->destroy);
61 popup->destroy.notify = popup_handle_destroy; 81 popup->destroy.notify = popup_handle_destroy;
62 82
83 popup_unconstrain(popup);
84
63 return popup; 85 return popup;
64} 86}
65 87
@@ -73,6 +95,16 @@ static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view(
73 return (struct sway_xdg_shell_v6_view *)view; 95 return (struct sway_xdg_shell_v6_view *)view;
74} 96}
75 97
98static void get_constraints(struct sway_view *view, double *min_width,
99 double *max_width, double *min_height, double *max_height) {
100 struct wlr_xdg_toplevel_v6_state *state =
101 &view->wlr_xdg_surface_v6->toplevel->current;
102 *min_width = state->min_width > 0 ? state->min_width : DBL_MIN;
103 *max_width = state->max_width > 0 ? state->max_width : DBL_MAX;
104 *min_height = state->min_height > 0 ? state->min_height : DBL_MIN;
105 *max_height = state->max_height > 0 ? state->max_height : DBL_MAX;
106}
107
76static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { 108static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) {
77 if (xdg_shell_v6_view_from_view(view) == NULL) { 109 if (xdg_shell_v6_view_from_view(view) == NULL) {
78 return NULL; 110 return NULL;
@@ -163,6 +195,7 @@ static void destroy(struct sway_view *view) {
163} 195}
164 196
165static const struct sway_view_impl view_impl = { 197static const struct sway_view_impl view_impl = {
198 .get_constraints = get_constraints,
166 .get_string_prop = get_string_prop, 199 .get_string_prop = get_string_prop,
167 .configure = configure, 200 .configure = configure,
168 .set_activated = set_activated, 201 .set_activated = set_activated,
@@ -187,10 +220,24 @@ static void handle_commit(struct wl_listener *listener, void *data) {
187 transaction_notify_view_ready(view, xdg_surface_v6->configure_serial); 220 transaction_notify_view_ready(view, xdg_surface_v6->configure_serial);
188 } 221 }
189 222
190 view_update_title(view, false);
191 view_damage_from(view); 223 view_damage_from(view);
192} 224}
193 225
226static void handle_set_title(struct wl_listener *listener, void *data) {
227 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
228 wl_container_of(listener, xdg_shell_v6_view, set_title);
229 struct sway_view *view = &xdg_shell_v6_view->view;
230 view_update_title(view, false);
231 view_execute_criteria(view);
232}
233
234static void handle_set_app_id(struct wl_listener *listener, void *data) {
235 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
236 wl_container_of(listener, xdg_shell_v6_view, set_app_id);
237 struct sway_view *view = &xdg_shell_v6_view->view;
238 view_execute_criteria(view);
239}
240
194static void handle_new_popup(struct wl_listener *listener, void *data) { 241static void handle_new_popup(struct wl_listener *listener, void *data) {
195 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 242 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
196 wl_container_of(listener, xdg_shell_v6_view, new_popup); 243 wl_container_of(listener, xdg_shell_v6_view, new_popup);
@@ -217,8 +264,37 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
217 264
218 view_set_fullscreen(view, e->fullscreen); 265 view_set_fullscreen(view, e->fullscreen);
219 266
220 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 267 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
221 arrange_and_commit(ws); 268 arrange_windows(output);
269 transaction_commit_dirty();
270}
271
272static void handle_request_move(struct wl_listener *listener, void *data) {
273 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
274 wl_container_of(listener, xdg_shell_v6_view, request_move);
275 struct sway_view *view = &xdg_shell_v6_view->view;
276 if (!container_is_floating(view->swayc)) {
277 return;
278 }
279 struct wlr_xdg_toplevel_v6_move_event *e = data;
280 struct sway_seat *seat = e->seat->seat->data;
281 if (e->serial == seat->last_button_serial) {
282 seat_begin_move(seat, view->swayc, seat->last_button);
283 }
284}
285
286static void handle_request_resize(struct wl_listener *listener, void *data) {
287 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
288 wl_container_of(listener, xdg_shell_v6_view, request_resize);
289 struct sway_view *view = &xdg_shell_v6_view->view;
290 if (!container_is_floating(view->swayc)) {
291 return;
292 }
293 struct wlr_xdg_toplevel_v6_resize_event *e = data;
294 struct sway_seat *seat = e->seat->seat->data;
295 if (e->serial == seat->last_button_serial) {
296 seat_begin_resize(seat, view->swayc, seat->last_button, e->edges);
297 }
222} 298}
223 299
224static void handle_unmap(struct wl_listener *listener, void *data) { 300static void handle_unmap(struct wl_listener *listener, void *data) {
@@ -235,6 +311,10 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
235 wl_list_remove(&xdg_shell_v6_view->commit.link); 311 wl_list_remove(&xdg_shell_v6_view->commit.link);
236 wl_list_remove(&xdg_shell_v6_view->new_popup.link); 312 wl_list_remove(&xdg_shell_v6_view->new_popup.link);
237 wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); 313 wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link);
314 wl_list_remove(&xdg_shell_v6_view->request_move.link);
315 wl_list_remove(&xdg_shell_v6_view->request_resize.link);
316 wl_list_remove(&xdg_shell_v6_view->set_title.link);
317 wl_list_remove(&xdg_shell_v6_view->set_app_id.link);
238} 318}
239 319
240static void handle_map(struct wl_listener *listener, void *data) { 320static void handle_map(struct wl_listener *listener, void *data) {
@@ -246,8 +326,8 @@ static void handle_map(struct wl_listener *listener, void *data) {
246 view->natural_width = view->wlr_xdg_surface_v6->geometry.width; 326 view->natural_width = view->wlr_xdg_surface_v6->geometry.width;
247 view->natural_height = view->wlr_xdg_surface_v6->geometry.height; 327 view->natural_height = view->wlr_xdg_surface_v6->geometry.height;
248 if (!view->natural_width && !view->natural_height) { 328 if (!view->natural_width && !view->natural_height) {
249 view->natural_width = view->wlr_xdg_surface_v6->surface->current->width; 329 view->natural_width = view->wlr_xdg_surface_v6->surface->current.width;
250 view->natural_height = view->wlr_xdg_surface_v6->surface->current->height; 330 view->natural_height = view->wlr_xdg_surface_v6->surface->current.height;
251 } 331 }
252 332
253 view_map(view, view->wlr_xdg_surface_v6->surface); 333 view_map(view, view->wlr_xdg_surface_v6->surface);
@@ -255,10 +335,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
255 if (xdg_surface->toplevel->client_pending.fullscreen) { 335 if (xdg_surface->toplevel->client_pending.fullscreen) {
256 view_set_fullscreen(view, true); 336 view_set_fullscreen(view, true);
257 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 337 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
258 arrange_and_commit(ws); 338 arrange_windows(ws);
259 } else { 339 } else {
260 arrange_and_commit(view->swayc->parent); 340 arrange_windows(view->swayc->parent);
261 } 341 }
342 transaction_commit_dirty();
262 343
263 xdg_shell_v6_view->commit.notify = handle_commit; 344 xdg_shell_v6_view->commit.notify = handle_commit;
264 wl_signal_add(&xdg_surface->surface->events.commit, 345 wl_signal_add(&xdg_surface->surface->events.commit,
@@ -271,6 +352,22 @@ static void handle_map(struct wl_listener *listener, void *data) {
271 xdg_shell_v6_view->request_fullscreen.notify = handle_request_fullscreen; 352 xdg_shell_v6_view->request_fullscreen.notify = handle_request_fullscreen;
272 wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, 353 wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen,
273 &xdg_shell_v6_view->request_fullscreen); 354 &xdg_shell_v6_view->request_fullscreen);
355
356 xdg_shell_v6_view->request_move.notify = handle_request_move;
357 wl_signal_add(&xdg_surface->toplevel->events.request_move,
358 &xdg_shell_v6_view->request_move);
359
360 xdg_shell_v6_view->request_resize.notify = handle_request_resize;
361 wl_signal_add(&xdg_surface->toplevel->events.request_resize,
362 &xdg_shell_v6_view->request_resize);
363
364 xdg_shell_v6_view->set_title.notify = handle_set_title;
365 wl_signal_add(&xdg_surface->toplevel->events.set_title,
366 &xdg_shell_v6_view->set_title);
367
368 xdg_shell_v6_view->set_app_id.notify = handle_set_app_id;
369 wl_signal_add(&xdg_surface->toplevel->events.set_app_id,
370 &xdg_shell_v6_view->set_app_id);
274} 371}
275 372
276static void handle_destroy(struct wl_listener *listener, void *data) { 373static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -295,11 +392,11 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
295 struct wlr_xdg_surface_v6 *xdg_surface = data; 392 struct wlr_xdg_surface_v6 *xdg_surface = data;
296 393
297 if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { 394 if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) {
298 wlr_log(L_DEBUG, "New xdg_shell_v6 popup"); 395 wlr_log(WLR_DEBUG, "New xdg_shell_v6 popup");
299 return; 396 return;
300 } 397 }
301 398
302 wlr_log(L_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'", 399 wlr_log(WLR_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'",
303 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); 400 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id);
304 wlr_xdg_surface_v6_ping(xdg_surface); 401 wlr_xdg_surface_v6_ping(xdg_surface);
305 402
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index b3b1473d..65d4fcd4 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -69,16 +69,11 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
69 surface->ly = xsurface->y; 69 surface->ly = xsurface->y;
70 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); 70 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true);
71 71
72 if (!wlr_xwayland_surface_is_unmanaged(xsurface)) { 72 struct sway_seat *seat = input_manager_current_seat(input_manager);
73 struct sway_seat *seat = input_manager_current_seat(input_manager); 73 struct wlr_xwayland *xwayland =
74 struct wlr_xwayland *xwayland = 74 seat->input->server->xwayland.wlr_xwayland;
75 seat->input->server->xwayland.wlr_xwayland; 75 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
76 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 76 seat_set_focus_surface(seat, xsurface->surface, false);
77 seat_set_focus_surface(seat, xsurface->surface);
78 }
79
80 // TODO: we don't send surface enter/leave events to xwayland unmanaged
81 // surfaces, but xwayland doesn't support HiDPI anyway
82} 77}
83 78
84static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { 79static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
@@ -89,18 +84,16 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
89 wl_list_remove(&surface->link); 84 wl_list_remove(&surface->link);
90 wl_list_remove(&surface->commit.link); 85 wl_list_remove(&surface->commit.link);
91 86
92 if (!wlr_xwayland_surface_is_unmanaged(xsurface)) { 87 struct sway_seat *seat = input_manager_current_seat(input_manager);
93 struct sway_seat *seat = input_manager_current_seat(input_manager); 88 if (seat->wlr_seat->keyboard_state.focused_surface ==
94 if (seat->wlr_seat->keyboard_state.focused_surface == 89 xsurface->surface) {
95 xsurface->surface) { 90 // Restore focus
96 // Restore focus 91 struct sway_container *previous =
97 struct sway_container *previous = 92 seat_get_focus_inactive(seat, &root_container);
98 seat_get_focus_inactive(seat, &root_container); 93 if (previous) {
99 if (previous) { 94 // Hack to get seat to re-focus the return value of get_focus
100 // Hack to get seat to re-focus the return value of get_focus 95 seat_set_focus(seat, previous->parent);
101 seat_set_focus(seat, previous->parent); 96 seat_set_focus(seat, previous);
102 seat_set_focus(seat, previous);
103 }
104 } 97 }
105 } 98 }
106} 99}
@@ -119,7 +112,7 @@ static struct sway_xwayland_unmanaged *create_unmanaged(
119 struct sway_xwayland_unmanaged *surface = 112 struct sway_xwayland_unmanaged *surface =
120 calloc(1, sizeof(struct sway_xwayland_unmanaged)); 113 calloc(1, sizeof(struct sway_xwayland_unmanaged));
121 if (surface == NULL) { 114 if (surface == NULL) {
122 wlr_log(L_ERROR, "Allocation failed"); 115 wlr_log(WLR_ERROR, "Allocation failed");
123 return NULL; 116 return NULL;
124 } 117 }
125 118
@@ -246,6 +239,14 @@ static bool wants_floating(struct sway_view *view) {
246 return false; 239 return false;
247} 240}
248 241
242static bool has_client_side_decorations(struct sway_view *view) {
243 if (xwayland_view_from_view(view) == NULL) {
244 return false;
245 }
246 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
247 return surface->decorations != WLR_XWAYLAND_SURFACE_DECORATIONS_ALL;
248}
249
249static void _close(struct sway_view *view) { 250static void _close(struct sway_view *view) {
250 if (xwayland_view_from_view(view) == NULL) { 251 if (xwayland_view_from_view(view) == NULL) {
251 return; 252 return;
@@ -269,6 +270,7 @@ static const struct sway_view_impl view_impl = {
269 .set_tiled = set_tiled, 270 .set_tiled = set_tiled,
270 .set_fullscreen = set_fullscreen, 271 .set_fullscreen = set_fullscreen,
271 .wants_floating = wants_floating, 272 .wants_floating = wants_floating,
273 .has_client_side_decorations = has_client_side_decorations,
272 .close = _close, 274 .close = _close,
273 .destroy = destroy, 275 .destroy = destroy,
274}; 276};
@@ -278,15 +280,42 @@ static void handle_commit(struct wl_listener *listener, void *data) {
278 wl_container_of(listener, xwayland_view, commit); 280 wl_container_of(listener, xwayland_view, commit);
279 struct sway_view *view = &xwayland_view->view; 281 struct sway_view *view = &xwayland_view->view;
280 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 282 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
281 struct wlr_surface_state *surface_state = xsurface->surface->current; 283 struct wlr_surface_state *surface_state = &xsurface->surface->current;
282 284
283 if (view->swayc->instructions->length) { 285 if (view->swayc->instructions->length) {
284 transaction_notify_view_ready_by_size(view, 286 transaction_notify_view_ready_by_size(view,
285 surface_state->width, surface_state->height); 287 surface_state->width, surface_state->height);
288 } else if (container_is_floating(view->swayc)) {
289 view_update_size(view, surface_state->width, surface_state->height);
286 } 290 }
291
287 view_damage_from(view); 292 view_damage_from(view);
288} 293}
289 294
295static void handle_destroy(struct wl_listener *listener, void *data) {
296 struct sway_xwayland_view *xwayland_view =
297 wl_container_of(listener, xwayland_view, destroy);
298 struct sway_view *view = &xwayland_view->view;
299
300 if (view->surface) {
301 view_unmap(view);
302 wl_list_remove(&xwayland_view->commit.link);
303 }
304
305 wl_list_remove(&xwayland_view->destroy.link);
306 wl_list_remove(&xwayland_view->request_configure.link);
307 wl_list_remove(&xwayland_view->request_fullscreen.link);
308 wl_list_remove(&xwayland_view->request_move.link);
309 wl_list_remove(&xwayland_view->request_resize.link);
310 wl_list_remove(&xwayland_view->set_title.link);
311 wl_list_remove(&xwayland_view->set_class.link);
312 wl_list_remove(&xwayland_view->set_window_type.link);
313 wl_list_remove(&xwayland_view->set_hints.link);
314 wl_list_remove(&xwayland_view->map.link);
315 wl_list_remove(&xwayland_view->unmap.link);
316 view_destroy(&xwayland_view->view);
317}
318
290static void handle_unmap(struct wl_listener *listener, void *data) { 319static void handle_unmap(struct wl_listener *listener, void *data) {
291 struct sway_xwayland_view *xwayland_view = 320 struct sway_xwayland_view *xwayland_view =
292 wl_container_of(listener, xwayland_view, unmap); 321 wl_container_of(listener, xwayland_view, unmap);
@@ -307,6 +336,15 @@ static void handle_map(struct wl_listener *listener, void *data) {
307 struct wlr_xwayland_surface *xsurface = data; 336 struct wlr_xwayland_surface *xsurface = data;
308 struct sway_view *view = &xwayland_view->view; 337 struct sway_view *view = &xwayland_view->view;
309 338
339 if (xsurface->override_redirect) {
340 // This window used not to have the override redirect flag and has it
341 // now. Switch to unmanaged.
342 handle_destroy(&xwayland_view->destroy, view);
343 struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface);
344 unmanaged_handle_map(&unmanaged->map, xsurface);
345 return;
346 }
347
310 view->natural_width = xsurface->width; 348 view->natural_width = xsurface->width;
311 view->natural_height = xsurface->height; 349 view->natural_height = xsurface->height;
312 350
@@ -321,31 +359,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
321 if (xsurface->fullscreen) { 359 if (xsurface->fullscreen) {
322 view_set_fullscreen(view, true); 360 view_set_fullscreen(view, true);
323 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 361 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
324 arrange_and_commit(ws); 362 arrange_windows(ws);
325 } else { 363 } else {
326 arrange_and_commit(view->swayc->parent); 364 arrange_windows(view->swayc->parent);
327 }
328}
329
330static void handle_destroy(struct wl_listener *listener, void *data) {
331 struct sway_xwayland_view *xwayland_view =
332 wl_container_of(listener, xwayland_view, destroy);
333 struct sway_view *view = &xwayland_view->view;
334
335 if (view->surface) {
336 view_unmap(view);
337 wl_list_remove(&xwayland_view->commit.link);
338 } 365 }
339 366 transaction_commit_dirty();
340 wl_list_remove(&xwayland_view->destroy.link);
341 wl_list_remove(&xwayland_view->request_configure.link);
342 wl_list_remove(&xwayland_view->request_fullscreen.link);
343 wl_list_remove(&xwayland_view->set_title.link);
344 wl_list_remove(&xwayland_view->set_class.link);
345 wl_list_remove(&xwayland_view->set_window_type.link);
346 wl_list_remove(&xwayland_view->map.link);
347 wl_list_remove(&xwayland_view->unmap.link);
348 view_destroy(&xwayland_view->view);
349} 367}
350 368
351static void handle_request_configure(struct wl_listener *listener, void *data) { 369static void handle_request_configure(struct wl_listener *listener, void *data) {
@@ -379,8 +397,40 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
379 } 397 }
380 view_set_fullscreen(view, xsurface->fullscreen); 398 view_set_fullscreen(view, xsurface->fullscreen);
381 399
382 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 400 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
383 arrange_and_commit(ws); 401 arrange_windows(output);
402 transaction_commit_dirty();
403}
404
405static void handle_request_move(struct wl_listener *listener, void *data) {
406 struct sway_xwayland_view *xwayland_view =
407 wl_container_of(listener, xwayland_view, request_move);
408 struct sway_view *view = &xwayland_view->view;
409 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
410 if (!xsurface->mapped) {
411 return;
412 }
413 if (!container_is_floating(view->swayc)) {
414 return;
415 }
416 struct sway_seat *seat = input_manager_current_seat(input_manager);
417 seat_begin_move(seat, view->swayc, seat->last_button);
418}
419
420static void handle_request_resize(struct wl_listener *listener, void *data) {
421 struct sway_xwayland_view *xwayland_view =
422 wl_container_of(listener, xwayland_view, request_resize);
423 struct sway_view *view = &xwayland_view->view;
424 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
425 if (!xsurface->mapped) {
426 return;
427 }
428 if (!container_is_floating(view->swayc)) {
429 return;
430 }
431 struct wlr_xwayland_resize_event *e = data;
432 struct sway_seat *seat = input_manager_current_seat(input_manager);
433 seat_begin_resize(seat, view->swayc, seat->last_button, e->edges);
384} 434}
385 435
386static void handle_set_title(struct wl_listener *listener, void *data) { 436static void handle_set_title(struct wl_listener *listener, void *data) {
@@ -417,6 +467,25 @@ static void handle_set_window_type(struct wl_listener *listener, void *data) {
417 view_execute_criteria(view); 467 view_execute_criteria(view);
418} 468}
419 469
470static void handle_set_hints(struct wl_listener *listener, void *data) {
471 struct sway_xwayland_view *xwayland_view =
472 wl_container_of(listener, xwayland_view, set_hints);
473 struct sway_view *view = &xwayland_view->view;
474 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
475 if (!xsurface->mapped) {
476 return;
477 }
478 if (!xsurface->hints_urgency && view->urgent_timer) {
479 // The view is is in the timeout period. We'll ignore the request to
480 // unset urgency so that the view remains urgent until the timer clears
481 // it.
482 return;
483 }
484 if (view->allow_request_urgent) {
485 view_set_urgent(view, (bool)xsurface->hints_urgency);
486 }
487}
488
420struct sway_view *view_from_wlr_xwayland_surface( 489struct sway_view *view_from_wlr_xwayland_surface(
421 struct wlr_xwayland_surface *xsurface) { 490 struct wlr_xwayland_surface *xsurface) {
422 return xsurface->data; 491 return xsurface->data;
@@ -427,14 +496,13 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
427 xwayland_surface); 496 xwayland_surface);
428 struct wlr_xwayland_surface *xsurface = data; 497 struct wlr_xwayland_surface *xsurface = data;
429 498
430 if (wlr_xwayland_surface_is_unmanaged(xsurface) || 499 if (xsurface->override_redirect) {
431 xsurface->override_redirect) { 500 wlr_log(WLR_DEBUG, "New xwayland unmanaged surface");
432 wlr_log(L_DEBUG, "New xwayland unmanaged surface");
433 create_unmanaged(xsurface); 501 create_unmanaged(xsurface);
434 return; 502 return;
435 } 503 }
436 504
437 wlr_log(L_DEBUG, "New xwayland surface title='%s' class='%s'", 505 wlr_log(WLR_DEBUG, "New xwayland surface title='%s' class='%s'",
438 xsurface->title, xsurface->class); 506 xsurface->title, xsurface->class);
439 507
440 struct sway_xwayland_view *xwayland_view = 508 struct sway_xwayland_view *xwayland_view =
@@ -457,6 +525,14 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
457 &xwayland_view->request_fullscreen); 525 &xwayland_view->request_fullscreen);
458 xwayland_view->request_fullscreen.notify = handle_request_fullscreen; 526 xwayland_view->request_fullscreen.notify = handle_request_fullscreen;
459 527
528 wl_signal_add(&xsurface->events.request_move,
529 &xwayland_view->request_move);
530 xwayland_view->request_move.notify = handle_request_move;
531
532 wl_signal_add(&xsurface->events.request_resize,
533 &xwayland_view->request_resize);
534 xwayland_view->request_resize.notify = handle_request_resize;
535
460 wl_signal_add(&xsurface->events.set_title, &xwayland_view->set_title); 536 wl_signal_add(&xsurface->events.set_title, &xwayland_view->set_title);
461 xwayland_view->set_title.notify = handle_set_title; 537 xwayland_view->set_title.notify = handle_set_title;
462 538
@@ -467,6 +543,9 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
467 &xwayland_view->set_window_type); 543 &xwayland_view->set_window_type);
468 xwayland_view->set_window_type.notify = handle_set_window_type; 544 xwayland_view->set_window_type.notify = handle_set_window_type;
469 545
546 wl_signal_add(&xsurface->events.set_hints, &xwayland_view->set_hints);
547 xwayland_view->set_hints.notify = handle_set_hints;
548
470 wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); 549 wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap);
471 xwayland_view->unmap.notify = handle_unmap; 550 xwayland_view->unmap.notify = handle_unmap;
472 551
@@ -484,7 +563,7 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) {
484 xcb_connection_t *xcb_conn = xcb_connect(NULL, NULL); 563 xcb_connection_t *xcb_conn = xcb_connect(NULL, NULL);
485 int err = xcb_connection_has_error(xcb_conn); 564 int err = xcb_connection_has_error(xcb_conn);
486 if (err) { 565 if (err) {
487 wlr_log(L_ERROR, "XCB connect failed: %d", err); 566 wlr_log(WLR_ERROR, "XCB connect failed: %d", err);
488 return; 567 return;
489 } 568 }
490 569
@@ -503,7 +582,7 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) {
503 free(reply); 582 free(reply);
504 583
505 if (error != NULL) { 584 if (error != NULL) {
506 wlr_log(L_ERROR, "could not resolve atom %s, X11 error code %d", 585 wlr_log(WLR_ERROR, "could not resolve atom %s, X11 error code %d",
507 atom_map[i], error->error_code); 586 atom_map[i], error->error_code);
508 free(error); 587 free(error);
509 break; 588 break;