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