diff options
Diffstat (limited to 'sway/desktop/output.c')
-rw-r--r-- | sway/desktop/output.c | 958 |
1 files changed, 274 insertions, 684 deletions
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 5edc8f96..9c4baafd 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c | |||
@@ -4,39 +4,57 @@ | |||
4 | #include <strings.h> | 4 | #include <strings.h> |
5 | #include <time.h> | 5 | #include <time.h> |
6 | #include <wayland-server-core.h> | 6 | #include <wayland-server-core.h> |
7 | #include <wlr/config.h> | ||
8 | #include <wlr/backend/headless.h> | ||
9 | #include <wlr/render/swapchain.h> | ||
7 | #include <wlr/render/wlr_renderer.h> | 10 | #include <wlr/render/wlr_renderer.h> |
8 | #include <wlr/types/wlr_box.h> | ||
9 | #include <wlr/types/wlr_buffer.h> | 11 | #include <wlr/types/wlr_buffer.h> |
12 | #include <wlr/types/wlr_gamma_control_v1.h> | ||
10 | #include <wlr/types/wlr_matrix.h> | 13 | #include <wlr/types/wlr_matrix.h> |
11 | #include <wlr/types/wlr_output_damage.h> | ||
12 | #include <wlr/types/wlr_output_layout.h> | 14 | #include <wlr/types/wlr_output_layout.h> |
13 | #include <wlr/types/wlr_output.h> | 15 | #include <wlr/types/wlr_output.h> |
14 | #include <wlr/types/wlr_presentation_time.h> | 16 | #include <wlr/types/wlr_presentation_time.h> |
15 | #include <wlr/types/wlr_surface.h> | 17 | #include <wlr/types/wlr_compositor.h> |
16 | #include <wlr/util/region.h> | 18 | #include <wlr/util/region.h> |
19 | #include <wlr/util/transform.h> | ||
17 | #include "config.h" | 20 | #include "config.h" |
18 | #include "log.h" | 21 | #include "log.h" |
19 | #include "sway/config.h" | 22 | #include "sway/config.h" |
20 | #include "sway/desktop/transaction.h" | 23 | #include "sway/desktop/transaction.h" |
21 | #include "sway/input/input-manager.h" | 24 | #include "sway/input/input-manager.h" |
22 | #include "sway/input/seat.h" | 25 | #include "sway/input/seat.h" |
26 | #include "sway/ipc-server.h" | ||
23 | #include "sway/layers.h" | 27 | #include "sway/layers.h" |
24 | #include "sway/output.h" | 28 | #include "sway/output.h" |
29 | #include "sway/scene_descriptor.h" | ||
25 | #include "sway/server.h" | 30 | #include "sway/server.h" |
26 | #include "sway/surface.h" | ||
27 | #include "sway/tree/arrange.h" | 31 | #include "sway/tree/arrange.h" |
28 | #include "sway/tree/container.h" | 32 | #include "sway/tree/container.h" |
29 | #include "sway/tree/root.h" | 33 | #include "sway/tree/root.h" |
30 | #include "sway/tree/view.h" | 34 | #include "sway/tree/view.h" |
31 | #include "sway/tree/workspace.h" | 35 | #include "sway/tree/workspace.h" |
32 | 36 | ||
37 | #if WLR_HAS_DRM_BACKEND | ||
38 | #include <wlr/backend/drm.h> | ||
39 | #include <wlr/types/wlr_drm_lease_v1.h> | ||
40 | #endif | ||
41 | |||
42 | bool output_match_name_or_id(struct sway_output *output, | ||
43 | const char *name_or_id) { | ||
44 | if (strcmp(name_or_id, "*") == 0) { | ||
45 | return true; | ||
46 | } | ||
47 | |||
48 | char identifier[128]; | ||
49 | output_get_identifier(identifier, sizeof(identifier), output); | ||
50 | return strcasecmp(identifier, name_or_id) == 0 | ||
51 | || strcasecmp(output->wlr_output->name, name_or_id) == 0; | ||
52 | } | ||
53 | |||
33 | struct sway_output *output_by_name_or_id(const char *name_or_id) { | 54 | struct sway_output *output_by_name_or_id(const char *name_or_id) { |
34 | for (int i = 0; i < root->outputs->length; ++i) { | 55 | for (int i = 0; i < root->outputs->length; ++i) { |
35 | struct sway_output *output = root->outputs->items[i]; | 56 | struct sway_output *output = root->outputs->items[i]; |
36 | char identifier[128]; | 57 | if (output_match_name_or_id(output, name_or_id)) { |
37 | output_get_identifier(identifier, sizeof(identifier), output); | ||
38 | if (strcasecmp(identifier, name_or_id) == 0 | ||
39 | || strcasecmp(output->wlr_output->name, name_or_id) == 0) { | ||
40 | return output; | 58 | return output; |
41 | } | 59 | } |
42 | } | 60 | } |
@@ -46,583 +64,209 @@ struct sway_output *output_by_name_or_id(const char *name_or_id) { | |||
46 | struct sway_output *all_output_by_name_or_id(const char *name_or_id) { | 64 | struct sway_output *all_output_by_name_or_id(const char *name_or_id) { |
47 | struct sway_output *output; | 65 | struct sway_output *output; |
48 | wl_list_for_each(output, &root->all_outputs, link) { | 66 | wl_list_for_each(output, &root->all_outputs, link) { |
49 | char identifier[128]; | 67 | if (output_match_name_or_id(output, name_or_id)) { |
50 | output_get_identifier(identifier, sizeof(identifier), output); | ||
51 | if (strcasecmp(identifier, name_or_id) == 0 | ||
52 | || strcasecmp(output->wlr_output->name, name_or_id) == 0) { | ||
53 | return output; | 68 | return output; |
54 | } | 69 | } |
55 | } | 70 | } |
56 | return NULL; | 71 | return NULL; |
57 | } | 72 | } |
58 | 73 | ||
59 | /** | ||
60 | * Rotate a child's position relative to a parent. The parent size is (pw, ph), | ||
61 | * the child position is (*sx, *sy) and its size is (sw, sh). | ||
62 | */ | ||
63 | static void rotate_child_position(double *sx, double *sy, double sw, double sh, | ||
64 | double pw, double ph, float rotation) { | ||
65 | if (rotation == 0.0f) { | ||
66 | return; | ||
67 | } | ||
68 | 74 | ||
69 | // Coordinates relative to the center of the subsurface | 75 | struct sway_workspace *output_get_active_workspace(struct sway_output *output) { |
70 | double ox = *sx - pw/2 + sw/2, | 76 | struct sway_seat *seat = input_manager_current_seat(); |
71 | oy = *sy - ph/2 + sh/2; | 77 | struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); |
72 | // Rotated coordinates | 78 | if (!focus) { |
73 | double rx = cos(-rotation)*ox - sin(-rotation)*oy, | 79 | if (!output->workspaces->length) { |
74 | ry = cos(-rotation)*oy + sin(-rotation)*ox; | 80 | return NULL; |
75 | *sx = rx + pw/2 - sw/2; | 81 | } |
76 | *sy = ry + ph/2 - sh/2; | 82 | return output->workspaces->items[0]; |
83 | } | ||
84 | return focus->sway_workspace; | ||
77 | } | 85 | } |
78 | 86 | ||
79 | struct surface_iterator_data { | 87 | struct send_frame_done_data { |
80 | sway_surface_iterator_func_t user_iterator; | 88 | struct timespec when; |
81 | void *user_data; | 89 | int msec_until_refresh; |
82 | |||
83 | struct sway_output *output; | 90 | struct sway_output *output; |
84 | struct sway_view *view; | ||
85 | double ox, oy; | ||
86 | int width, height; | ||
87 | float rotation; | ||
88 | }; | 91 | }; |
89 | 92 | ||
90 | static bool get_surface_box(struct surface_iterator_data *data, | 93 | struct buffer_timer { |
91 | struct wlr_surface *surface, int sx, int sy, | 94 | struct wl_listener destroy; |
92 | struct wlr_box *surface_box) { | 95 | struct wl_event_source *frame_done_timer; |
93 | struct sway_output *output = data->output; | 96 | }; |
94 | |||
95 | if (!wlr_surface_has_buffer(surface)) { | ||
96 | return false; | ||
97 | } | ||
98 | |||
99 | int sw = surface->current.width; | ||
100 | int sh = surface->current.height; | ||
101 | |||
102 | double _sx = sx + surface->sx; | ||
103 | double _sy = sy + surface->sy; | ||
104 | rotate_child_position(&_sx, &_sy, sw, sh, data->width, data->height, | ||
105 | data->rotation); | ||
106 | |||
107 | struct wlr_box box = { | ||
108 | .x = data->ox + _sx, | ||
109 | .y = data->oy + _sy, | ||
110 | .width = sw, | ||
111 | .height = sh, | ||
112 | }; | ||
113 | if (surface_box != NULL) { | ||
114 | memcpy(surface_box, &box, sizeof(struct wlr_box)); | ||
115 | } | ||
116 | |||
117 | struct wlr_box rotated_box; | ||
118 | wlr_box_rotated_bounds(&rotated_box, &box, data->rotation); | ||
119 | |||
120 | struct wlr_box output_box = { | ||
121 | .width = output->width, | ||
122 | .height = output->height, | ||
123 | }; | ||
124 | |||
125 | struct wlr_box intersection; | ||
126 | return wlr_box_intersection(&intersection, &output_box, &rotated_box); | ||
127 | } | ||
128 | |||
129 | static void output_for_each_surface_iterator(struct wlr_surface *surface, | ||
130 | int sx, int sy, void *_data) { | ||
131 | struct surface_iterator_data *data = _data; | ||
132 | |||
133 | struct wlr_box box; | ||
134 | bool intersects = get_surface_box(data, surface, sx, sy, &box); | ||
135 | if (!intersects) { | ||
136 | return; | ||
137 | } | ||
138 | |||
139 | data->user_iterator(data->output, data->view, surface, &box, data->rotation, | ||
140 | data->user_data); | ||
141 | } | ||
142 | 97 | ||
143 | void output_surface_for_each_surface(struct sway_output *output, | 98 | static int handle_buffer_timer(void *data) { |
144 | struct wlr_surface *surface, double ox, double oy, | 99 | struct wlr_scene_buffer *buffer = data; |
145 | sway_surface_iterator_func_t iterator, void *user_data) { | ||
146 | struct surface_iterator_data data = { | ||
147 | .user_iterator = iterator, | ||
148 | .user_data = user_data, | ||
149 | .output = output, | ||
150 | .view = NULL, | ||
151 | .ox = ox, | ||
152 | .oy = oy, | ||
153 | .width = surface->current.width, | ||
154 | .height = surface->current.height, | ||
155 | .rotation = 0, | ||
156 | }; | ||
157 | |||
158 | wlr_surface_for_each_surface(surface, | ||
159 | output_for_each_surface_iterator, &data); | ||
160 | } | ||
161 | 100 | ||
162 | void output_view_for_each_surface(struct sway_output *output, | 101 | struct timespec now; |
163 | struct sway_view *view, sway_surface_iterator_func_t iterator, | 102 | clock_gettime(CLOCK_MONOTONIC, &now); |
164 | void *user_data) { | 103 | wlr_scene_buffer_send_frame_done(buffer, &now); |
165 | struct surface_iterator_data data = { | 104 | return 0; |
166 | .user_iterator = iterator, | ||
167 | .user_data = user_data, | ||
168 | .output = output, | ||
169 | .view = view, | ||
170 | .ox = view->container->surface_x - output->lx | ||
171 | - view->geometry.x, | ||
172 | .oy = view->container->surface_y - output->ly | ||
173 | - view->geometry.y, | ||
174 | .width = view->container->current.content_width, | ||
175 | .height = view->container->current.content_height, | ||
176 | .rotation = 0, // TODO | ||
177 | }; | ||
178 | |||
179 | view_for_each_surface(view, output_for_each_surface_iterator, &data); | ||
180 | } | 105 | } |
181 | 106 | ||
182 | void output_view_for_each_popup_surface(struct sway_output *output, | 107 | static void handle_buffer_timer_destroy(struct wl_listener *listener, |
183 | struct sway_view *view, sway_surface_iterator_func_t iterator, | 108 | void *data) { |
184 | void *user_data) { | 109 | struct buffer_timer *timer = wl_container_of(listener, timer, destroy); |
185 | struct surface_iterator_data data = { | ||
186 | .user_iterator = iterator, | ||
187 | .user_data = user_data, | ||
188 | .output = output, | ||
189 | .view = view, | ||
190 | .ox = view->container->surface_x - output->lx | ||
191 | - view->geometry.x, | ||
192 | .oy = view->container->surface_y - output->ly | ||
193 | - view->geometry.y, | ||
194 | .width = view->container->current.content_width, | ||
195 | .height = view->container->current.content_height, | ||
196 | .rotation = 0, // TODO | ||
197 | }; | ||
198 | |||
199 | view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); | ||
200 | } | ||
201 | 110 | ||
202 | void output_layer_for_each_surface(struct sway_output *output, | 111 | wl_list_remove(&timer->destroy.link); |
203 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 112 | wl_event_source_remove(timer->frame_done_timer); |
204 | void *user_data) { | 113 | free(timer); |
205 | struct sway_layer_surface *layer_surface; | ||
206 | wl_list_for_each(layer_surface, layer_surfaces, link) { | ||
207 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
208 | layer_surface->layer_surface; | ||
209 | output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, | ||
210 | layer_surface->geo.x, layer_surface->geo.y, iterator, | ||
211 | user_data); | ||
212 | |||
213 | struct wlr_xdg_popup *state; | ||
214 | wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { | ||
215 | struct wlr_xdg_surface *popup = state->base; | ||
216 | if (!popup->configured) { | ||
217 | continue; | ||
218 | } | ||
219 | |||
220 | double popup_sx, popup_sy; | ||
221 | popup_sx = layer_surface->geo.x + | ||
222 | popup->popup->geometry.x - popup->geometry.x; | ||
223 | popup_sy = layer_surface->geo.y + | ||
224 | popup->popup->geometry.y - popup->geometry.y; | ||
225 | |||
226 | struct wlr_surface *surface = popup->surface; | ||
227 | |||
228 | struct surface_iterator_data data = { | ||
229 | .user_iterator = iterator, | ||
230 | .user_data = user_data, | ||
231 | .output = output, | ||
232 | .view = NULL, | ||
233 | .ox = popup_sx, | ||
234 | .oy = popup_sy, | ||
235 | .width = surface->current.width, | ||
236 | .height = surface->current.height, | ||
237 | .rotation = 0, | ||
238 | }; | ||
239 | |||
240 | wlr_xdg_surface_for_each_surface( | ||
241 | popup, output_for_each_surface_iterator, &data); | ||
242 | } | ||
243 | } | ||
244 | } | 114 | } |
245 | 115 | ||
246 | void output_layer_for_each_toplevel_surface(struct sway_output *output, | 116 | static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_buffer *buffer) { |
247 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 117 | struct buffer_timer *timer = |
248 | void *user_data) { | 118 | scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER); |
249 | struct sway_layer_surface *layer_surface; | 119 | if (timer) { |
250 | wl_list_for_each(layer_surface, layer_surfaces, link) { | 120 | return timer; |
251 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
252 | layer_surface->layer_surface; | ||
253 | output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, | ||
254 | layer_surface->geo.x, layer_surface->geo.y, iterator, | ||
255 | user_data); | ||
256 | } | 121 | } |
257 | } | ||
258 | 122 | ||
259 | 123 | timer = calloc(1, sizeof(struct buffer_timer)); | |
260 | void output_layer_for_each_popup_surface(struct sway_output *output, | 124 | if (!timer) { |
261 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 125 | return NULL; |
262 | void *user_data) { | ||
263 | struct sway_layer_surface *layer_surface; | ||
264 | wl_list_for_each(layer_surface, layer_surfaces, link) { | ||
265 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
266 | layer_surface->layer_surface; | ||
267 | |||
268 | struct wlr_xdg_popup *state; | ||
269 | wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { | ||
270 | struct wlr_xdg_surface *popup = state->base; | ||
271 | if (!popup->configured) { | ||
272 | continue; | ||
273 | } | ||
274 | |||
275 | double popup_sx, popup_sy; | ||
276 | popup_sx = layer_surface->geo.x + | ||
277 | popup->popup->geometry.x - popup->geometry.x; | ||
278 | popup_sy = layer_surface->geo.y + | ||
279 | popup->popup->geometry.y - popup->geometry.y; | ||
280 | |||
281 | struct wlr_surface *surface = popup->surface; | ||
282 | |||
283 | struct surface_iterator_data data = { | ||
284 | .user_iterator = iterator, | ||
285 | .user_data = user_data, | ||
286 | .output = output, | ||
287 | .view = NULL, | ||
288 | .ox = popup_sx, | ||
289 | .oy = popup_sy, | ||
290 | .width = surface->current.width, | ||
291 | .height = surface->current.height, | ||
292 | .rotation = 0, | ||
293 | }; | ||
294 | |||
295 | wlr_xdg_surface_for_each_surface( | ||
296 | popup, output_for_each_surface_iterator, &data); | ||
297 | } | ||
298 | } | 126 | } |
299 | } | ||
300 | 127 | ||
301 | #if HAVE_XWAYLAND | 128 | timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, |
302 | void output_unmanaged_for_each_surface(struct sway_output *output, | 129 | handle_buffer_timer, buffer); |
303 | struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, | 130 | if (!timer->frame_done_timer) { |
304 | void *user_data) { | 131 | free(timer); |
305 | struct sway_xwayland_unmanaged *unmanaged_surface; | 132 | return NULL; |
306 | wl_list_for_each(unmanaged_surface, unmanaged, link) { | ||
307 | struct wlr_xwayland_surface *xsurface = | ||
308 | unmanaged_surface->wlr_xwayland_surface; | ||
309 | double ox = unmanaged_surface->lx - output->lx; | ||
310 | double oy = unmanaged_surface->ly - output->ly; | ||
311 | |||
312 | output_surface_for_each_surface(output, xsurface->surface, ox, oy, | ||
313 | iterator, user_data); | ||
314 | } | 133 | } |
315 | } | ||
316 | #endif | ||
317 | 134 | ||
318 | void output_drag_icons_for_each_surface(struct sway_output *output, | 135 | scene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer); |
319 | struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, | ||
320 | void *user_data) { | ||
321 | struct sway_drag_icon *drag_icon; | ||
322 | wl_list_for_each(drag_icon, drag_icons, link) { | ||
323 | double ox = drag_icon->x - output->lx; | ||
324 | double oy = drag_icon->y - output->ly; | ||
325 | |||
326 | if (drag_icon->wlr_drag_icon->mapped) { | ||
327 | output_surface_for_each_surface(output, | ||
328 | drag_icon->wlr_drag_icon->surface, ox, oy, | ||
329 | iterator, user_data); | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | 136 | ||
334 | static void for_each_surface_container_iterator(struct sway_container *con, | 137 | timer->destroy.notify = handle_buffer_timer_destroy; |
335 | void *_data) { | 138 | wl_signal_add(&buffer->node.events.destroy, &timer->destroy); |
336 | if (!con->view || !view_is_visible(con->view)) { | ||
337 | return; | ||
338 | } | ||
339 | 139 | ||
340 | struct surface_iterator_data *data = _data; | 140 | return timer; |
341 | output_view_for_each_surface(data->output, con->view, | ||
342 | data->user_iterator, data->user_data); | ||
343 | } | 141 | } |
344 | 142 | ||
345 | static void output_for_each_surface(struct sway_output *output, | 143 | static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, |
346 | sway_surface_iterator_func_t iterator, void *user_data) { | 144 | int x, int y, void *user_data) { |
347 | if (output_has_opaque_overlay_layer_surface(output)) { | 145 | struct send_frame_done_data *data = user_data; |
348 | goto overlay; | 146 | struct sway_output *output = data->output; |
349 | } | 147 | int view_max_render_time = 0; |
350 | 148 | ||
351 | struct surface_iterator_data data = { | 149 | if (buffer->primary_output != data->output->scene_output) { |
352 | .user_iterator = iterator, | 150 | return; |
353 | .user_data = user_data, | ||
354 | .output = output, | ||
355 | .view = NULL, | ||
356 | }; | ||
357 | |||
358 | struct sway_workspace *workspace = output_get_active_workspace(output); | ||
359 | struct sway_container *fullscreen_con = root->fullscreen_global; | ||
360 | if (!fullscreen_con) { | ||
361 | if (!workspace) { | ||
362 | return; | ||
363 | } | ||
364 | fullscreen_con = workspace->current.fullscreen; | ||
365 | } | ||
366 | if (fullscreen_con) { | ||
367 | for_each_surface_container_iterator(fullscreen_con, &data); | ||
368 | container_for_each_child(fullscreen_con, | ||
369 | for_each_surface_container_iterator, &data); | ||
370 | |||
371 | // TODO: Show transient containers for fullscreen global | ||
372 | if (fullscreen_con == workspace->current.fullscreen) { | ||
373 | for (int i = 0; i < workspace->current.floating->length; ++i) { | ||
374 | struct sway_container *floater = | ||
375 | workspace->current.floating->items[i]; | ||
376 | if (container_is_transient_for(floater, fullscreen_con)) { | ||
377 | for_each_surface_container_iterator(floater, &data); | ||
378 | } | ||
379 | } | ||
380 | } | ||
381 | #if HAVE_XWAYLAND | ||
382 | output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, | ||
383 | iterator, user_data); | ||
384 | #endif | ||
385 | } else { | ||
386 | output_layer_for_each_surface(output, | ||
387 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
388 | iterator, user_data); | ||
389 | output_layer_for_each_surface(output, | ||
390 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
391 | iterator, user_data); | ||
392 | |||
393 | workspace_for_each_container(workspace, | ||
394 | for_each_surface_container_iterator, &data); | ||
395 | |||
396 | #if HAVE_XWAYLAND | ||
397 | output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, | ||
398 | iterator, user_data); | ||
399 | #endif | ||
400 | output_layer_for_each_surface(output, | ||
401 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
402 | iterator, user_data); | ||
403 | } | 151 | } |
404 | 152 | ||
405 | overlay: | 153 | struct wlr_scene_node *current = &buffer->node; |
406 | output_layer_for_each_surface(output, | 154 | while (true) { |
407 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | 155 | struct sway_view *view = scene_descriptor_try_get(current, |
408 | iterator, user_data); | 156 | SWAY_SCENE_DESC_VIEW); |
409 | output_drag_icons_for_each_surface(output, &root->drag_icons, | 157 | if (view) { |
410 | iterator, user_data); | 158 | view_max_render_time = view->max_render_time; |
411 | } | 159 | break; |
412 | |||
413 | static int scale_length(int length, int offset, float scale) { | ||
414 | return round((offset + length) * scale) - round(offset * scale); | ||
415 | } | ||
416 | |||
417 | void scale_box(struct wlr_box *box, float scale) { | ||
418 | box->width = scale_length(box->width, box->x, scale); | ||
419 | box->height = scale_length(box->height, box->y, scale); | ||
420 | box->x = round(box->x * scale); | ||
421 | box->y = round(box->y * scale); | ||
422 | } | ||
423 | |||
424 | struct sway_workspace *output_get_active_workspace(struct sway_output *output) { | ||
425 | struct sway_seat *seat = input_manager_current_seat(); | ||
426 | struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); | ||
427 | if (!focus) { | ||
428 | if (!output->workspaces->length) { | ||
429 | return NULL; | ||
430 | } | 160 | } |
431 | return output->workspaces->items[0]; | ||
432 | } | ||
433 | return focus->sway_workspace; | ||
434 | } | ||
435 | 161 | ||
436 | bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { | 162 | if (!current->parent) { |
437 | struct sway_layer_surface *sway_layer_surface; | 163 | break; |
438 | wl_list_for_each(sway_layer_surface, | ||
439 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { | ||
440 | struct wlr_surface *wlr_surface = sway_layer_surface->layer_surface->surface; | ||
441 | pixman_box32_t output_box = { | ||
442 | .x2 = output->width, | ||
443 | .y2 = output->height, | ||
444 | }; | ||
445 | pixman_region32_t surface_opaque_box; | ||
446 | pixman_region32_init(&surface_opaque_box); | ||
447 | pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region); | ||
448 | pixman_region32_translate(&surface_opaque_box, | ||
449 | sway_layer_surface->geo.x, sway_layer_surface->geo.y); | ||
450 | pixman_region_overlap_t contains = | ||
451 | pixman_region32_contains_rectangle(&surface_opaque_box, &output_box); | ||
452 | pixman_region32_fini(&surface_opaque_box); | ||
453 | |||
454 | if (contains == PIXMAN_REGION_IN) { | ||
455 | return true; | ||
456 | } | 164 | } |
457 | } | ||
458 | return false; | ||
459 | } | ||
460 | 165 | ||
461 | struct send_frame_done_data { | 166 | current = ¤t->parent->node; |
462 | struct timespec when; | ||
463 | int msec_until_refresh; | ||
464 | }; | ||
465 | |||
466 | static void send_frame_done_iterator(struct sway_output *output, struct sway_view *view, | ||
467 | struct wlr_surface *surface, struct wlr_box *box, float rotation, | ||
468 | void *user_data) { | ||
469 | int view_max_render_time = 0; | ||
470 | if (view != NULL) { | ||
471 | view_max_render_time = view->max_render_time; | ||
472 | } | 167 | } |
473 | 168 | ||
474 | struct send_frame_done_data *data = user_data; | ||
475 | |||
476 | int delay = data->msec_until_refresh - output->max_render_time | 169 | int delay = data->msec_until_refresh - output->max_render_time |
477 | - view_max_render_time; | 170 | - view_max_render_time; |
478 | 171 | ||
479 | if (output->max_render_time == 0 || view_max_render_time == 0 || delay < 1) { | 172 | struct buffer_timer *timer = NULL; |
480 | wlr_surface_send_frame_done(surface, &data->when); | ||
481 | } else { | ||
482 | struct sway_surface *sway_surface = surface->data; | ||
483 | wl_event_source_timer_update(sway_surface->frame_done_timer, delay); | ||
484 | } | ||
485 | } | ||
486 | |||
487 | static void send_frame_done(struct sway_output *output, struct send_frame_done_data *data) { | ||
488 | output_for_each_surface(output, send_frame_done_iterator, data); | ||
489 | } | ||
490 | |||
491 | static void count_surface_iterator(struct sway_output *output, struct sway_view *view, | ||
492 | struct wlr_surface *surface, struct wlr_box *_box, float rotation, | ||
493 | void *data) { | ||
494 | size_t *n = data; | ||
495 | (*n)++; | ||
496 | } | ||
497 | |||
498 | static bool scan_out_fullscreen_view(struct sway_output *output, | ||
499 | struct sway_view *view) { | ||
500 | struct wlr_output *wlr_output = output->wlr_output; | ||
501 | struct sway_workspace *workspace = output->current.active_workspace; | ||
502 | if (!sway_assert(workspace, "Expected an active workspace")) { | ||
503 | return false; | ||
504 | } | ||
505 | 173 | ||
506 | if (!wl_list_empty(&view->saved_buffers)) { | 174 | if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) { |
507 | return false; | 175 | timer = buffer_timer_get_or_create(buffer); |
508 | } | 176 | } |
509 | 177 | ||
510 | for (int i = 0; i < workspace->current.floating->length; ++i) { | 178 | if (timer) { |
511 | struct sway_container *floater = | 179 | wl_event_source_timer_update(timer->frame_done_timer, delay); |
512 | workspace->current.floating->items[i]; | 180 | } else { |
513 | if (container_is_transient_for(floater, view->container)) { | 181 | wlr_scene_buffer_send_frame_done(buffer, &data->when); |
514 | return false; | ||
515 | } | ||
516 | } | 182 | } |
183 | } | ||
517 | 184 | ||
518 | #if HAVE_XWAYLAND | 185 | static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output) { |
519 | if (!wl_list_empty(&root->xwayland_unmanaged)) { | 186 | switch (output->scale_filter) { |
520 | return false; | 187 | case SCALE_FILTER_LINEAR: |
188 | return WLR_SCALE_FILTER_BILINEAR; | ||
189 | case SCALE_FILTER_NEAREST: | ||
190 | return WLR_SCALE_FILTER_NEAREST; | ||
191 | default: | ||
192 | abort(); // unreachable | ||
521 | } | 193 | } |
522 | #endif | 194 | } |
523 | 195 | ||
524 | if (!wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) { | 196 | static void output_configure_scene(struct sway_output *output, |
525 | return false; | 197 | struct wlr_scene_node *node, float opacity) { |
526 | } | 198 | if (!node->enabled) { |
527 | if (!wl_list_empty(&root->drag_icons)) { | 199 | return; |
528 | return false; | ||
529 | } | 200 | } |
530 | 201 | ||
531 | struct wlr_surface *surface = view->surface; | 202 | struct sway_container *con = |
532 | if (surface == NULL) { | 203 | scene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER); |
533 | return false; | 204 | if (con) { |
534 | } | 205 | opacity = con->alpha; |
535 | size_t n_surfaces = 0; | ||
536 | output_view_for_each_surface(output, view, | ||
537 | count_surface_iterator, &n_surfaces); | ||
538 | if (n_surfaces != 1) { | ||
539 | return false; | ||
540 | } | 206 | } |
541 | 207 | ||
542 | if (surface->buffer == NULL) { | 208 | if (node->type == WLR_SCENE_NODE_BUFFER) { |
543 | return false; | 209 | struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); |
544 | } | ||
545 | 210 | ||
546 | if ((float)surface->current.scale != wlr_output->scale || | 211 | // hack: don't call the scene setter because that will damage all outputs |
547 | surface->current.transform != wlr_output->transform) { | 212 | // We don't want to damage outputs that aren't our current output that |
548 | return false; | 213 | // we're configuring |
549 | } | 214 | buffer->filter_mode = get_scale_filter(output); |
550 | 215 | ||
551 | wlr_output_attach_buffer(wlr_output, &surface->buffer->base); | 216 | wlr_scene_buffer_set_opacity(buffer, opacity); |
552 | if (!wlr_output_test(wlr_output)) { | 217 | } else if (node->type == WLR_SCENE_NODE_TREE) { |
553 | return false; | 218 | struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node); |
219 | struct wlr_scene_node *node; | ||
220 | wl_list_for_each(node, &tree->children, link) { | ||
221 | output_configure_scene(output, node, opacity); | ||
222 | } | ||
554 | } | 223 | } |
555 | |||
556 | wlr_presentation_surface_sampled_on_output(server.presentation, surface, | ||
557 | wlr_output); | ||
558 | |||
559 | return wlr_output_commit(wlr_output); | ||
560 | } | 224 | } |
561 | 225 | ||
562 | static int output_repaint_timer_handler(void *data) { | 226 | static int output_repaint_timer_handler(void *data) { |
563 | struct sway_output *output = data; | 227 | struct sway_output *output = data; |
564 | if (output->wlr_output == NULL) { | ||
565 | return 0; | ||
566 | } | ||
567 | |||
568 | output->wlr_output->frame_pending = false; | ||
569 | 228 | ||
570 | struct sway_workspace *workspace = output->current.active_workspace; | 229 | if (!output->enabled) { |
571 | if (workspace == NULL) { | ||
572 | return 0; | 230 | return 0; |
573 | } | 231 | } |
574 | 232 | ||
575 | struct sway_container *fullscreen_con = root->fullscreen_global; | 233 | output->wlr_output->frame_pending = false; |
576 | if (!fullscreen_con) { | ||
577 | fullscreen_con = workspace->current.fullscreen; | ||
578 | } | ||
579 | 234 | ||
580 | if (fullscreen_con && fullscreen_con->view) { | 235 | output_configure_scene(output, &root->root_scene->tree.node, 1.0f); |
581 | // Try to scan-out the fullscreen view | ||
582 | static bool last_scanned_out = false; | ||
583 | bool scanned_out = | ||
584 | scan_out_fullscreen_view(output, fullscreen_con->view); | ||
585 | 236 | ||
586 | if (scanned_out && !last_scanned_out) { | 237 | if (output->gamma_lut_changed) { |
587 | sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", | 238 | struct wlr_output_state pending; |
588 | output->wlr_output->name); | 239 | wlr_output_state_init(&pending); |
240 | if (!wlr_scene_output_build_state(output->scene_output, &pending, NULL)) { | ||
241 | return 0; | ||
589 | } | 242 | } |
590 | if (last_scanned_out && !scanned_out) { | 243 | |
591 | sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", | 244 | output->gamma_lut_changed = false; |
592 | output->wlr_output->name); | 245 | struct wlr_gamma_control_v1 *gamma_control = |
246 | wlr_gamma_control_manager_v1_get_control( | ||
247 | server.gamma_control_manager_v1, output->wlr_output); | ||
248 | if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) { | ||
249 | wlr_output_state_finish(&pending); | ||
250 | return 0; | ||
593 | } | 251 | } |
594 | last_scanned_out = scanned_out; | ||
595 | 252 | ||
596 | if (scanned_out) { | 253 | if (!wlr_output_commit_state(output->wlr_output, &pending)) { |
254 | wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); | ||
255 | wlr_output_state_finish(&pending); | ||
597 | return 0; | 256 | return 0; |
598 | } | 257 | } |
599 | } | ||
600 | 258 | ||
601 | bool needs_frame; | 259 | wlr_output_state_finish(&pending); |
602 | pixman_region32_t damage; | ||
603 | pixman_region32_init(&damage); | ||
604 | if (!wlr_output_damage_attach_render(output->damage, | ||
605 | &needs_frame, &damage)) { | ||
606 | return 0; | 260 | return 0; |
607 | } | 261 | } |
608 | 262 | ||
609 | if (needs_frame) { | 263 | wlr_scene_output_commit(output->scene_output, NULL); |
610 | struct timespec now; | ||
611 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
612 | |||
613 | output_render(output, &now, &damage); | ||
614 | } else { | ||
615 | wlr_output_rollback(output->wlr_output); | ||
616 | } | ||
617 | |||
618 | pixman_region32_fini(&damage); | ||
619 | |||
620 | return 0; | 264 | return 0; |
621 | } | 265 | } |
622 | 266 | ||
623 | static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | 267 | static void handle_frame(struct wl_listener *listener, void *user_data) { |
624 | struct sway_output *output = | 268 | struct sway_output *output = |
625 | wl_container_of(listener, output, damage_frame); | 269 | wl_container_of(listener, output, frame); |
626 | if (!output->enabled || !output->wlr_output->enabled) { | 270 | if (!output->enabled || !output->wlr_output->enabled) { |
627 | return; | 271 | return; |
628 | } | 272 | } |
@@ -633,9 +277,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | |||
633 | 277 | ||
634 | if (output->max_render_time != 0) { | 278 | if (output->max_render_time != 0) { |
635 | struct timespec now; | 279 | struct timespec now; |
636 | clockid_t presentation_clock | 280 | clock_gettime(CLOCK_MONOTONIC, &now); |
637 | = wlr_backend_get_presentation_clock(server.backend); | ||
638 | clock_gettime(presentation_clock, &now); | ||
639 | 281 | ||
640 | const long NSEC_IN_SECONDS = 1000000000; | 282 | const long NSEC_IN_SECONDS = 1000000000; |
641 | struct timespec predicted_refresh = output->last_presentation; | 283 | struct timespec predicted_refresh = output->last_presentation; |
@@ -682,124 +324,8 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | |||
682 | struct send_frame_done_data data = {0}; | 324 | struct send_frame_done_data data = {0}; |
683 | clock_gettime(CLOCK_MONOTONIC, &data.when); | 325 | clock_gettime(CLOCK_MONOTONIC, &data.when); |
684 | data.msec_until_refresh = msec_until_refresh; | 326 | data.msec_until_refresh = msec_until_refresh; |
685 | send_frame_done(output, &data); | 327 | data.output = output; |
686 | } | 328 | wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data); |
687 | |||
688 | void output_damage_whole(struct sway_output *output) { | ||
689 | // The output can exist with no wlr_output if it's just been disconnected | ||
690 | // and the transaction to evacuate it has't completed yet. | ||
691 | if (output && output->wlr_output && output->damage) { | ||
692 | wlr_output_damage_add_whole(output->damage); | ||
693 | } | ||
694 | } | ||
695 | |||
696 | static void damage_surface_iterator(struct sway_output *output, struct sway_view *view, | ||
697 | struct wlr_surface *surface, struct wlr_box *_box, float rotation, | ||
698 | void *_data) { | ||
699 | bool *data = _data; | ||
700 | bool whole = *data; | ||
701 | |||
702 | struct wlr_box box = *_box; | ||
703 | scale_box(&box, output->wlr_output->scale); | ||
704 | |||
705 | int center_x = box.x + box.width/2; | ||
706 | int center_y = box.y + box.height/2; | ||
707 | |||
708 | if (pixman_region32_not_empty(&surface->buffer_damage)) { | ||
709 | pixman_region32_t damage; | ||
710 | pixman_region32_init(&damage); | ||
711 | wlr_surface_get_effective_damage(surface, &damage); | ||
712 | wlr_region_scale(&damage, &damage, output->wlr_output->scale); | ||
713 | if (ceil(output->wlr_output->scale) > surface->current.scale) { | ||
714 | // When scaling up a surface, it'll become blurry so we need to | ||
715 | // expand the damage region | ||
716 | wlr_region_expand(&damage, &damage, | ||
717 | ceil(output->wlr_output->scale) - surface->current.scale); | ||
718 | } | ||
719 | pixman_region32_translate(&damage, box.x, box.y); | ||
720 | wlr_region_rotated_bounds(&damage, &damage, rotation, | ||
721 | center_x, center_y); | ||
722 | wlr_output_damage_add(output->damage, &damage); | ||
723 | pixman_region32_fini(&damage); | ||
724 | } | ||
725 | |||
726 | if (whole) { | ||
727 | wlr_box_rotated_bounds(&box, &box, rotation); | ||
728 | wlr_output_damage_add_box(output->damage, &box); | ||
729 | } | ||
730 | |||
731 | if (!wl_list_empty(&surface->current.frame_callback_list)) { | ||
732 | wlr_output_schedule_frame(output->wlr_output); | ||
733 | } | ||
734 | } | ||
735 | |||
736 | void output_damage_surface(struct sway_output *output, double ox, double oy, | ||
737 | struct wlr_surface *surface, bool whole) { | ||
738 | output_surface_for_each_surface(output, surface, ox, oy, | ||
739 | damage_surface_iterator, &whole); | ||
740 | } | ||
741 | |||
742 | void output_damage_from_view(struct sway_output *output, | ||
743 | struct sway_view *view) { | ||
744 | if (!view_is_visible(view)) { | ||
745 | return; | ||
746 | } | ||
747 | bool whole = false; | ||
748 | output_view_for_each_surface(output, view, damage_surface_iterator, &whole); | ||
749 | } | ||
750 | |||
751 | // Expecting an unscaled box in layout coordinates | ||
752 | void output_damage_box(struct sway_output *output, struct wlr_box *_box) { | ||
753 | struct wlr_box box; | ||
754 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
755 | box.x -= output->lx; | ||
756 | box.y -= output->ly; | ||
757 | scale_box(&box, output->wlr_output->scale); | ||
758 | wlr_output_damage_add_box(output->damage, &box); | ||
759 | } | ||
760 | |||
761 | static void damage_child_views_iterator(struct sway_container *con, | ||
762 | void *data) { | ||
763 | if (!con->view || !view_is_visible(con->view)) { | ||
764 | return; | ||
765 | } | ||
766 | struct sway_output *output = data; | ||
767 | bool whole = true; | ||
768 | output_view_for_each_surface(output, con->view, damage_surface_iterator, | ||
769 | &whole); | ||
770 | } | ||
771 | |||
772 | void output_damage_whole_container(struct sway_output *output, | ||
773 | struct sway_container *con) { | ||
774 | // Pad the box by 1px, because the width is a double and might be a fraction | ||
775 | struct wlr_box box = { | ||
776 | .x = con->current.x - output->lx - 1, | ||
777 | .y = con->current.y - output->ly - 1, | ||
778 | .width = con->current.width + 2, | ||
779 | .height = con->current.height + 2, | ||
780 | }; | ||
781 | scale_box(&box, output->wlr_output->scale); | ||
782 | wlr_output_damage_add_box(output->damage, &box); | ||
783 | // Damage subsurfaces as well, which may extend outside the box | ||
784 | if (con->view) { | ||
785 | damage_child_views_iterator(con, output); | ||
786 | } else { | ||
787 | container_for_each_child(con, damage_child_views_iterator, output); | ||
788 | } | ||
789 | } | ||
790 | |||
791 | static void damage_handle_destroy(struct wl_listener *listener, void *data) { | ||
792 | struct sway_output *output = | ||
793 | wl_container_of(listener, output, damage_destroy); | ||
794 | if (!output->enabled) { | ||
795 | return; | ||
796 | } | ||
797 | output_disable(output); | ||
798 | |||
799 | wl_list_remove(&output->damage_destroy.link); | ||
800 | wl_list_remove(&output->damage_frame.link); | ||
801 | |||
802 | transaction_commit_dirty(); | ||
803 | } | 329 | } |
804 | 330 | ||
805 | static void update_output_manager_config(struct sway_server *server) { | 331 | static void update_output_manager_config(struct sway_server *server) { |
@@ -808,73 +334,61 @@ static void update_output_manager_config(struct sway_server *server) { | |||
808 | 334 | ||
809 | struct sway_output *output; | 335 | struct sway_output *output; |
810 | wl_list_for_each(output, &root->all_outputs, link) { | 336 | wl_list_for_each(output, &root->all_outputs, link) { |
811 | if (output == root->noop_output) { | 337 | if (output == root->fallback_output) { |
812 | continue; | 338 | continue; |
813 | } | 339 | } |
814 | struct wlr_output_configuration_head_v1 *config_head = | 340 | struct wlr_output_configuration_head_v1 *config_head = |
815 | wlr_output_configuration_head_v1_create(config, output->wlr_output); | 341 | wlr_output_configuration_head_v1_create(config, output->wlr_output); |
816 | struct wlr_box *output_box = wlr_output_layout_get_box( | 342 | struct wlr_box output_box; |
817 | root->output_layout, output->wlr_output); | 343 | wlr_output_layout_get_box(root->output_layout, |
818 | // We mark the output enabled even if it is switched off by DPMS | 344 | output->wlr_output, &output_box); |
819 | config_head->state.enabled = output->enabled; | 345 | // We mark the output enabled when it's switched off but not disabled |
820 | config_head->state.mode = output->current_mode; | 346 | config_head->state.enabled = !wlr_box_empty(&output_box); |
821 | if (output_box) { | 347 | config_head->state.x = output_box.x; |
822 | config_head->state.x = output_box->x; | 348 | config_head->state.y = output_box.y; |
823 | config_head->state.y = output_box->y; | ||
824 | } | ||
825 | } | 349 | } |
826 | 350 | ||
827 | wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); | 351 | wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); |
352 | |||
353 | ipc_event_output(); | ||
828 | } | 354 | } |
829 | 355 | ||
830 | static void handle_destroy(struct wl_listener *listener, void *data) { | 356 | static void begin_destroy(struct sway_output *output) { |
831 | struct sway_output *output = wl_container_of(listener, output, destroy); | ||
832 | struct sway_server *server = output->server; | 357 | struct sway_server *server = output->server; |
833 | wl_signal_emit(&output->events.destroy, output); | ||
834 | 358 | ||
835 | if (output->enabled) { | 359 | if (output->enabled) { |
836 | output_disable(output); | 360 | output_disable(output); |
837 | } | 361 | } |
362 | |||
838 | output_begin_destroy(output); | 363 | output_begin_destroy(output); |
839 | 364 | ||
365 | wl_list_remove(&output->link); | ||
366 | |||
367 | wl_list_remove(&output->layout_destroy.link); | ||
840 | wl_list_remove(&output->destroy.link); | 368 | wl_list_remove(&output->destroy.link); |
841 | wl_list_remove(&output->commit.link); | 369 | wl_list_remove(&output->commit.link); |
842 | wl_list_remove(&output->mode.link); | ||
843 | wl_list_remove(&output->present.link); | 370 | wl_list_remove(&output->present.link); |
371 | wl_list_remove(&output->frame.link); | ||
372 | wl_list_remove(&output->request_state.link); | ||
373 | |||
374 | wlr_scene_output_destroy(output->scene_output); | ||
375 | output->scene_output = NULL; | ||
376 | output->wlr_output->data = NULL; | ||
377 | output->wlr_output = NULL; | ||
844 | 378 | ||
845 | transaction_commit_dirty(); | 379 | transaction_commit_dirty(); |
846 | 380 | ||
847 | update_output_manager_config(server); | 381 | update_output_manager_config(server); |
848 | } | 382 | } |
849 | 383 | ||
850 | static void handle_mode(struct wl_listener *listener, void *data) { | 384 | static void handle_destroy(struct wl_listener *listener, void *data) { |
851 | struct sway_output *output = wl_container_of(listener, output, mode); | 385 | struct sway_output *output = wl_container_of(listener, output, destroy); |
852 | if (!output->enabled && !output->enabling) { | 386 | begin_destroy(output); |
853 | struct output_config *oc = find_output_config(output); | ||
854 | if (output->wlr_output->current_mode != NULL && | ||
855 | (!oc || oc->enabled)) { | ||
856 | // We want to enable this output, but it didn't work last time, | ||
857 | // possibly because we hadn't enough CRTCs. Try again now that the | ||
858 | // output has a mode. | ||
859 | sway_log(SWAY_DEBUG, "Output %s has gained a CRTC, " | ||
860 | "trying to enable it", output->wlr_output->name); | ||
861 | apply_output_config(oc, output); | ||
862 | } | ||
863 | return; | ||
864 | } | ||
865 | if (!output->enabled) { | ||
866 | return; | ||
867 | } | ||
868 | arrange_layers(output); | ||
869 | arrange_output(output); | ||
870 | transaction_commit_dirty(); | ||
871 | |||
872 | update_output_manager_config(output->server); | ||
873 | } | 387 | } |
874 | 388 | ||
875 | static void update_textures(struct sway_container *con, void *data) { | 389 | static void handle_layout_destroy(struct wl_listener *listener, void *data) { |
876 | container_update_title_textures(con); | 390 | struct sway_output *output = wl_container_of(listener, output, layout_destroy); |
877 | container_update_marks_textures(con); | 391 | begin_destroy(output); |
878 | } | 392 | } |
879 | 393 | ||
880 | static void handle_commit(struct wl_listener *listener, void *data) { | 394 | static void handle_commit(struct wl_listener *listener, void *data) { |
@@ -885,24 +399,28 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
885 | return; | 399 | return; |
886 | } | 400 | } |
887 | 401 | ||
888 | if (event->committed & WLR_OUTPUT_STATE_SCALE) { | 402 | if (event->state->committed & ( |
889 | output_for_each_container(output, update_textures, NULL); | 403 | WLR_OUTPUT_STATE_MODE | |
890 | } | 404 | WLR_OUTPUT_STATE_TRANSFORM | |
891 | 405 | WLR_OUTPUT_STATE_SCALE)) { | |
892 | if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) { | ||
893 | arrange_layers(output); | 406 | arrange_layers(output); |
894 | arrange_output(output); | 407 | arrange_output(output); |
895 | transaction_commit_dirty(); | 408 | transaction_commit_dirty(); |
896 | 409 | ||
897 | update_output_manager_config(output->server); | 410 | update_output_manager_config(output->server); |
898 | } | 411 | } |
412 | |||
413 | // Next time the output is enabled, try to re-apply the gamma LUT | ||
414 | if ((event->state->committed & WLR_OUTPUT_STATE_ENABLED) && !output->wlr_output->enabled) { | ||
415 | output->gamma_lut_changed = true; | ||
416 | } | ||
899 | } | 417 | } |
900 | 418 | ||
901 | static void handle_present(struct wl_listener *listener, void *data) { | 419 | static void handle_present(struct wl_listener *listener, void *data) { |
902 | struct sway_output *output = wl_container_of(listener, output, present); | 420 | struct sway_output *output = wl_container_of(listener, output, present); |
903 | struct wlr_output_event_present *output_event = data; | 421 | struct wlr_output_event_present *output_event = data; |
904 | 422 | ||
905 | if (!output->enabled) { | 423 | if (!output->enabled || !output_event->presented) { |
906 | return; | 424 | return; |
907 | } | 425 | } |
908 | 426 | ||
@@ -910,34 +428,90 @@ static void handle_present(struct wl_listener *listener, void *data) { | |||
910 | output->refresh_nsec = output_event->refresh; | 428 | output->refresh_nsec = output_event->refresh; |
911 | } | 429 | } |
912 | 430 | ||
431 | static void handle_request_state(struct wl_listener *listener, void *data) { | ||
432 | struct sway_output *output = | ||
433 | wl_container_of(listener, output, request_state); | ||
434 | const struct wlr_output_event_request_state *event = data; | ||
435 | wlr_output_commit_state(output->wlr_output, event->state); | ||
436 | } | ||
437 | |||
438 | static unsigned int last_headless_num = 0; | ||
439 | |||
913 | void handle_new_output(struct wl_listener *listener, void *data) { | 440 | void handle_new_output(struct wl_listener *listener, void *data) { |
914 | struct sway_server *server = wl_container_of(listener, server, new_output); | 441 | struct sway_server *server = wl_container_of(listener, server, new_output); |
915 | struct wlr_output *wlr_output = data; | 442 | struct wlr_output *wlr_output = data; |
916 | sway_log(SWAY_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); | 443 | |
444 | if (wlr_output == root->fallback_output->wlr_output) { | ||
445 | return; | ||
446 | } | ||
447 | |||
448 | if (wlr_output_is_headless(wlr_output)) { | ||
449 | char name[64]; | ||
450 | snprintf(name, sizeof(name), "HEADLESS-%u", ++last_headless_num); | ||
451 | wlr_output_set_name(wlr_output, name); | ||
452 | } | ||
453 | |||
454 | sway_log(SWAY_DEBUG, "New output %p: %s (non-desktop: %d)", | ||
455 | wlr_output, wlr_output->name, wlr_output->non_desktop); | ||
456 | |||
457 | if (wlr_output->non_desktop) { | ||
458 | sway_log(SWAY_DEBUG, "Not configuring non-desktop output"); | ||
459 | struct sway_output_non_desktop *non_desktop = output_non_desktop_create(wlr_output); | ||
460 | #if WLR_HAS_DRM_BACKEND | ||
461 | if (server->drm_lease_manager) { | ||
462 | wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, | ||
463 | wlr_output); | ||
464 | } | ||
465 | #endif | ||
466 | list_add(root->non_desktop_outputs, non_desktop); | ||
467 | return; | ||
468 | } | ||
469 | |||
470 | if (!wlr_output_init_render(wlr_output, server->allocator, | ||
471 | server->renderer)) { | ||
472 | sway_log(SWAY_ERROR, "Failed to init output render"); | ||
473 | return; | ||
474 | } | ||
475 | |||
476 | // Create the scene output here so we're not accidentally creating one for | ||
477 | // the fallback output | ||
478 | struct wlr_scene_output *scene_output = | ||
479 | wlr_scene_output_create(root->root_scene, wlr_output); | ||
480 | if (!scene_output) { | ||
481 | sway_log(SWAY_ERROR, "Failed to create a scene output"); | ||
482 | return; | ||
483 | } | ||
917 | 484 | ||
918 | struct sway_output *output = output_create(wlr_output); | 485 | struct sway_output *output = output_create(wlr_output); |
919 | if (!output) { | 486 | if (!output) { |
487 | sway_log(SWAY_ERROR, "Failed to create a sway output"); | ||
488 | wlr_scene_output_destroy(scene_output); | ||
920 | return; | 489 | return; |
921 | } | 490 | } |
491 | |||
922 | output->server = server; | 492 | output->server = server; |
923 | output->damage = wlr_output_damage_create(wlr_output); | 493 | output->scene_output = scene_output; |
924 | 494 | ||
495 | wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy); | ||
496 | output->layout_destroy.notify = handle_layout_destroy; | ||
925 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); | 497 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); |
926 | output->destroy.notify = handle_destroy; | 498 | output->destroy.notify = handle_destroy; |
927 | wl_signal_add(&wlr_output->events.commit, &output->commit); | 499 | wl_signal_add(&wlr_output->events.commit, &output->commit); |
928 | output->commit.notify = handle_commit; | 500 | output->commit.notify = handle_commit; |
929 | wl_signal_add(&wlr_output->events.mode, &output->mode); | ||
930 | output->mode.notify = handle_mode; | ||
931 | wl_signal_add(&wlr_output->events.present, &output->present); | 501 | wl_signal_add(&wlr_output->events.present, &output->present); |
932 | output->present.notify = handle_present; | 502 | output->present.notify = handle_present; |
933 | wl_signal_add(&output->damage->events.frame, &output->damage_frame); | 503 | wl_signal_add(&wlr_output->events.frame, &output->frame); |
934 | output->damage_frame.notify = damage_handle_frame; | 504 | output->frame.notify = handle_frame; |
935 | wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); | 505 | wl_signal_add(&wlr_output->events.request_state, &output->request_state); |
936 | output->damage_destroy.notify = damage_handle_destroy; | 506 | output->request_state.notify = handle_request_state; |
937 | 507 | ||
938 | output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, | 508 | output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, |
939 | output_repaint_timer_handler, output); | 509 | output_repaint_timer_handler, output); |
940 | 510 | ||
511 | if (server->session_lock.lock) { | ||
512 | sway_session_lock_add_output(server->session_lock.lock, output); | ||
513 | } | ||
514 | |||
941 | struct output_config *oc = find_output_config(output); | 515 | struct output_config *oc = find_output_config(output); |
942 | apply_output_config(oc, output); | 516 | apply_output_config(oc, output); |
943 | free_output_config(oc); | 517 | free_output_config(oc); |
@@ -954,6 +528,21 @@ void handle_output_layout_change(struct wl_listener *listener, | |||
954 | update_output_manager_config(server); | 528 | update_output_manager_config(server); |
955 | } | 529 | } |
956 | 530 | ||
531 | void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) { | ||
532 | struct sway_server *server = | ||
533 | wl_container_of(listener, server, gamma_control_set_gamma); | ||
534 | const struct wlr_gamma_control_manager_v1_set_gamma_event *event = data; | ||
535 | |||
536 | struct sway_output *output = event->output->data; | ||
537 | |||
538 | if(!output) { | ||
539 | return; | ||
540 | } | ||
541 | |||
542 | output->gamma_lut_changed = true; | ||
543 | wlr_output_schedule_frame(output->wlr_output); | ||
544 | } | ||
545 | |||
957 | static void output_manager_apply(struct sway_server *server, | 546 | static void output_manager_apply(struct sway_server *server, |
958 | struct wlr_output_configuration_v1 *config, bool test_only) { | 547 | struct wlr_output_configuration_v1 *config, bool test_only) { |
959 | // TODO: perform atomic tests on the whole backend atomically | 548 | // TODO: perform atomic tests on the whole backend atomically |
@@ -1002,6 +591,7 @@ static void output_manager_apply(struct sway_server *server, | |||
1002 | oc->y = config_head->state.y; | 591 | oc->y = config_head->state.y; |
1003 | oc->transform = config_head->state.transform; | 592 | oc->transform = config_head->state.transform; |
1004 | oc->scale = config_head->state.scale; | 593 | oc->scale = config_head->state.scale; |
594 | oc->adaptive_sync = config_head->state.adaptive_sync_enabled; | ||
1005 | 595 | ||
1006 | if (test_only) { | 596 | if (test_only) { |
1007 | ok &= test_output_config(oc, output); | 597 | ok &= test_output_config(oc, output); |
@@ -1047,10 +637,10 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, | |||
1047 | struct output_config *oc = new_output_config(output->wlr_output->name); | 637 | struct output_config *oc = new_output_config(output->wlr_output->name); |
1048 | switch (event->mode) { | 638 | switch (event->mode) { |
1049 | case ZWLR_OUTPUT_POWER_V1_MODE_OFF: | 639 | case ZWLR_OUTPUT_POWER_V1_MODE_OFF: |
1050 | oc->dpms_state = DPMS_OFF; | 640 | oc->power = 0; |
1051 | break; | 641 | break; |
1052 | case ZWLR_OUTPUT_POWER_V1_MODE_ON: | 642 | case ZWLR_OUTPUT_POWER_V1_MODE_ON: |
1053 | oc->dpms_state = DPMS_ON; | 643 | oc->power = 1; |
1054 | break; | 644 | break; |
1055 | } | 645 | } |
1056 | oc = store_output_config(oc); | 646 | oc = store_output_config(oc); |