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