diff options
Diffstat (limited to 'sway/desktop/output.c')
-rw-r--r-- | sway/desktop/output.c | 579 |
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 | |||
26 | struct 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 | */ | ||
40 | static 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 | */ | ||
60 | struct root_geometry { | ||
61 | double x, y; | ||
62 | int width, height; | ||
63 | float rotation; | ||
64 | }; | ||
65 | |||
66 | static 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 | |||
102 | static 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 | |||
114 | static 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 | |||
126 | static 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 | |||
139 | static 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 | |||
154 | static 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 | |||
161 | struct render_data { | ||
162 | struct root_geometry root_geo; | ||
163 | struct sway_output *output; | ||
164 | float alpha; | ||
165 | }; | ||
166 | |||
167 | static 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 | |||
204 | static 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 | |||
211 | static 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 | |||
218 | static 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 | |||
229 | static void render_container(struct sway_output *output, | ||
230 | struct sway_container *con) { | ||
231 | container_descendants(con, C_VIEW, render_container_iterator, output); | ||
232 | } | ||
233 | |||
234 | static 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 | |||
250 | static 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 | |||
288 | renderer_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 | |||
301 | struct send_frame_done_data { | ||
302 | struct root_geometry root_geo; | ||
303 | struct sway_output *output; | ||
304 | struct timespec *when; | ||
305 | }; | ||
306 | |||
307 | static 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 | |||
318 | static 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 | |||
324 | static 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 | |||
330 | static 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 | |||
340 | static 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 | |||
346 | static 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 | |||
369 | static 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 | |||
397 | void output_damage_whole(struct sway_output *output) { | ||
398 | wlr_output_damage_add_whole(output->damage); | ||
399 | } | ||
400 | |||
401 | struct damage_data { | ||
402 | struct root_geometry root_geo; | ||
403 | struct sway_output *output; | ||
404 | bool whole; | ||
405 | }; | ||
406 | |||
407 | static 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 | |||
448 | void 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 | |||
459 | void 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 | |||
474 | static 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 | |||
485 | void 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 | |||
500 | static 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 | |||
506 | static 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 | |||
511 | static 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 | |||
517 | static 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 | |||
523 | static 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 | |||
529 | void 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 | } | ||