aboutsummaryrefslogtreecommitdiffstats
path: root/sway/desktop/output.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/desktop/output.c')
-rw-r--r--sway/desktop/output.c579
1 files changed, 579 insertions, 0 deletions
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
new file mode 100644
index 00000000..1b3143d0
--- /dev/null
+++ b/sway/desktop/output.c
@@ -0,0 +1,579 @@
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_matrix.h>
10#include <wlr/types/wlr_output_damage.h>
11#include <wlr/types/wlr_output_layout.h>
12#include <wlr/types/wlr_output.h>
13#include <wlr/types/wlr_surface.h>
14#include <wlr/types/wlr_wl_shell.h>
15#include <wlr/util/region.h>
16#include "log.h"
17#include "sway/input/input-manager.h"
18#include "sway/input/seat.h"
19#include "sway/layers.h"
20#include "sway/output.h"
21#include "sway/server.h"
22#include "sway/tree/container.h"
23#include "sway/tree/layout.h"
24#include "sway/tree/view.h"
25
26struct sway_container *output_by_name(const char *name) {
27 for (int i = 0; i < root_container.children->length; ++i) {
28 struct sway_container *output = root_container.children->items[i];
29 if (strcasecmp(output->name, name) == 0) {
30 return output;
31 }
32 }
33 return NULL;
34}
35
36/**
37 * Rotate a child's position relative to a parent. The parent size is (pw, ph),
38 * the child position is (*sx, *sy) and its size is (sw, sh).
39 */
40static void rotate_child_position(double *sx, double *sy, double sw, double sh,
41 double pw, double ph, float rotation) {
42 if (rotation == 0.0f) {
43 return;
44 }
45
46 // Coordinates relative to the center of the subsurface
47 double ox = *sx - pw/2 + sw/2,
48 oy = *sy - ph/2 + sh/2;
49 // Rotated coordinates
50 double rx = cos(-rotation)*ox - sin(-rotation)*oy,
51 ry = cos(-rotation)*oy + sin(-rotation)*ox;
52 *sx = rx + pw/2 - sw/2;
53 *sy = ry + ph/2 - sh/2;
54}
55
56/**
57 * Contains a surface's root geometry information. For instance, when rendering
58 * a popup, this will contain the parent view's position and size.
59 */
60struct root_geometry {
61 double x, y;
62 int width, height;
63 float rotation;
64};
65
66static bool get_surface_box(struct root_geometry *geo,
67 struct sway_output *output, struct wlr_surface *surface, int sx, int sy,
68 struct wlr_box *surface_box) {
69 if (!wlr_surface_has_buffer(surface)) {
70 return false;
71 }
72
73 int sw = surface->current->width;
74 int sh = surface->current->height;
75
76 double _sx = sx, _sy = sy;
77 rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height,
78 geo->rotation);
79
80 struct wlr_box box = {
81 .x = geo->x + _sx,
82 .y = geo->y + _sy,
83 .width = sw,
84 .height = sh,
85 };
86 if (surface_box != NULL) {
87 memcpy(surface_box, &box, sizeof(struct wlr_box));
88 }
89
90 struct wlr_box rotated_box;
91 wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box);
92
93 struct wlr_box output_box = {
94 .width = output->swayc->width,
95 .height = output->swayc->height,
96 };
97
98 struct wlr_box intersection;
99 return wlr_box_intersection(&output_box, &rotated_box, &intersection);
100}
101
102static void surface_for_each_surface(struct wlr_surface *surface,
103 double ox, double oy, struct root_geometry *geo,
104 wlr_surface_iterator_func_t iterator, void *user_data) {
105 geo->x = ox;
106 geo->y = oy;
107 geo->width = surface->current->width;
108 geo->height = surface->current->height;
109 geo->rotation = 0;
110
111 wlr_surface_for_each_surface(surface, iterator, user_data);
112}
113
114static void output_view_for_each_surface(struct sway_view *view,
115 struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
116 void *user_data) {
117 geo->x = view->swayc->x;
118 geo->y = view->swayc->y;
119 geo->width = view->surface->current->width;
120 geo->height = view->surface->current->height;
121 geo->rotation = 0; // TODO
122
123 view_for_each_surface(view, iterator, user_data);
124}
125
126static void layer_for_each_surface(struct wl_list *layer_surfaces,
127 struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
128 void *user_data) {
129 struct sway_layer_surface *layer_surface;
130 wl_list_for_each(layer_surface, layer_surfaces, link) {
131 struct wlr_layer_surface *wlr_layer_surface =
132 layer_surface->layer_surface;
133 surface_for_each_surface(wlr_layer_surface->surface,
134 layer_surface->geo.x, layer_surface->geo.y, geo, iterator,
135 user_data);
136 }
137}
138
139static void unmanaged_for_each_surface(struct wl_list *unmanaged,
140 struct sway_output *output, struct root_geometry *geo,
141 wlr_surface_iterator_func_t iterator, void *user_data) {
142 struct sway_xwayland_unmanaged *unmanaged_surface;
143 wl_list_for_each(unmanaged_surface, unmanaged, link) {
144 struct wlr_xwayland_surface *xsurface =
145 unmanaged_surface->wlr_xwayland_surface;
146 double ox = unmanaged_surface->lx - output->swayc->x;
147 double oy = unmanaged_surface->ly - output->swayc->y;
148
149 surface_for_each_surface(xsurface->surface, ox, oy, geo,
150 iterator, user_data);
151 }
152}
153
154static void scale_box(struct wlr_box *box, float scale) {
155 box->x *= scale;
156 box->y *= scale;
157 box->width *= scale;
158 box->height *= scale;
159}
160
161struct render_data {
162 struct root_geometry root_geo;
163 struct sway_output *output;
164 float alpha;
165};
166
167static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy,
168 void *_data) {
169 struct render_data *data = _data;
170 struct wlr_output *wlr_output = data->output->wlr_output;
171 float rotation = data->root_geo.rotation;
172 float alpha = data->alpha;
173
174 if (!wlr_surface_has_buffer(surface)) {
175 return;
176 }
177
178 struct wlr_box box;
179 bool intersects = get_surface_box(&data->root_geo, data->output, surface,
180 sx, sy, &box);
181 if (!intersects) {
182 return;
183 }
184
185 struct wlr_renderer *renderer =
186 wlr_backend_get_renderer(wlr_output->backend);
187 if (!sway_assert(renderer != NULL,
188 "expected the output backend to have a renderer")) {
189 return;
190 }
191
192 scale_box(&box, wlr_output->scale);
193
194 float matrix[9];
195 enum wl_output_transform transform =
196 wlr_output_transform_invert(surface->current->transform);
197 wlr_matrix_project_box(matrix, &box, transform, rotation,
198 wlr_output->transform_matrix);
199
200 wlr_render_texture_with_matrix(renderer, surface->texture,
201 matrix, alpha);
202}
203
204static void render_layer(struct sway_output *output,
205 struct wl_list *layer_surfaces) {
206 struct render_data data = { .output = output, .alpha = 1.0f };
207 layer_for_each_surface(layer_surfaces, &data.root_geo,
208 render_surface_iterator, &data);
209}
210
211static void render_unmanaged(struct sway_output *output,
212 struct wl_list *unmanaged) {
213 struct render_data data = { .output = output, .alpha = 1.0f };
214 unmanaged_for_each_surface(unmanaged, output, &data.root_geo,
215 render_surface_iterator, &data);
216}
217
218static void render_container_iterator(struct sway_container *con,
219 void *_data) {
220 struct sway_output *output = _data;
221 if (!sway_assert(con->type == C_VIEW, "expected a view")) {
222 return;
223 }
224 struct render_data data = { .output = output, .alpha = con->alpha };
225 output_view_for_each_surface(con->sway_view, &data.root_geo,
226 render_surface_iterator, &data);
227}
228
229static void render_container(struct sway_output *output,
230 struct sway_container *con) {
231 container_descendants(con, C_VIEW, render_container_iterator, output);
232}
233
234static struct sway_container *output_get_active_workspace(
235 struct sway_output *output) {
236 struct sway_seat *seat = input_manager_current_seat(input_manager);
237 struct sway_container *focus =
238 seat_get_focus_inactive(seat, output->swayc);
239 if (!focus) {
240 // We've never been to this output before
241 focus = output->swayc->children->items[0];
242 }
243 struct sway_container *workspace = focus;
244 if (workspace->type != C_WORKSPACE) {
245 workspace = container_parent(workspace, C_WORKSPACE);
246 }
247 return workspace;
248}
249
250static void render_output(struct sway_output *output, struct timespec *when,
251 pixman_region32_t *damage) {
252 struct wlr_output *wlr_output = output->wlr_output;
253
254 struct wlr_renderer *renderer =
255 wlr_backend_get_renderer(wlr_output->backend);
256 if (!sway_assert(renderer != NULL,
257 "expected the output backend to have a renderer")) {
258 return;
259 }
260
261 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
262
263 if (!pixman_region32_not_empty(damage)) {
264 // Output isn't damaged but needs buffer swap
265 goto renderer_end;
266 }
267
268 // TODO: don't damage the whole output
269 int width, height;
270 wlr_output_transformed_resolution(wlr_output, &width, &height);
271 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
272
273 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
274 wlr_renderer_clear(renderer, clear_color);
275
276 render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
277 render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
278
279 struct sway_container *workspace = output_get_active_workspace(output);
280 render_container(output, workspace);
281
282 render_unmanaged(output, &root_container.sway_root->xwayland_unmanaged);
283
284 // TODO: consider revising this when fullscreen windows are supported
285 render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
286 render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
287
288renderer_end:
289 if (root_container.sway_root->debug_tree) {
290 wlr_render_texture(renderer, root_container.sway_root->debug_tree,
291 wlr_output->transform_matrix, 0, 0, 1);
292 }
293
294 wlr_renderer_end(renderer);
295 if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) {
296 return;
297 }
298 output->last_frame = *when;
299}
300
301struct send_frame_done_data {
302 struct root_geometry root_geo;
303 struct sway_output *output;
304 struct timespec *when;
305};
306
307static void send_frame_done_iterator(struct wlr_surface *surface,
308 int sx, int sy, void *_data) {
309 struct send_frame_done_data *data = _data;
310
311 bool intersects = get_surface_box(&data->root_geo, data->output, surface,
312 sx, sy, NULL);
313 if (intersects) {
314 wlr_surface_send_frame_done(surface, data->when);
315 }
316}
317
318static void send_frame_done_layer(struct send_frame_done_data *data,
319 struct wl_list *layer_surfaces) {
320 layer_for_each_surface(layer_surfaces, &data->root_geo,
321 send_frame_done_iterator, data);
322}
323
324static void send_frame_done_unmanaged(struct send_frame_done_data *data,
325 struct wl_list *unmanaged) {
326 unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo,
327 send_frame_done_iterator, data);
328}
329
330static void send_frame_done_container_iterator(struct sway_container *con,
331 void *_data) {
332 struct send_frame_done_data *data = _data;
333 if (!sway_assert(con->type == C_VIEW, "expected a view")) {
334 return;
335 }
336 output_view_for_each_surface(con->sway_view, &data->root_geo,
337 send_frame_done_iterator, data);
338}
339
340static void send_frame_done_container(struct send_frame_done_data *data,
341 struct sway_container *con) {
342 container_descendants(con, C_VIEW,
343 send_frame_done_container_iterator, data);
344}
345
346static void send_frame_done(struct sway_output *output, struct timespec *when) {
347 struct send_frame_done_data data = {
348 .output = output,
349 .when = when,
350 };
351
352 send_frame_done_layer(&data,
353 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
354 send_frame_done_layer(&data,
355 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
356
357 struct sway_container *workspace = output_get_active_workspace(output);
358 send_frame_done_container(&data, workspace);
359
360 send_frame_done_unmanaged(&data,
361 &root_container.sway_root->xwayland_unmanaged);
362
363 send_frame_done_layer(&data,
364 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
365 send_frame_done_layer(&data,
366 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
367}
368
369static void damage_handle_frame(struct wl_listener *listener, void *data) {
370 struct sway_output *output =
371 wl_container_of(listener, output, damage_frame);
372
373 if (!output->wlr_output->enabled) {
374 return;
375 }
376
377 struct timespec now;
378 clock_gettime(CLOCK_MONOTONIC, &now);
379
380 bool needs_swap;
381 pixman_region32_t damage;
382 pixman_region32_init(&damage);
383 if (!wlr_output_damage_make_current(output->damage, &needs_swap, &damage)) {
384 return;
385 }
386
387 if (needs_swap) {
388 render_output(output, &now, &damage);
389 }
390
391 pixman_region32_fini(&damage);
392
393 // Send frame done to all visible surfaces
394 send_frame_done(output, &now);
395}
396
397void output_damage_whole(struct sway_output *output) {
398 wlr_output_damage_add_whole(output->damage);
399}
400
401struct damage_data {
402 struct root_geometry root_geo;
403 struct sway_output *output;
404 bool whole;
405};
406
407static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy,
408 void *_data) {
409 struct damage_data *data = _data;
410 struct sway_output *output = data->output;
411 float rotation = data->root_geo.rotation;
412 bool whole = data->whole;
413
414 struct wlr_box box;
415 bool intersects = get_surface_box(&data->root_geo, data->output, surface,
416 sx, sy, &box);
417 if (!intersects) {
418 return;
419 }
420
421 scale_box(&box, output->wlr_output->scale);
422
423 if (whole) {
424 wlr_box_rotated_bounds(&box, rotation, &box);
425 wlr_output_damage_add_box(output->damage, &box);
426 } else {
427 int center_x = box.x + box.width/2;
428 int center_y = box.y + box.height/2;
429
430 pixman_region32_t damage;
431 pixman_region32_init(&damage);
432 pixman_region32_copy(&damage, &surface->current->surface_damage);
433 wlr_region_scale(&damage, &damage, output->wlr_output->scale);
434 if (ceil(output->wlr_output->scale) > surface->current->scale) {
435 // When scaling up a surface, it'll become blurry so we need to
436 // expand the damage region
437 wlr_region_expand(&damage, &damage,
438 ceil(output->wlr_output->scale) - surface->current->scale);
439 }
440 pixman_region32_translate(&damage, box.x, box.y);
441 wlr_region_rotated_bounds(&damage, &damage, rotation,
442 center_x, center_y);
443 wlr_output_damage_add(output->damage, &damage);
444 pixman_region32_fini(&damage);
445 }
446}
447
448void output_damage_surface(struct sway_output *output, double ox, double oy,
449 struct wlr_surface *surface, bool whole) {
450 struct damage_data data = {
451 .output = output,
452 .whole = whole,
453 };
454
455 surface_for_each_surface(surface, ox, oy, &data.root_geo,
456 damage_surface_iterator, &data);
457}
458
459void output_damage_view(struct sway_output *output, struct sway_view *view,
460 bool whole) {
461 if (!sway_assert(view->swayc != NULL, "expected a view in the tree")) {
462 return;
463 }
464
465 struct damage_data data = {
466 .output = output,
467 .whole = whole,
468 };
469
470 output_view_for_each_surface(view, &data.root_geo,
471 damage_surface_iterator, &data);
472}
473
474static void output_damage_whole_container_iterator(struct sway_container *con,
475 void *data) {
476 struct sway_output *output = data;
477
478 if (!sway_assert(con->type == C_VIEW, "expected a view")) {
479 return;
480 }
481
482 output_damage_view(output, con->sway_view, true);
483}
484
485void output_damage_whole_container(struct sway_output *output,
486 struct sway_container *con) {
487 float scale = output->wlr_output->scale;
488 struct wlr_box box = {
489 .x = con->x * scale,
490 .y = con->y * scale,
491 .width = con->width * scale,
492 .height = con->height * scale,
493 };
494 wlr_output_damage_add_box(output->damage, &box);
495
496 container_descendants(con, C_VIEW, output_damage_whole_container_iterator,
497 output);
498}
499
500static void damage_handle_destroy(struct wl_listener *listener, void *data) {
501 struct sway_output *output =
502 wl_container_of(listener, output, damage_destroy);
503 container_destroy(output->swayc);
504}
505
506static void handle_destroy(struct wl_listener *listener, void *data) {
507 struct sway_output *output = wl_container_of(listener, output, destroy);
508 container_destroy(output->swayc);
509}
510
511static void handle_mode(struct wl_listener *listener, void *data) {
512 struct sway_output *output = wl_container_of(listener, output, mode);
513 arrange_layers(output);
514 arrange_windows(output->swayc, -1, -1);
515}
516
517static void handle_transform(struct wl_listener *listener, void *data) {
518 struct sway_output *output = wl_container_of(listener, output, transform);
519 arrange_layers(output);
520 arrange_windows(output->swayc, -1, -1);
521}
522
523static void handle_scale(struct wl_listener *listener, void *data) {
524 struct sway_output *output = wl_container_of(listener, output, scale);
525 arrange_layers(output);
526 arrange_windows(output->swayc, -1, -1);
527}
528
529void handle_new_output(struct wl_listener *listener, void *data) {
530 struct sway_server *server = wl_container_of(listener, server, new_output);
531 struct wlr_output *wlr_output = data;
532 wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name);
533
534 struct sway_output *output = calloc(1, sizeof(struct sway_output));
535 if (!output) {
536 return;
537 }
538 output->wlr_output = wlr_output;
539 wlr_output->data = output;
540 output->server = server;
541
542 if (!wl_list_empty(&wlr_output->modes)) {
543 struct wlr_output_mode *mode =
544 wl_container_of(wlr_output->modes.prev, mode, link);
545 wlr_output_set_mode(wlr_output, mode);
546 }
547
548 output->damage = wlr_output_damage_create(wlr_output);
549
550 output->swayc = output_create(output);
551 if (!output->swayc) {
552 free(output);
553 return;
554 }
555
556 size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
557 for (size_t i = 0; i < len; ++i) {
558 wl_list_init(&output->layers[i]);
559 }
560
561 input_manager_configure_xcursor(input_manager);
562
563 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
564 output->destroy.notify = handle_destroy;
565 wl_signal_add(&wlr_output->events.mode, &output->mode);
566 output->mode.notify = handle_mode;
567 wl_signal_add(&wlr_output->events.transform, &output->transform);
568 output->transform.notify = handle_transform;
569 wl_signal_add(&wlr_output->events.scale, &output->scale);
570 output->scale.notify = handle_scale;
571
572 wl_signal_add(&output->damage->events.frame, &output->damage_frame);
573 output->damage_frame.notify = damage_handle_frame;
574 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
575 output->damage_destroy.notify = damage_handle_destroy;
576
577 arrange_layers(output);
578 arrange_windows(&root_container, -1, -1);
579}