diff options
author | Drew DeVault <sir@cmpwn.com> | 2018-07-23 20:27:56 -0400 |
---|---|---|
committer | Drew DeVault <sir@cmpwn.com> | 2018-07-23 20:31:11 -0400 |
commit | f4b882475eee7a81c206c7825616cc4656b2f60b (patch) | |
tree | 38e6ebf81b235424f105dcbcbb194e5e9eac70c0 /sway/desktop | |
parent | Implement pid->workspace tracking (diff) | |
parent | Merge pull request #2342 from RyanDwyer/update-cursor (diff) | |
download | sway-f4b882475eee7a81c206c7825616cc4656b2f60b.tar.gz sway-f4b882475eee7a81c206c7825616cc4656b2f60b.tar.zst sway-f4b882475eee7a81c206c7825616cc4656b2f60b.zip |
Merge branch 'master' into pid-workspaces
Diffstat (limited to 'sway/desktop')
-rw-r--r-- | sway/desktop/desktop.c | 9 | ||||
-rw-r--r-- | sway/desktop/idle_inhibit_v1.c | 80 | ||||
-rw-r--r-- | sway/desktop/layer_shell.c | 21 | ||||
-rw-r--r-- | sway/desktop/output.c | 957 | ||||
-rw-r--r-- | sway/desktop/render.c | 919 | ||||
-rw-r--r-- | sway/desktop/transaction.c | 107 | ||||
-rw-r--r-- | sway/desktop/xdg_shell.c | 115 | ||||
-rw-r--r-- | sway/desktop/xdg_shell_v6.c | 115 | ||||
-rw-r--r-- | sway/desktop/xwayland.c | 189 |
9 files changed, 1509 insertions, 1003 deletions
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index e495790c..6575519d 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c | |||
@@ -13,3 +13,12 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, | |||
13 | } | 13 | } |
14 | } | 14 | } |
15 | } | 15 | } |
16 | |||
17 | void desktop_damage_whole_container(struct sway_container *con) { | ||
18 | for (int i = 0; i < root_container.children->length; ++i) { | ||
19 | struct sway_container *cont = root_container.children->items[i]; | ||
20 | if (cont->type == C_OUTPUT) { | ||
21 | output_damage_whole_container(cont->sway_output, con); | ||
22 | } | ||
23 | } | ||
24 | } | ||
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c new file mode 100644 index 00000000..da17d0f2 --- /dev/null +++ b/sway/desktop/idle_inhibit_v1.c | |||
@@ -0,0 +1,80 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <wlr/types/wlr_idle.h> | ||
3 | #include "log.h" | ||
4 | #include "sway/desktop/idle_inhibit_v1.h" | ||
5 | #include "sway/tree/view.h" | ||
6 | #include "sway/server.h" | ||
7 | |||
8 | |||
9 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
10 | struct sway_idle_inhibitor_v1 *inhibitor = | ||
11 | wl_container_of(listener, inhibitor, destroy); | ||
12 | wlr_log(WLR_DEBUG, "Sway idle inhibitor destroyed"); | ||
13 | wl_list_remove(&inhibitor->link); | ||
14 | wl_list_remove(&inhibitor->destroy.link); | ||
15 | idle_inhibit_v1_check_active(inhibitor->manager); | ||
16 | free(inhibitor); | ||
17 | } | ||
18 | |||
19 | void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { | ||
20 | struct wlr_idle_inhibitor_v1 *wlr_inhibitor = data; | ||
21 | struct sway_idle_inhibit_manager_v1 *manager = | ||
22 | wl_container_of(listener, manager, new_idle_inhibitor_v1); | ||
23 | wlr_log(WLR_DEBUG, "New sway idle inhibitor"); | ||
24 | |||
25 | struct sway_idle_inhibitor_v1 *inhibitor = | ||
26 | calloc(1, sizeof(struct sway_idle_inhibitor_v1)); | ||
27 | if (!inhibitor) { | ||
28 | return; | ||
29 | } | ||
30 | |||
31 | inhibitor->manager = manager; | ||
32 | inhibitor->view = view_from_wlr_surface(wlr_inhibitor->surface); | ||
33 | wl_list_insert(&manager->inhibitors, &inhibitor->link); | ||
34 | |||
35 | |||
36 | inhibitor->destroy.notify = handle_destroy; | ||
37 | wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); | ||
38 | |||
39 | idle_inhibit_v1_check_active(manager); | ||
40 | } | ||
41 | |||
42 | void idle_inhibit_v1_check_active( | ||
43 | struct sway_idle_inhibit_manager_v1 *manager) { | ||
44 | struct sway_idle_inhibitor_v1 *inhibitor; | ||
45 | bool inhibited = false; | ||
46 | wl_list_for_each(inhibitor, &manager->inhibitors, link) { | ||
47 | if (!inhibitor->view) { | ||
48 | /* Cannot guess if view is visible so assume it is */ | ||
49 | inhibited = true; | ||
50 | break; | ||
51 | } | ||
52 | if (view_is_visible(inhibitor->view)) { | ||
53 | inhibited = true; | ||
54 | break; | ||
55 | } | ||
56 | } | ||
57 | wlr_idle_set_enabled(manager->idle, NULL, !inhibited); | ||
58 | } | ||
59 | |||
60 | struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( | ||
61 | struct wl_display *wl_display, struct wlr_idle *idle) { | ||
62 | struct sway_idle_inhibit_manager_v1 *manager = | ||
63 | calloc(1, sizeof(struct sway_idle_inhibit_manager_v1)); | ||
64 | if (!manager) { | ||
65 | return NULL; | ||
66 | } | ||
67 | |||
68 | manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display); | ||
69 | if (!manager->wlr_manager) { | ||
70 | free(manager); | ||
71 | return NULL; | ||
72 | } | ||
73 | manager->idle = idle; | ||
74 | wl_signal_add(&manager->wlr_manager->events.new_inhibitor, | ||
75 | &manager->new_idle_inhibitor_v1); | ||
76 | manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; | ||
77 | wl_list_init(&manager->inhibitors); | ||
78 | |||
79 | return manager; | ||
80 | } | ||
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index de1fe349..a7d96717 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c | |||
@@ -12,7 +12,6 @@ | |||
12 | #include "sway/layers.h" | 12 | #include "sway/layers.h" |
13 | #include "sway/output.h" | 13 | #include "sway/output.h" |
14 | #include "sway/server.h" | 14 | #include "sway/server.h" |
15 | #include "sway/tree/arrange.h" | ||
16 | #include "sway/tree/layout.h" | 15 | #include "sway/tree/layout.h" |
17 | #include "log.h" | 16 | #include "log.h" |
18 | 17 | ||
@@ -174,9 +173,9 @@ void arrange_layers(struct sway_output *output) { | |||
174 | 173 | ||
175 | if (memcmp(&usable_area, &output->usable_area, | 174 | if (memcmp(&usable_area, &output->usable_area, |
176 | sizeof(struct wlr_box)) != 0) { | 175 | sizeof(struct wlr_box)) != 0) { |
177 | wlr_log(L_DEBUG, "Usable area changed, rearranging output"); | 176 | wlr_log(WLR_DEBUG, "Usable area changed, rearranging output"); |
178 | memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); | 177 | memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); |
179 | arrange_and_commit(output->swayc); | 178 | container_set_dirty(output->swayc); |
180 | } | 179 | } |
181 | 180 | ||
182 | // Arrange non-exlusive surfaces from top->bottom | 181 | // Arrange non-exlusive surfaces from top->bottom |
@@ -269,7 +268,7 @@ static void unmap(struct sway_layer_surface *sway_layer) { | |||
269 | static void handle_destroy(struct wl_listener *listener, void *data) { | 268 | static void handle_destroy(struct wl_listener *listener, void *data) { |
270 | struct sway_layer_surface *sway_layer = | 269 | struct sway_layer_surface *sway_layer = |
271 | wl_container_of(listener, sway_layer, destroy); | 270 | wl_container_of(listener, sway_layer, destroy); |
272 | wlr_log(L_DEBUG, "Layer surface destroyed (%s)", | 271 | wlr_log(WLR_DEBUG, "Layer surface destroyed (%s)", |
273 | sway_layer->layer_surface->namespace); | 272 | sway_layer->layer_surface->namespace); |
274 | if (sway_layer->layer_surface->mapped) { | 273 | if (sway_layer->layer_surface->mapped) { |
275 | unmap(sway_layer); | 274 | unmap(sway_layer); |
@@ -316,7 +315,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
316 | struct wlr_layer_surface *layer_surface = data; | 315 | struct wlr_layer_surface *layer_surface = data; |
317 | struct sway_server *server = | 316 | struct sway_server *server = |
318 | wl_container_of(listener, server, layer_shell_surface); | 317 | wl_container_of(listener, server, layer_shell_surface); |
319 | wlr_log(L_DEBUG, "new layer surface: namespace %s layer %d anchor %d " | 318 | wlr_log(WLR_DEBUG, "new layer surface: namespace %s layer %d anchor %d " |
320 | "size %dx%d margin %d,%d,%d,%d", | 319 | "size %dx%d margin %d,%d,%d,%d", |
321 | layer_surface->namespace, layer_surface->layer, layer_surface->layer, | 320 | layer_surface->namespace, layer_surface->layer, layer_surface->layer, |
322 | layer_surface->client_pending.desired_width, | 321 | layer_surface->client_pending.desired_width, |
@@ -326,12 +325,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
326 | layer_surface->client_pending.margin.bottom, | 325 | layer_surface->client_pending.margin.bottom, |
327 | layer_surface->client_pending.margin.left); | 326 | layer_surface->client_pending.margin.left); |
328 | 327 | ||
329 | struct sway_layer_surface *sway_layer = | ||
330 | calloc(1, sizeof(struct sway_layer_surface)); | ||
331 | if (!sway_layer) { | ||
332 | return; | ||
333 | } | ||
334 | |||
335 | if (!layer_surface->output) { | 328 | if (!layer_surface->output) { |
336 | // Assign last active output | 329 | // Assign last active output |
337 | struct sway_container *output = NULL; | 330 | struct sway_container *output = NULL; |
@@ -353,6 +346,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
353 | layer_surface->output = output->sway_output->wlr_output; | 346 | layer_surface->output = output->sway_output->wlr_output; |
354 | } | 347 | } |
355 | 348 | ||
349 | struct sway_layer_surface *sway_layer = | ||
350 | calloc(1, sizeof(struct sway_layer_surface)); | ||
351 | if (!sway_layer) { | ||
352 | return; | ||
353 | } | ||
354 | |||
356 | sway_layer->surface_commit.notify = handle_surface_commit; | 355 | sway_layer->surface_commit.notify = handle_surface_commit; |
357 | wl_signal_add(&layer_surface->surface->events.commit, | 356 | wl_signal_add(&layer_surface->surface->events.commit, |
358 | &sway_layer->surface_commit); | 357 | &sway_layer->surface_commit); |
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 1e7494b3..a206ac6b 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c | |||
@@ -56,33 +56,15 @@ static void rotate_child_position(double *sx, double *sy, double sw, double sh, | |||
56 | *sy = ry + ph/2 - sh/2; | 56 | *sy = ry + ph/2 - sh/2; |
57 | } | 57 | } |
58 | 58 | ||
59 | /** | 59 | bool output_get_surface_box(struct root_geometry *geo, |
60 | * Contains a surface's root geometry information. For instance, when rendering | ||
61 | * a popup, this will contain the parent view's position and size. | ||
62 | */ | ||
63 | struct root_geometry { | ||
64 | double x, y; | ||
65 | int width, height; | ||
66 | float rotation; | ||
67 | }; | ||
68 | |||
69 | struct render_data { | ||
70 | struct root_geometry root_geo; | ||
71 | struct sway_output *output; | ||
72 | pixman_region32_t *damage; | ||
73 | struct sway_view *view; | ||
74 | float alpha; | ||
75 | }; | ||
76 | |||
77 | static bool get_surface_box(struct root_geometry *geo, | ||
78 | struct sway_output *output, struct wlr_surface *surface, int sx, int sy, | 60 | struct sway_output *output, struct wlr_surface *surface, int sx, int sy, |
79 | struct wlr_box *surface_box) { | 61 | struct wlr_box *surface_box) { |
80 | if (!wlr_surface_has_buffer(surface)) { | 62 | if (!wlr_surface_has_buffer(surface)) { |
81 | return false; | 63 | return false; |
82 | } | 64 | } |
83 | 65 | ||
84 | int sw = surface->current->width; | 66 | int sw = surface->current.width; |
85 | int sh = surface->current->height; | 67 | int sh = surface->current.height; |
86 | 68 | ||
87 | double _sx = sx, _sy = sy; | 69 | double _sx = sx, _sy = sy; |
88 | rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height, | 70 | rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height, |
@@ -110,24 +92,23 @@ static bool get_surface_box(struct root_geometry *geo, | |||
110 | return wlr_box_intersection(&output_box, &rotated_box, &intersection); | 92 | return wlr_box_intersection(&output_box, &rotated_box, &intersection); |
111 | } | 93 | } |
112 | 94 | ||
113 | static void surface_for_each_surface(struct wlr_surface *surface, | 95 | void output_surface_for_each_surface(struct wlr_surface *surface, |
114 | double ox, double oy, struct root_geometry *geo, | 96 | double ox, double oy, struct root_geometry *geo, |
115 | wlr_surface_iterator_func_t iterator, void *user_data) { | 97 | wlr_surface_iterator_func_t iterator, void *user_data) { |
116 | geo->x = ox; | 98 | geo->x = ox; |
117 | geo->y = oy; | 99 | geo->y = oy; |
118 | geo->width = surface->current->width; | 100 | geo->width = surface->current.width; |
119 | geo->height = surface->current->height; | 101 | geo->height = surface->current.height; |
120 | geo->rotation = 0; | 102 | geo->rotation = 0; |
121 | 103 | ||
122 | wlr_surface_for_each_surface(surface, iterator, user_data); | 104 | wlr_surface_for_each_surface(surface, iterator, user_data); |
123 | } | 105 | } |
124 | 106 | ||
125 | static void output_view_for_each_surface(struct sway_view *view, | 107 | void output_view_for_each_surface(struct sway_view *view, |
126 | struct root_geometry *geo, wlr_surface_iterator_func_t iterator, | 108 | struct sway_output *output, struct root_geometry *geo, |
127 | void *user_data) { | 109 | wlr_surface_iterator_func_t iterator, void *user_data) { |
128 | struct render_data *data = user_data; | 110 | geo->x = view->swayc->current.view_x - output->swayc->current.swayc_x; |
129 | geo->x = view->swayc->current.view_x - data->output->swayc->current.swayc_x; | 111 | geo->y = view->swayc->current.view_y - output->swayc->current.swayc_y; |
130 | geo->y = view->swayc->current.view_y - data->output->swayc->current.swayc_y; | ||
131 | geo->width = view->swayc->current.view_width; | 112 | geo->width = view->swayc->current.view_width; |
132 | geo->height = view->swayc->current.view_height; | 113 | geo->height = view->swayc->current.view_height; |
133 | geo->rotation = 0; // TODO | 114 | geo->rotation = 0; // TODO |
@@ -135,20 +116,20 @@ static void output_view_for_each_surface(struct sway_view *view, | |||
135 | view_for_each_surface(view, iterator, user_data); | 116 | view_for_each_surface(view, iterator, user_data); |
136 | } | 117 | } |
137 | 118 | ||
138 | static void layer_for_each_surface(struct wl_list *layer_surfaces, | 119 | void output_layer_for_each_surface(struct wl_list *layer_surfaces, |
139 | struct root_geometry *geo, wlr_surface_iterator_func_t iterator, | 120 | struct root_geometry *geo, wlr_surface_iterator_func_t iterator, |
140 | void *user_data) { | 121 | void *user_data) { |
141 | struct sway_layer_surface *layer_surface; | 122 | struct sway_layer_surface *layer_surface; |
142 | wl_list_for_each(layer_surface, layer_surfaces, link) { | 123 | wl_list_for_each(layer_surface, layer_surfaces, link) { |
143 | struct wlr_layer_surface *wlr_layer_surface = | 124 | struct wlr_layer_surface *wlr_layer_surface = |
144 | layer_surface->layer_surface; | 125 | layer_surface->layer_surface; |
145 | surface_for_each_surface(wlr_layer_surface->surface, | 126 | output_surface_for_each_surface(wlr_layer_surface->surface, |
146 | layer_surface->geo.x, layer_surface->geo.y, geo, iterator, | 127 | layer_surface->geo.x, layer_surface->geo.y, geo, iterator, |
147 | user_data); | 128 | user_data); |
148 | } | 129 | } |
149 | } | 130 | } |
150 | 131 | ||
151 | static void unmanaged_for_each_surface(struct wl_list *unmanaged, | 132 | void output_unmanaged_for_each_surface(struct wl_list *unmanaged, |
152 | struct sway_output *output, struct root_geometry *geo, | 133 | struct sway_output *output, struct root_geometry *geo, |
153 | wlr_surface_iterator_func_t iterator, void *user_data) { | 134 | wlr_surface_iterator_func_t iterator, void *user_data) { |
154 | struct sway_xwayland_unmanaged *unmanaged_surface; | 135 | struct sway_xwayland_unmanaged *unmanaged_surface; |
@@ -158,12 +139,12 @@ static void unmanaged_for_each_surface(struct wl_list *unmanaged, | |||
158 | double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; | 139 | double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; |
159 | double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; | 140 | double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; |
160 | 141 | ||
161 | surface_for_each_surface(xsurface->surface, ox, oy, geo, | 142 | output_surface_for_each_surface(xsurface->surface, ox, oy, geo, |
162 | iterator, user_data); | 143 | iterator, user_data); |
163 | } | 144 | } |
164 | } | 145 | } |
165 | 146 | ||
166 | static void drag_icons_for_each_surface(struct wl_list *drag_icons, | 147 | void output_drag_icons_for_each_surface(struct wl_list *drag_icons, |
167 | struct sway_output *output, struct root_geometry *geo, | 148 | struct sway_output *output, struct root_geometry *geo, |
168 | wlr_surface_iterator_func_t iterator, void *user_data) { | 149 | wlr_surface_iterator_func_t iterator, void *user_data) { |
169 | struct sway_drag_icon *drag_icon; | 150 | struct sway_drag_icon *drag_icon; |
@@ -172,7 +153,7 @@ static void drag_icons_for_each_surface(struct wl_list *drag_icons, | |||
172 | double oy = drag_icon->y - output->swayc->y; | 153 | double oy = drag_icon->y - output->swayc->y; |
173 | 154 | ||
174 | if (drag_icon->wlr_drag_icon->mapped) { | 155 | if (drag_icon->wlr_drag_icon->mapped) { |
175 | surface_for_each_surface(drag_icon->wlr_drag_icon->surface, | 156 | output_surface_for_each_surface(drag_icon->wlr_drag_icon->surface, |
176 | ox, oy, geo, iterator, user_data); | 157 | ox, oy, geo, iterator, user_data); |
177 | } | 158 | } |
178 | } | 159 | } |
@@ -185,722 +166,7 @@ static void scale_box(struct wlr_box *box, float scale) { | |||
185 | box->height *= scale; | 166 | box->height *= scale; |
186 | } | 167 | } |
187 | 168 | ||
188 | static void scissor_output(struct wlr_output *wlr_output, | 169 | struct sway_container *output_get_active_workspace(struct sway_output *output) { |
189 | pixman_box32_t *rect) { | ||
190 | struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); | ||
191 | assert(renderer); | ||
192 | |||
193 | struct wlr_box box = { | ||
194 | .x = rect->x1, | ||
195 | .y = rect->y1, | ||
196 | .width = rect->x2 - rect->x1, | ||
197 | .height = rect->y2 - rect->y1, | ||
198 | }; | ||
199 | |||
200 | int ow, oh; | ||
201 | wlr_output_transformed_resolution(wlr_output, &ow, &oh); | ||
202 | |||
203 | enum wl_output_transform transform = | ||
204 | wlr_output_transform_invert(wlr_output->transform); | ||
205 | wlr_box_transform(&box, transform, ow, oh, &box); | ||
206 | |||
207 | wlr_renderer_scissor(renderer, &box); | ||
208 | } | ||
209 | |||
210 | static void render_texture(struct wlr_output *wlr_output, | ||
211 | pixman_region32_t *output_damage, struct wlr_texture *texture, | ||
212 | const struct wlr_box *box, const float matrix[static 9], float alpha) { | ||
213 | struct wlr_renderer *renderer = | ||
214 | wlr_backend_get_renderer(wlr_output->backend); | ||
215 | |||
216 | pixman_region32_t damage; | ||
217 | pixman_region32_init(&damage); | ||
218 | pixman_region32_union_rect(&damage, &damage, box->x, box->y, | ||
219 | box->width, box->height); | ||
220 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
221 | bool damaged = pixman_region32_not_empty(&damage); | ||
222 | if (!damaged) { | ||
223 | goto damage_finish; | ||
224 | } | ||
225 | |||
226 | int nrects; | ||
227 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
228 | for (int i = 0; i < nrects; ++i) { | ||
229 | scissor_output(wlr_output, &rects[i]); | ||
230 | wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); | ||
231 | } | ||
232 | |||
233 | damage_finish: | ||
234 | pixman_region32_fini(&damage); | ||
235 | } | ||
236 | |||
237 | static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, | ||
238 | void *_data) { | ||
239 | struct render_data *data = _data; | ||
240 | struct wlr_output *wlr_output = data->output->wlr_output; | ||
241 | float rotation = data->root_geo.rotation; | ||
242 | pixman_region32_t *output_damage = data->damage; | ||
243 | float alpha = data->alpha; | ||
244 | |||
245 | struct wlr_texture *texture = wlr_surface_get_texture(surface); | ||
246 | if (!texture) { | ||
247 | return; | ||
248 | } | ||
249 | |||
250 | struct wlr_box box; | ||
251 | bool intersects = get_surface_box(&data->root_geo, data->output, surface, | ||
252 | sx, sy, &box); | ||
253 | if (!intersects) { | ||
254 | return; | ||
255 | } | ||
256 | |||
257 | scale_box(&box, wlr_output->scale); | ||
258 | |||
259 | float matrix[9]; | ||
260 | enum wl_output_transform transform = | ||
261 | wlr_output_transform_invert(surface->current->transform); | ||
262 | wlr_matrix_project_box(matrix, &box, transform, rotation, | ||
263 | wlr_output->transform_matrix); | ||
264 | |||
265 | render_texture(wlr_output, output_damage, texture, &box, matrix, alpha); | ||
266 | } | ||
267 | |||
268 | static void render_layer(struct sway_output *output, | ||
269 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
270 | struct render_data data = { | ||
271 | .output = output, | ||
272 | .damage = damage, | ||
273 | .alpha = 1.0f, | ||
274 | }; | ||
275 | layer_for_each_surface(layer_surfaces, &data.root_geo, | ||
276 | render_surface_iterator, &data); | ||
277 | } | ||
278 | |||
279 | static void render_unmanaged(struct sway_output *output, | ||
280 | pixman_region32_t *damage, struct wl_list *unmanaged) { | ||
281 | struct render_data data = { | ||
282 | .output = output, | ||
283 | .damage = damage, | ||
284 | .alpha = 1.0f, | ||
285 | }; | ||
286 | unmanaged_for_each_surface(unmanaged, output, &data.root_geo, | ||
287 | render_surface_iterator, &data); | ||
288 | } | ||
289 | |||
290 | static void render_drag_icons(struct sway_output *output, | ||
291 | pixman_region32_t *damage, struct wl_list *drag_icons) { | ||
292 | struct render_data data = { | ||
293 | .output = output, | ||
294 | .damage = damage, | ||
295 | .alpha = 1.0f, | ||
296 | }; | ||
297 | drag_icons_for_each_surface(drag_icons, output, &data.root_geo, | ||
298 | render_surface_iterator, &data); | ||
299 | } | ||
300 | |||
301 | static void render_rect(struct wlr_output *wlr_output, | ||
302 | pixman_region32_t *output_damage, const struct wlr_box *_box, | ||
303 | float color[static 4]) { | ||
304 | struct wlr_renderer *renderer = | ||
305 | wlr_backend_get_renderer(wlr_output->backend); | ||
306 | |||
307 | struct wlr_box box; | ||
308 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
309 | box.x -= wlr_output->lx * wlr_output->scale; | ||
310 | box.y -= wlr_output->ly * wlr_output->scale; | ||
311 | |||
312 | pixman_region32_t damage; | ||
313 | pixman_region32_init(&damage); | ||
314 | pixman_region32_union_rect(&damage, &damage, box.x, box.y, | ||
315 | box.width, box.height); | ||
316 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
317 | bool damaged = pixman_region32_not_empty(&damage); | ||
318 | if (!damaged) { | ||
319 | goto damage_finish; | ||
320 | } | ||
321 | |||
322 | int nrects; | ||
323 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
324 | for (int i = 0; i < nrects; ++i) { | ||
325 | scissor_output(wlr_output, &rects[i]); | ||
326 | wlr_render_rect(renderer, &box, color, | ||
327 | wlr_output->transform_matrix); | ||
328 | } | ||
329 | |||
330 | damage_finish: | ||
331 | pixman_region32_fini(&damage); | ||
332 | } | ||
333 | |||
334 | static void premultiply_alpha(float color[4], float opacity) { | ||
335 | color[3] *= opacity; | ||
336 | color[0] *= color[3]; | ||
337 | color[1] *= color[3]; | ||
338 | color[2] *= color[3]; | ||
339 | } | ||
340 | |||
341 | static void render_view_surfaces(struct sway_view *view, | ||
342 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
343 | struct render_data data = { | ||
344 | .output = output, | ||
345 | .damage = damage, | ||
346 | .view = view, | ||
347 | .alpha = alpha, | ||
348 | }; | ||
349 | output_view_for_each_surface( | ||
350 | view, &data.root_geo, render_surface_iterator, &data); | ||
351 | } | ||
352 | |||
353 | static void render_saved_view(struct sway_view *view, | ||
354 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
355 | struct wlr_output *wlr_output = output->wlr_output; | ||
356 | |||
357 | int width, height; | ||
358 | struct wlr_texture *texture = | ||
359 | transaction_get_saved_texture(view, &width, &height); | ||
360 | if (!texture) { | ||
361 | return; | ||
362 | } | ||
363 | struct wlr_box box = { | ||
364 | .x = view->swayc->current.view_x - output->swayc->current.swayc_x, | ||
365 | .y = view->swayc->current.view_y - output->swayc->current.swayc_y, | ||
366 | .width = width, | ||
367 | .height = height, | ||
368 | }; | ||
369 | |||
370 | struct wlr_box output_box = { | ||
371 | .width = output->swayc->current.swayc_width, | ||
372 | .height = output->swayc->current.swayc_height, | ||
373 | }; | ||
374 | |||
375 | struct wlr_box intersection; | ||
376 | bool intersects = wlr_box_intersection(&output_box, &box, &intersection); | ||
377 | if (!intersects) { | ||
378 | return; | ||
379 | } | ||
380 | |||
381 | scale_box(&box, wlr_output->scale); | ||
382 | |||
383 | float matrix[9]; | ||
384 | wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, | ||
385 | wlr_output->transform_matrix); | ||
386 | |||
387 | render_texture(wlr_output, damage, texture, &box, matrix, alpha); | ||
388 | } | ||
389 | |||
390 | /** | ||
391 | * Render a view's surface and left/bottom/right borders. | ||
392 | */ | ||
393 | static void render_view(struct sway_output *output, pixman_region32_t *damage, | ||
394 | struct sway_container *con, struct border_colors *colors) { | ||
395 | struct sway_view *view = con->sway_view; | ||
396 | if (view->swayc->instructions->length) { | ||
397 | render_saved_view(view, output, damage, view->swayc->alpha); | ||
398 | } else { | ||
399 | render_view_surfaces(view, output, damage, view->swayc->alpha); | ||
400 | } | ||
401 | |||
402 | struct wlr_box box; | ||
403 | float output_scale = output->wlr_output->scale; | ||
404 | float color[4]; | ||
405 | struct sway_container_state *state = &con->current; | ||
406 | |||
407 | if (state->border != B_NONE) { | ||
408 | if (state->border_left) { | ||
409 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
410 | premultiply_alpha(color, con->alpha); | ||
411 | box.x = state->swayc_x; | ||
412 | box.y = state->view_y; | ||
413 | box.width = state->border_thickness; | ||
414 | box.height = state->view_height; | ||
415 | scale_box(&box, output_scale); | ||
416 | render_rect(output->wlr_output, damage, &box, color); | ||
417 | } | ||
418 | |||
419 | if (state->border_right) { | ||
420 | if (state->parent->current.children->length == 1 | ||
421 | && state->parent->current.layout == L_HORIZ) { | ||
422 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
423 | } else { | ||
424 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
425 | } | ||
426 | premultiply_alpha(color, con->alpha); | ||
427 | box.x = state->view_x + state->view_width; | ||
428 | box.y = state->view_y; | ||
429 | box.width = state->border_thickness; | ||
430 | box.height = state->view_height; | ||
431 | scale_box(&box, output_scale); | ||
432 | render_rect(output->wlr_output, damage, &box, color); | ||
433 | } | ||
434 | |||
435 | if (state->border_bottom) { | ||
436 | if (state->parent->current.children->length == 1 | ||
437 | && con->current.parent->current.layout == L_VERT) { | ||
438 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
439 | } else { | ||
440 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
441 | } | ||
442 | premultiply_alpha(color, con->alpha); | ||
443 | box.x = state->swayc_x; | ||
444 | box.y = state->view_y + state->view_height; | ||
445 | box.width = state->swayc_width; | ||
446 | box.height = state->border_thickness; | ||
447 | scale_box(&box, output_scale); | ||
448 | render_rect(output->wlr_output, damage, &box, color); | ||
449 | } | ||
450 | } | ||
451 | } | ||
452 | |||
453 | /** | ||
454 | * Render a titlebar. | ||
455 | * | ||
456 | * Care must be taken not to render over the same pixel multiple times, | ||
457 | * otherwise the colors will be incorrect when using opacity. | ||
458 | * | ||
459 | * The height is: 1px border, 3px padding, font height, 3px padding, 1px border | ||
460 | * The left side for L_TABBED is: 1px border, 2px padding, title | ||
461 | * The left side for other layouts is: 3px padding, title | ||
462 | */ | ||
463 | static void render_titlebar(struct sway_output *output, | ||
464 | pixman_region32_t *output_damage, struct sway_container *con, | ||
465 | int x, int y, int width, | ||
466 | struct border_colors *colors, struct wlr_texture *title_texture, | ||
467 | struct wlr_texture *marks_texture) { | ||
468 | struct wlr_box box; | ||
469 | float color[4]; | ||
470 | struct sway_container_state *state = &con->current; | ||
471 | float output_scale = output->wlr_output->scale; | ||
472 | enum sway_container_layout layout = state->parent->current.layout; | ||
473 | list_t *children = state->parent->current.children; | ||
474 | bool is_last_child = children->items[children->length - 1] == con; | ||
475 | double output_x = output->swayc->current.swayc_x; | ||
476 | double output_y = output->swayc->current.swayc_y; | ||
477 | |||
478 | // Single pixel bar above title | ||
479 | memcpy(&color, colors->border, sizeof(float) * 4); | ||
480 | premultiply_alpha(color, con->alpha); | ||
481 | box.x = x; | ||
482 | box.y = y; | ||
483 | box.width = width; | ||
484 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
485 | scale_box(&box, output_scale); | ||
486 | render_rect(output->wlr_output, output_damage, &box, color); | ||
487 | |||
488 | // Single pixel bar below title | ||
489 | size_t left_offset = 0, right_offset = 0; | ||
490 | bool connects_sides = false; | ||
491 | if (layout == L_HORIZ || layout == L_VERT || | ||
492 | (layout == L_STACKED && is_last_child)) { | ||
493 | if (con->type == C_VIEW) { | ||
494 | left_offset = state->border_left * state->border_thickness; | ||
495 | right_offset = state->border_right * state->border_thickness; | ||
496 | connects_sides = true; | ||
497 | } | ||
498 | } | ||
499 | box.x = x + left_offset; | ||
500 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; | ||
501 | box.width = width - left_offset - right_offset; | ||
502 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
503 | scale_box(&box, output_scale); | ||
504 | render_rect(output->wlr_output, output_damage, &box, color); | ||
505 | |||
506 | if (layout == L_TABBED) { | ||
507 | // Single pixel left edge | ||
508 | box.x = x; | ||
509 | box.y = y + TITLEBAR_BORDER_THICKNESS; | ||
510 | box.width = TITLEBAR_BORDER_THICKNESS; | ||
511 | box.height = | ||
512 | container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2; | ||
513 | scale_box(&box, output_scale); | ||
514 | render_rect(output->wlr_output, output_damage, &box, color); | ||
515 | |||
516 | // Single pixel right edge | ||
517 | box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale; | ||
518 | render_rect(output->wlr_output, output_damage, &box, color); | ||
519 | } | ||
520 | |||
521 | size_t inner_width = width - TITLEBAR_H_PADDING * 2; | ||
522 | |||
523 | // Marks | ||
524 | size_t marks_width = 0; | ||
525 | if (config->show_marks && marks_texture) { | ||
526 | struct wlr_box texture_box; | ||
527 | wlr_texture_get_size(marks_texture, | ||
528 | &texture_box.width, &texture_box.height); | ||
529 | texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING) | ||
530 | * output_scale - texture_box.width; | ||
531 | texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; | ||
532 | |||
533 | float matrix[9]; | ||
534 | wlr_matrix_project_box(matrix, &texture_box, | ||
535 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
536 | 0.0, output->wlr_output->transform_matrix); | ||
537 | |||
538 | if (inner_width * output_scale < texture_box.width) { | ||
539 | texture_box.width = inner_width * output_scale; | ||
540 | } | ||
541 | render_texture(output->wlr_output, output_damage, marks_texture, | ||
542 | &texture_box, matrix, con->alpha); | ||
543 | marks_width = texture_box.width; | ||
544 | } | ||
545 | |||
546 | // Title text | ||
547 | size_t title_width = 0; | ||
548 | if (title_texture) { | ||
549 | struct wlr_box texture_box; | ||
550 | wlr_texture_get_size(title_texture, | ||
551 | &texture_box.width, &texture_box.height); | ||
552 | texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale; | ||
553 | texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; | ||
554 | |||
555 | float matrix[9]; | ||
556 | wlr_matrix_project_box(matrix, &texture_box, | ||
557 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
558 | 0.0, output->wlr_output->transform_matrix); | ||
559 | |||
560 | if (inner_width * output_scale - marks_width < texture_box.width) { | ||
561 | texture_box.width = inner_width * output_scale - marks_width; | ||
562 | } | ||
563 | render_texture(output->wlr_output, output_damage, title_texture, | ||
564 | &texture_box, matrix, con->alpha); | ||
565 | title_width = texture_box.width; | ||
566 | } | ||
567 | |||
568 | // Padding above title | ||
569 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
570 | premultiply_alpha(color, con->alpha); | ||
571 | box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; | ||
572 | box.y = y + TITLEBAR_BORDER_THICKNESS; | ||
573 | box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2; | ||
574 | box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS; | ||
575 | scale_box(&box, output_scale); | ||
576 | render_rect(output->wlr_output, output_damage, &box, color); | ||
577 | |||
578 | // Padding below title | ||
579 | box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale; | ||
580 | render_rect(output->wlr_output, output_damage, &box, color); | ||
581 | |||
582 | // Filler between title and marks | ||
583 | box.width = inner_width * output_scale - title_width - marks_width; | ||
584 | if (box.width > 0) { | ||
585 | box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_width; | ||
586 | box.y = (y + TITLEBAR_V_PADDING) * output_scale; | ||
587 | box.height = config->font_height * output_scale; | ||
588 | render_rect(output->wlr_output, output_damage, &box, color); | ||
589 | } | ||
590 | |||
591 | // Padding left of title | ||
592 | left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; | ||
593 | box.x = x + left_offset; | ||
594 | box.y = y + TITLEBAR_V_PADDING; | ||
595 | box.width = TITLEBAR_H_PADDING - left_offset; | ||
596 | box.height = config->font_height; | ||
597 | scale_box(&box, output_scale); | ||
598 | render_rect(output->wlr_output, output_damage, &box, color); | ||
599 | |||
600 | // Padding right of marks | ||
601 | right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; | ||
602 | box.x = x + width - TITLEBAR_H_PADDING; | ||
603 | box.y = y + TITLEBAR_V_PADDING; | ||
604 | box.width = TITLEBAR_H_PADDING - right_offset; | ||
605 | box.height = config->font_height; | ||
606 | scale_box(&box, output_scale); | ||
607 | render_rect(output->wlr_output, output_damage, &box, color); | ||
608 | |||
609 | if (connects_sides) { | ||
610 | // Left pixel in line with bottom bar | ||
611 | box.x = x; | ||
612 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; | ||
613 | box.width = state->border_thickness * state->border_left; | ||
614 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
615 | scale_box(&box, output_scale); | ||
616 | render_rect(output->wlr_output, output_damage, &box, color); | ||
617 | |||
618 | // Right pixel in line with bottom bar | ||
619 | box.x = x + width - state->border_thickness * state->border_right; | ||
620 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; | ||
621 | box.width = state->border_thickness * state->border_right; | ||
622 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
623 | scale_box(&box, output_scale); | ||
624 | render_rect(output->wlr_output, output_damage, &box, color); | ||
625 | } | ||
626 | } | ||
627 | |||
628 | /** | ||
629 | * Render the top border line for a view using "border pixel". | ||
630 | */ | ||
631 | static void render_top_border(struct sway_output *output, | ||
632 | pixman_region32_t *output_damage, struct sway_container *con, | ||
633 | struct border_colors *colors) { | ||
634 | struct sway_container_state *state = &con->current; | ||
635 | if (!state->border_top) { | ||
636 | return; | ||
637 | } | ||
638 | struct wlr_box box; | ||
639 | float color[4]; | ||
640 | float output_scale = output->wlr_output->scale; | ||
641 | |||
642 | // Child border - top edge | ||
643 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
644 | premultiply_alpha(color, con->alpha); | ||
645 | box.x = state->swayc_x; | ||
646 | box.y = state->swayc_y; | ||
647 | box.width = state->swayc_width; | ||
648 | box.height = state->border_thickness; | ||
649 | scale_box(&box, output_scale); | ||
650 | render_rect(output->wlr_output, output_damage, &box, color); | ||
651 | } | ||
652 | |||
653 | static void render_container(struct sway_output *output, | ||
654 | pixman_region32_t *damage, struct sway_container *con, bool parent_focused); | ||
655 | |||
656 | /** | ||
657 | * Render a container's children using a L_HORIZ or L_VERT layout. | ||
658 | * | ||
659 | * Wrap child views in borders and leave child containers borderless because | ||
660 | * they'll apply their own borders to their children. | ||
661 | */ | ||
662 | static void render_container_simple(struct sway_output *output, | ||
663 | pixman_region32_t *damage, struct sway_container *con, | ||
664 | bool parent_focused) { | ||
665 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
666 | struct sway_container *focus = seat_get_focus(seat); | ||
667 | |||
668 | for (int i = 0; i < con->current.children->length; ++i) { | ||
669 | struct sway_container *child = con->current.children->items[i]; | ||
670 | |||
671 | if (child->type == C_VIEW) { | ||
672 | struct sway_view *view = child->sway_view; | ||
673 | struct border_colors *colors; | ||
674 | struct wlr_texture *title_texture; | ||
675 | struct wlr_texture *marks_texture; | ||
676 | struct sway_container_state *state = &child->current; | ||
677 | |||
678 | if (focus == child || parent_focused) { | ||
679 | colors = &config->border_colors.focused; | ||
680 | title_texture = child->title_focused; | ||
681 | marks_texture = view->marks_focused; | ||
682 | } else if (seat_get_focus_inactive(seat, con) == child) { | ||
683 | colors = &config->border_colors.focused_inactive; | ||
684 | title_texture = child->title_focused_inactive; | ||
685 | marks_texture = view->marks_focused_inactive; | ||
686 | } else { | ||
687 | colors = &config->border_colors.unfocused; | ||
688 | title_texture = child->title_unfocused; | ||
689 | marks_texture = view->marks_unfocused; | ||
690 | } | ||
691 | |||
692 | if (state->border == B_NORMAL) { | ||
693 | render_titlebar(output, damage, child, state->swayc_x, | ||
694 | state->swayc_y, state->swayc_width, colors, | ||
695 | title_texture, marks_texture); | ||
696 | } else { | ||
697 | render_top_border(output, damage, child, colors); | ||
698 | } | ||
699 | render_view(output, damage, child, colors); | ||
700 | } else { | ||
701 | render_container(output, damage, child, | ||
702 | parent_focused || focus == child); | ||
703 | } | ||
704 | } | ||
705 | } | ||
706 | |||
707 | /** | ||
708 | * Render a container's children using the L_TABBED layout. | ||
709 | */ | ||
710 | static void render_container_tabbed(struct sway_output *output, | ||
711 | pixman_region32_t *damage, struct sway_container *con, | ||
712 | bool parent_focused) { | ||
713 | if (!con->current.children->length) { | ||
714 | return; | ||
715 | } | ||
716 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
717 | struct sway_container *focus = seat_get_focus(seat); | ||
718 | struct sway_container *current = seat_get_active_current_child(seat, con); | ||
719 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
720 | struct sway_container_state *pstate = &con->current; | ||
721 | |||
722 | // Render tabs | ||
723 | for (int i = 0; i < con->current.children->length; ++i) { | ||
724 | struct sway_container *child = con->current.children->items[i]; | ||
725 | struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; | ||
726 | struct sway_container_state *cstate = &child->current; | ||
727 | struct border_colors *colors; | ||
728 | struct wlr_texture *title_texture; | ||
729 | struct wlr_texture *marks_texture; | ||
730 | |||
731 | if (focus == child || parent_focused) { | ||
732 | colors = &config->border_colors.focused; | ||
733 | title_texture = child->title_focused; | ||
734 | marks_texture = view ? view->marks_focused : NULL; | ||
735 | } else if (child == current) { | ||
736 | colors = &config->border_colors.focused_inactive; | ||
737 | title_texture = child->title_focused_inactive; | ||
738 | marks_texture = view ? view->marks_focused_inactive : NULL; | ||
739 | } else { | ||
740 | colors = &config->border_colors.unfocused; | ||
741 | title_texture = child->title_unfocused; | ||
742 | marks_texture = view ? view->marks_unfocused : NULL; | ||
743 | } | ||
744 | |||
745 | int tab_width = pstate->swayc_width / pstate->children->length; | ||
746 | int x = pstate->swayc_x + tab_width * i; | ||
747 | // Make last tab use the remaining width of the parent | ||
748 | if (i == pstate->children->length - 1) { | ||
749 | tab_width = pstate->swayc_width - tab_width * i; | ||
750 | } | ||
751 | |||
752 | render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width, | ||
753 | colors, title_texture, marks_texture); | ||
754 | |||
755 | if (child == current) { | ||
756 | current_colors = colors; | ||
757 | } | ||
758 | } | ||
759 | |||
760 | // Render surface and left/right/bottom borders | ||
761 | if (current) { | ||
762 | if (current->type == C_VIEW) { | ||
763 | render_view(output, damage, current, current_colors); | ||
764 | } else { | ||
765 | render_container(output, damage, current, | ||
766 | parent_focused || current == focus); | ||
767 | } | ||
768 | } | ||
769 | } | ||
770 | |||
771 | /** | ||
772 | * Render a container's children using the L_STACKED layout. | ||
773 | */ | ||
774 | static void render_container_stacked(struct sway_output *output, | ||
775 | pixman_region32_t *damage, struct sway_container *con, | ||
776 | bool parent_focused) { | ||
777 | if (!con->current.children->length) { | ||
778 | return; | ||
779 | } | ||
780 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
781 | struct sway_container *focus = seat_get_focus(seat); | ||
782 | struct sway_container *current = seat_get_active_current_child(seat, con); | ||
783 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
784 | struct sway_container_state *pstate = &con->current; | ||
785 | |||
786 | // Render titles | ||
787 | for (int i = 0; i < con->current.children->length; ++i) { | ||
788 | struct sway_container *child = con->current.children->items[i]; | ||
789 | struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; | ||
790 | struct sway_container_state *cstate = &child->current; | ||
791 | struct border_colors *colors; | ||
792 | struct wlr_texture *title_texture; | ||
793 | struct wlr_texture *marks_texture; | ||
794 | |||
795 | if (focus == child || parent_focused) { | ||
796 | colors = &config->border_colors.focused; | ||
797 | title_texture = child->title_focused; | ||
798 | marks_texture = view ? view->marks_focused : NULL; | ||
799 | } else if (child == current) { | ||
800 | colors = &config->border_colors.focused_inactive; | ||
801 | title_texture = child->title_focused_inactive; | ||
802 | marks_texture = view ? view->marks_focused_inactive : NULL; | ||
803 | } else { | ||
804 | colors = &config->border_colors.unfocused; | ||
805 | title_texture = child->title_unfocused; | ||
806 | marks_texture = view ? view->marks_unfocused : NULL; | ||
807 | } | ||
808 | |||
809 | int y = pstate->swayc_y + container_titlebar_height() * i; | ||
810 | render_titlebar(output, damage, child, cstate->swayc_x, y, | ||
811 | cstate->swayc_width, colors, title_texture, marks_texture); | ||
812 | |||
813 | if (child == current) { | ||
814 | current_colors = colors; | ||
815 | } | ||
816 | } | ||
817 | |||
818 | // Render surface and left/right/bottom borders | ||
819 | if (current) { | ||
820 | if (current->type == C_VIEW) { | ||
821 | render_view(output, damage, current, current_colors); | ||
822 | } else { | ||
823 | render_container(output, damage, current, | ||
824 | parent_focused || current == focus); | ||
825 | } | ||
826 | } | ||
827 | } | ||
828 | |||
829 | static void render_container(struct sway_output *output, | ||
830 | pixman_region32_t *damage, struct sway_container *con, | ||
831 | bool parent_focused) { | ||
832 | switch (con->current.layout) { | ||
833 | case L_NONE: | ||
834 | case L_HORIZ: | ||
835 | case L_VERT: | ||
836 | render_container_simple(output, damage, con, parent_focused); | ||
837 | break; | ||
838 | case L_STACKED: | ||
839 | render_container_stacked(output, damage, con, parent_focused); | ||
840 | break; | ||
841 | case L_TABBED: | ||
842 | render_container_tabbed(output, damage, con, parent_focused); | ||
843 | break; | ||
844 | case L_FLOATING: | ||
845 | sway_assert(false, "Didn't expect to see floating here"); | ||
846 | } | ||
847 | } | ||
848 | |||
849 | static void render_floating_container(struct sway_output *soutput, | ||
850 | pixman_region32_t *damage, struct sway_container *con) { | ||
851 | if (con->type == C_VIEW) { | ||
852 | struct sway_view *view = con->sway_view; | ||
853 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
854 | struct sway_container *focus = seat_get_focus(seat); | ||
855 | struct border_colors *colors; | ||
856 | struct wlr_texture *title_texture; | ||
857 | struct wlr_texture *marks_texture; | ||
858 | |||
859 | if (focus == con) { | ||
860 | colors = &config->border_colors.focused; | ||
861 | title_texture = con->title_focused; | ||
862 | marks_texture = view->marks_focused; | ||
863 | } else { | ||
864 | colors = &config->border_colors.unfocused; | ||
865 | title_texture = con->title_unfocused; | ||
866 | marks_texture = view->marks_unfocused; | ||
867 | } | ||
868 | |||
869 | if (con->current.border == B_NORMAL) { | ||
870 | render_titlebar(soutput, damage, con, con->current.swayc_x, | ||
871 | con->current.swayc_y, con->current.swayc_width, colors, | ||
872 | title_texture, marks_texture); | ||
873 | } else if (con->current.border != B_NONE) { | ||
874 | render_top_border(soutput, damage, con, colors); | ||
875 | } | ||
876 | render_view(soutput, damage, con, colors); | ||
877 | } else { | ||
878 | render_container(soutput, damage, con, false); | ||
879 | } | ||
880 | } | ||
881 | |||
882 | static void render_floating(struct sway_output *soutput, | ||
883 | pixman_region32_t *damage) { | ||
884 | for (int i = 0; i < root_container.current.children->length; ++i) { | ||
885 | struct sway_container *output = | ||
886 | root_container.current.children->items[i]; | ||
887 | for (int j = 0; j < output->current.children->length; ++j) { | ||
888 | struct sway_container *ws = output->current.children->items[j]; | ||
889 | if (!workspace_is_visible(ws)) { | ||
890 | continue; | ||
891 | } | ||
892 | list_t *floating = | ||
893 | ws->current.ws_floating->current.children; | ||
894 | for (int k = 0; k < floating->length; ++k) { | ||
895 | struct sway_container *floater = floating->items[k]; | ||
896 | render_floating_container(soutput, damage, floater); | ||
897 | } | ||
898 | } | ||
899 | } | ||
900 | } | ||
901 | |||
902 | static struct sway_container *output_get_active_workspace( | ||
903 | struct sway_output *output) { | ||
904 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 170 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
905 | struct sway_container *focus = | 171 | struct sway_container *focus = |
906 | seat_get_focus_inactive(seat, output->swayc); | 172 | seat_get_focus_inactive(seat, output->swayc); |
@@ -915,117 +181,58 @@ static struct sway_container *output_get_active_workspace( | |||
915 | return workspace; | 181 | return workspace; |
916 | } | 182 | } |
917 | 183 | ||
918 | static void render_output(struct sway_output *output, struct timespec *when, | 184 | bool output_has_opaque_lockscreen(struct sway_output *output, |
919 | pixman_region32_t *damage) { | 185 | struct sway_seat *seat) { |
920 | struct wlr_output *wlr_output = output->wlr_output; | 186 | if (!seat->exclusive_client) { |
921 | 187 | return false; | |
922 | struct wlr_renderer *renderer = | ||
923 | wlr_backend_get_renderer(wlr_output->backend); | ||
924 | if (!sway_assert(renderer != NULL, | ||
925 | "expected the output backend to have a renderer")) { | ||
926 | return; | ||
927 | } | ||
928 | |||
929 | wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); | ||
930 | |||
931 | bool damage_whole_before_swap = false; | ||
932 | if (!pixman_region32_not_empty(damage)) { | ||
933 | // Output isn't damaged but needs buffer swap | ||
934 | goto renderer_end; | ||
935 | } | ||
936 | |||
937 | const char *damage_debug = getenv("SWAY_DAMAGE_DEBUG"); | ||
938 | if (damage_debug != NULL) { | ||
939 | if (strcmp(damage_debug, "highlight") == 0) { | ||
940 | wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); | ||
941 | damage_whole_before_swap = true; | ||
942 | } else if (strcmp(damage_debug, "rerender") == 0) { | ||
943 | int width, height; | ||
944 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
945 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
946 | } | ||
947 | } | 188 | } |
948 | 189 | ||
949 | struct sway_container *workspace = output_get_active_workspace(output); | 190 | struct wlr_layer_surface *wlr_layer_surface; |
950 | struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; | 191 | wl_list_for_each(wlr_layer_surface, &server.layer_shell->surfaces, link) { |
951 | 192 | if (wlr_layer_surface->output != output->wlr_output) { | |
952 | if (fullscreen_view) { | 193 | continue; |
953 | float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; | ||
954 | |||
955 | int nrects; | ||
956 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
957 | for (int i = 0; i < nrects; ++i) { | ||
958 | scissor_output(wlr_output, &rects[i]); | ||
959 | wlr_renderer_clear(renderer, clear_color); | ||
960 | } | 194 | } |
961 | 195 | struct wlr_surface *wlr_surface = wlr_layer_surface->surface; | |
962 | // TODO: handle views smaller than the output | 196 | if (wlr_surface->resource->client != seat->exclusive_client) { |
963 | render_view_surfaces(fullscreen_view, output, damage, 1.0f); | 197 | continue; |
964 | |||
965 | if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { | ||
966 | render_unmanaged(output, damage, | ||
967 | &root_container.sway_root->xwayland_unmanaged); | ||
968 | } | 198 | } |
969 | } else { | 199 | struct sway_layer_surface *sway_layer_surface = |
970 | float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; | 200 | layer_from_wlr_layer_surface(wlr_layer_surface); |
971 | 201 | pixman_box32_t output_box = { | |
972 | int nrects; | 202 | .x2 = output->swayc->current.swayc_width, |
973 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | 203 | .y2 = output->swayc->current.swayc_height, |
974 | for (int i = 0; i < nrects; ++i) { | 204 | }; |
975 | scissor_output(wlr_output, &rects[i]); | 205 | pixman_region32_t surface_opaque_box; |
976 | wlr_renderer_clear(renderer, clear_color); | 206 | pixman_region32_init(&surface_opaque_box); |
207 | pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region); | ||
208 | pixman_region32_translate(&surface_opaque_box, | ||
209 | sway_layer_surface->geo.x, sway_layer_surface->geo.y); | ||
210 | bool contains = pixman_region32_contains_rectangle(&surface_opaque_box, | ||
211 | &output_box); | ||
212 | pixman_region32_fini(&surface_opaque_box); | ||
213 | if (contains) { | ||
214 | return true; | ||
977 | } | 215 | } |
978 | |||
979 | render_layer(output, damage, | ||
980 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
981 | render_layer(output, damage, | ||
982 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
983 | |||
984 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
985 | struct sway_container *focus = seat_get_focus(seat); | ||
986 | render_container(output, damage, workspace, focus == workspace); | ||
987 | render_floating(output, damage); | ||
988 | |||
989 | render_unmanaged(output, damage, | ||
990 | &root_container.sway_root->xwayland_unmanaged); | ||
991 | render_layer(output, damage, | ||
992 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
993 | } | ||
994 | render_layer(output, damage, | ||
995 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
996 | render_drag_icons(output, damage, &root_container.sway_root->drag_icons); | ||
997 | |||
998 | renderer_end: | ||
999 | if (root_container.sway_root->debug_tree) { | ||
1000 | wlr_render_texture(renderer, root_container.sway_root->debug_tree, | ||
1001 | wlr_output->transform_matrix, 0, 0, 1); | ||
1002 | } | ||
1003 | |||
1004 | if (damage_whole_before_swap || root_container.sway_root->debug_tree) { | ||
1005 | int width, height; | ||
1006 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
1007 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
1008 | } | 216 | } |
1009 | 217 | return false; | |
1010 | wlr_renderer_scissor(renderer, NULL); | ||
1011 | wlr_renderer_end(renderer); | ||
1012 | if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { | ||
1013 | return; | ||
1014 | } | ||
1015 | output->last_frame = *when; | ||
1016 | } | 218 | } |
1017 | 219 | ||
1018 | struct send_frame_done_data { | 220 | struct send_frame_done_data { |
1019 | struct root_geometry root_geo; | 221 | struct root_geometry root_geo; |
1020 | struct sway_output *output; | 222 | struct sway_output *output; |
1021 | struct timespec *when; | 223 | struct timespec *when; |
224 | struct wl_client *exclusive_client; | ||
1022 | }; | 225 | }; |
1023 | 226 | ||
1024 | static void send_frame_done_iterator(struct wlr_surface *surface, | 227 | static void send_frame_done_iterator(struct wlr_surface *surface, |
1025 | int sx, int sy, void *_data) { | 228 | int sx, int sy, void *_data) { |
1026 | struct send_frame_done_data *data = _data; | 229 | struct send_frame_done_data *data = _data; |
230 | if (data->exclusive_client && | ||
231 | data->exclusive_client != surface->resource->client) { | ||
232 | return; | ||
233 | } | ||
1027 | 234 | ||
1028 | bool intersects = get_surface_box(&data->root_geo, data->output, surface, | 235 | bool intersects = output_get_surface_box(&data->root_geo, data->output, surface, |
1029 | sx, sy, NULL); | 236 | sx, sy, NULL); |
1030 | if (intersects) { | 237 | if (intersects) { |
1031 | wlr_surface_send_frame_done(surface, data->when); | 238 | wlr_surface_send_frame_done(surface, data->when); |
@@ -1034,19 +241,19 @@ static void send_frame_done_iterator(struct wlr_surface *surface, | |||
1034 | 241 | ||
1035 | static void send_frame_done_layer(struct send_frame_done_data *data, | 242 | static void send_frame_done_layer(struct send_frame_done_data *data, |
1036 | struct wl_list *layer_surfaces) { | 243 | struct wl_list *layer_surfaces) { |
1037 | layer_for_each_surface(layer_surfaces, &data->root_geo, | 244 | output_layer_for_each_surface(layer_surfaces, &data->root_geo, |
1038 | send_frame_done_iterator, data); | 245 | send_frame_done_iterator, data); |
1039 | } | 246 | } |
1040 | 247 | ||
1041 | static void send_frame_done_unmanaged(struct send_frame_done_data *data, | 248 | static void send_frame_done_unmanaged(struct send_frame_done_data *data, |
1042 | struct wl_list *unmanaged) { | 249 | struct wl_list *unmanaged) { |
1043 | unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo, | 250 | output_unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo, |
1044 | send_frame_done_iterator, data); | 251 | send_frame_done_iterator, data); |
1045 | } | 252 | } |
1046 | 253 | ||
1047 | static void send_frame_done_drag_icons(struct send_frame_done_data *data, | 254 | static void send_frame_done_drag_icons(struct send_frame_done_data *data, |
1048 | struct wl_list *drag_icons) { | 255 | struct wl_list *drag_icons) { |
1049 | drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo, | 256 | output_drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo, |
1050 | send_frame_done_iterator, data); | 257 | send_frame_done_iterator, data); |
1051 | } | 258 | } |
1052 | 259 | ||
@@ -1061,7 +268,7 @@ static void send_frame_done_container_iterator(struct sway_container *con, | |||
1061 | return; | 268 | return; |
1062 | } | 269 | } |
1063 | 270 | ||
1064 | output_view_for_each_surface(con->sway_view, &data->root_geo, | 271 | output_view_for_each_surface(con->sway_view, data->output, &data->root_geo, |
1065 | send_frame_done_iterator, data); | 272 | send_frame_done_iterator, data); |
1066 | } | 273 | } |
1067 | 274 | ||
@@ -1072,9 +279,12 @@ static void send_frame_done_container(struct send_frame_done_data *data, | |||
1072 | } | 279 | } |
1073 | 280 | ||
1074 | static void send_frame_done(struct sway_output *output, struct timespec *when) { | 281 | static void send_frame_done(struct sway_output *output, struct timespec *when) { |
282 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
1075 | struct send_frame_done_data data = { | 283 | struct send_frame_done_data data = { |
1076 | .output = output, | 284 | .output = output, |
1077 | .when = when, | 285 | .when = when, |
286 | .exclusive_client = output_has_opaque_lockscreen(output, seat) ? | ||
287 | seat->exclusive_client : NULL, | ||
1078 | }; | 288 | }; |
1079 | 289 | ||
1080 | struct sway_container *workspace = output_get_active_workspace(output); | 290 | struct sway_container *workspace = output_get_active_workspace(output); |
@@ -1125,7 +335,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) { | |||
1125 | } | 335 | } |
1126 | 336 | ||
1127 | if (needs_swap) { | 337 | if (needs_swap) { |
1128 | render_output(output, &now, &damage); | 338 | output_render(output, &now, &damage); |
1129 | } | 339 | } |
1130 | 340 | ||
1131 | pixman_region32_fini(&damage); | 341 | pixman_region32_fini(&damage); |
@@ -1152,7 +362,7 @@ static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy, | |||
1152 | bool whole = data->whole; | 362 | bool whole = data->whole; |
1153 | 363 | ||
1154 | struct wlr_box box; | 364 | struct wlr_box box; |
1155 | bool intersects = get_surface_box(&data->root_geo, data->output, surface, | 365 | bool intersects = output_get_surface_box(&data->root_geo, data->output, surface, |
1156 | sx, sy, &box); | 366 | sx, sy, &box); |
1157 | if (!intersects) { | 367 | if (!intersects) { |
1158 | return; | 368 | return; |
@@ -1163,16 +373,22 @@ static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy, | |||
1163 | int center_x = box.x + box.width/2; | 373 | int center_x = box.x + box.width/2; |
1164 | int center_y = box.y + box.height/2; | 374 | int center_y = box.y + box.height/2; |
1165 | 375 | ||
1166 | if (pixman_region32_not_empty(&surface->current->surface_damage)) { | 376 | if (pixman_region32_not_empty(&surface->buffer_damage)) { |
377 | enum wl_output_transform transform = | ||
378 | wlr_output_transform_invert(surface->current.transform); | ||
379 | |||
1167 | pixman_region32_t damage; | 380 | pixman_region32_t damage; |
1168 | pixman_region32_init(&damage); | 381 | pixman_region32_init(&damage); |
1169 | pixman_region32_copy(&damage, &surface->current->surface_damage); | 382 | pixman_region32_copy(&damage, &surface->buffer_damage); |
1170 | wlr_region_scale(&damage, &damage, output->wlr_output->scale); | 383 | wlr_region_transform(&damage, &damage, transform, |
1171 | if (ceil(output->wlr_output->scale) > surface->current->scale) { | 384 | surface->current.buffer_width, surface->current.buffer_height); |
385 | wlr_region_scale(&damage, &damage, | ||
386 | output->wlr_output->scale / (float)surface->current.scale); | ||
387 | if (ceil(output->wlr_output->scale) > surface->current.scale) { | ||
1172 | // When scaling up a surface, it'll become blurry so we need to | 388 | // When scaling up a surface, it'll become blurry so we need to |
1173 | // expand the damage region | 389 | // expand the damage region |
1174 | wlr_region_expand(&damage, &damage, | 390 | wlr_region_expand(&damage, &damage, |
1175 | ceil(output->wlr_output->scale) - surface->current->scale); | 391 | ceil(output->wlr_output->scale) - surface->current.scale); |
1176 | } | 392 | } |
1177 | pixman_region32_translate(&damage, box.x, box.y); | 393 | pixman_region32_translate(&damage, box.x, box.y); |
1178 | wlr_region_rotated_bounds(&damage, &damage, rotation, | 394 | wlr_region_rotated_bounds(&damage, &damage, rotation, |
@@ -1196,7 +412,7 @@ void output_damage_surface(struct sway_output *output, double ox, double oy, | |||
1196 | .whole = whole, | 412 | .whole = whole, |
1197 | }; | 413 | }; |
1198 | 414 | ||
1199 | surface_for_each_surface(surface, ox, oy, &data.root_geo, | 415 | output_surface_for_each_surface(surface, ox, oy, &data.root_geo, |
1200 | damage_surface_iterator, &data); | 416 | damage_surface_iterator, &data); |
1201 | } | 417 | } |
1202 | 418 | ||
@@ -1215,7 +431,7 @@ static void output_damage_view(struct sway_output *output, | |||
1215 | .whole = whole, | 431 | .whole = whole, |
1216 | }; | 432 | }; |
1217 | 433 | ||
1218 | output_view_for_each_surface(view, &data.root_geo, | 434 | output_view_for_each_surface(view, output, &data.root_geo, |
1219 | damage_surface_iterator, &data); | 435 | damage_surface_iterator, &data); |
1220 | } | 436 | } |
1221 | 437 | ||
@@ -1247,11 +463,12 @@ static void output_damage_whole_container_iterator(struct sway_container *con, | |||
1247 | 463 | ||
1248 | void output_damage_whole_container(struct sway_output *output, | 464 | void output_damage_whole_container(struct sway_output *output, |
1249 | struct sway_container *con) { | 465 | struct sway_container *con) { |
466 | // Pad the box by 1px, because the width is a double and might be a fraction | ||
1250 | struct wlr_box box = { | 467 | struct wlr_box box = { |
1251 | .x = con->current.swayc_x - output->wlr_output->lx, | 468 | .x = con->current.swayc_x - output->wlr_output->lx - 1, |
1252 | .y = con->current.swayc_y - output->wlr_output->ly, | 469 | .y = con->current.swayc_y - output->wlr_output->ly - 1, |
1253 | .width = con->current.swayc_width, | 470 | .width = con->current.swayc_width + 2, |
1254 | .height = con->current.swayc_height, | 471 | .height = con->current.swayc_height + 2, |
1255 | }; | 472 | }; |
1256 | scale_box(&box, output->wlr_output->scale); | 473 | scale_box(&box, output->wlr_output->scale); |
1257 | wlr_output_damage_add_box(output->damage, &box); | 474 | wlr_output_damage_add_box(output->damage, &box); |
@@ -1276,19 +493,21 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
1276 | output->wlr_output->data = NULL; | 493 | output->wlr_output->data = NULL; |
1277 | free(output); | 494 | free(output); |
1278 | 495 | ||
1279 | arrange_and_commit(&root_container); | 496 | arrange_windows(&root_container); |
1280 | } | 497 | } |
1281 | 498 | ||
1282 | static void handle_mode(struct wl_listener *listener, void *data) { | 499 | static void handle_mode(struct wl_listener *listener, void *data) { |
1283 | struct sway_output *output = wl_container_of(listener, output, mode); | 500 | struct sway_output *output = wl_container_of(listener, output, mode); |
1284 | arrange_layers(output); | 501 | arrange_layers(output); |
1285 | arrange_and_commit(output->swayc); | 502 | arrange_windows(output->swayc); |
503 | transaction_commit_dirty(); | ||
1286 | } | 504 | } |
1287 | 505 | ||
1288 | static void handle_transform(struct wl_listener *listener, void *data) { | 506 | static void handle_transform(struct wl_listener *listener, void *data) { |
1289 | struct sway_output *output = wl_container_of(listener, output, transform); | 507 | struct sway_output *output = wl_container_of(listener, output, transform); |
1290 | arrange_layers(output); | 508 | arrange_layers(output); |
1291 | arrange_and_commit(output->swayc); | 509 | arrange_windows(output->swayc); |
510 | transaction_commit_dirty(); | ||
1292 | } | 511 | } |
1293 | 512 | ||
1294 | static void handle_scale_iterator(struct sway_container *view, void *data) { | 513 | static void handle_scale_iterator(struct sway_container *view, void *data) { |
@@ -1299,7 +518,8 @@ static void handle_scale(struct wl_listener *listener, void *data) { | |||
1299 | struct sway_output *output = wl_container_of(listener, output, scale); | 518 | struct sway_output *output = wl_container_of(listener, output, scale); |
1300 | arrange_layers(output); | 519 | arrange_layers(output); |
1301 | container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); | 520 | container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); |
1302 | arrange_and_commit(output->swayc); | 521 | arrange_windows(output->swayc); |
522 | transaction_commit_dirty(); | ||
1303 | } | 523 | } |
1304 | 524 | ||
1305 | struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { | 525 | struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { |
@@ -1309,7 +529,7 @@ struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { | |||
1309 | void handle_new_output(struct wl_listener *listener, void *data) { | 529 | void handle_new_output(struct wl_listener *listener, void *data) { |
1310 | struct sway_server *server = wl_container_of(listener, server, new_output); | 530 | struct sway_server *server = wl_container_of(listener, server, new_output); |
1311 | struct wlr_output *wlr_output = data; | 531 | struct wlr_output *wlr_output = data; |
1312 | wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); | 532 | wlr_log(WLR_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); |
1313 | 533 | ||
1314 | struct sway_output *output = calloc(1, sizeof(struct sway_output)); | 534 | struct sway_output *output = calloc(1, sizeof(struct sway_output)); |
1315 | if (!output) { | 535 | if (!output) { |
@@ -1368,5 +588,6 @@ void output_enable(struct sway_output *output) { | |||
1368 | output->damage_destroy.notify = damage_handle_destroy; | 588 | output->damage_destroy.notify = damage_handle_destroy; |
1369 | 589 | ||
1370 | arrange_layers(output); | 590 | arrange_layers(output); |
1371 | arrange_and_commit(&root_container); | 591 | arrange_windows(&root_container); |
592 | transaction_commit_dirty(); | ||
1372 | } | 593 | } |
diff --git a/sway/desktop/render.c b/sway/desktop/render.c new file mode 100644 index 00000000..7da54594 --- /dev/null +++ b/sway/desktop/render.c | |||
@@ -0,0 +1,919 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <strings.h> | ||
5 | #include <time.h> | ||
6 | #include <wayland-server.h> | ||
7 | #include <wlr/render/wlr_renderer.h> | ||
8 | #include <wlr/types/wlr_box.h> | ||
9 | #include <wlr/types/wlr_buffer.h> | ||
10 | #include <wlr/types/wlr_matrix.h> | ||
11 | #include <wlr/types/wlr_output_damage.h> | ||
12 | #include <wlr/types/wlr_output_layout.h> | ||
13 | #include <wlr/types/wlr_output.h> | ||
14 | #include <wlr/types/wlr_surface.h> | ||
15 | #include <wlr/util/region.h> | ||
16 | #include "log.h" | ||
17 | #include "sway/config.h" | ||
18 | #include "sway/debug.h" | ||
19 | #include "sway/input/input-manager.h" | ||
20 | #include "sway/input/seat.h" | ||
21 | #include "sway/layers.h" | ||
22 | #include "sway/output.h" | ||
23 | #include "sway/server.h" | ||
24 | #include "sway/tree/arrange.h" | ||
25 | #include "sway/tree/container.h" | ||
26 | #include "sway/tree/layout.h" | ||
27 | #include "sway/tree/view.h" | ||
28 | #include "sway/tree/workspace.h" | ||
29 | |||
30 | struct render_data { | ||
31 | struct root_geometry root_geo; | ||
32 | struct sway_output *output; | ||
33 | pixman_region32_t *damage; | ||
34 | struct sway_view *view; | ||
35 | float alpha; | ||
36 | }; | ||
37 | |||
38 | static void scale_box(struct wlr_box *box, float scale) { | ||
39 | box->x *= scale; | ||
40 | box->y *= scale; | ||
41 | box->width *= scale; | ||
42 | box->height *= scale; | ||
43 | } | ||
44 | |||
45 | static void scissor_output(struct wlr_output *wlr_output, | ||
46 | pixman_box32_t *rect) { | ||
47 | struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); | ||
48 | assert(renderer); | ||
49 | |||
50 | struct wlr_box box = { | ||
51 | .x = rect->x1, | ||
52 | .y = rect->y1, | ||
53 | .width = rect->x2 - rect->x1, | ||
54 | .height = rect->y2 - rect->y1, | ||
55 | }; | ||
56 | |||
57 | int ow, oh; | ||
58 | wlr_output_transformed_resolution(wlr_output, &ow, &oh); | ||
59 | |||
60 | enum wl_output_transform transform = | ||
61 | wlr_output_transform_invert(wlr_output->transform); | ||
62 | wlr_box_transform(&box, transform, ow, oh, &box); | ||
63 | |||
64 | wlr_renderer_scissor(renderer, &box); | ||
65 | } | ||
66 | |||
67 | static void render_texture(struct wlr_output *wlr_output, | ||
68 | pixman_region32_t *output_damage, struct wlr_texture *texture, | ||
69 | const struct wlr_box *box, const float matrix[static 9], float alpha) { | ||
70 | struct wlr_renderer *renderer = | ||
71 | wlr_backend_get_renderer(wlr_output->backend); | ||
72 | |||
73 | pixman_region32_t damage; | ||
74 | pixman_region32_init(&damage); | ||
75 | pixman_region32_union_rect(&damage, &damage, box->x, box->y, | ||
76 | box->width, box->height); | ||
77 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
78 | bool damaged = pixman_region32_not_empty(&damage); | ||
79 | if (!damaged) { | ||
80 | goto damage_finish; | ||
81 | } | ||
82 | |||
83 | int nrects; | ||
84 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
85 | for (int i = 0; i < nrects; ++i) { | ||
86 | scissor_output(wlr_output, &rects[i]); | ||
87 | wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); | ||
88 | } | ||
89 | |||
90 | damage_finish: | ||
91 | pixman_region32_fini(&damage); | ||
92 | } | ||
93 | |||
94 | static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, | ||
95 | void *_data) { | ||
96 | struct render_data *data = _data; | ||
97 | struct wlr_output *wlr_output = data->output->wlr_output; | ||
98 | float rotation = data->root_geo.rotation; | ||
99 | pixman_region32_t *output_damage = data->damage; | ||
100 | float alpha = data->alpha; | ||
101 | |||
102 | struct wlr_texture *texture = wlr_surface_get_texture(surface); | ||
103 | if (!texture) { | ||
104 | return; | ||
105 | } | ||
106 | |||
107 | struct wlr_box box; | ||
108 | bool intersects = output_get_surface_box(&data->root_geo, data->output, | ||
109 | surface, sx, sy, &box); | ||
110 | if (!intersects) { | ||
111 | return; | ||
112 | } | ||
113 | |||
114 | scale_box(&box, wlr_output->scale); | ||
115 | |||
116 | float matrix[9]; | ||
117 | enum wl_output_transform transform = | ||
118 | wlr_output_transform_invert(surface->current.transform); | ||
119 | wlr_matrix_project_box(matrix, &box, transform, rotation, | ||
120 | wlr_output->transform_matrix); | ||
121 | |||
122 | render_texture(wlr_output, output_damage, texture, &box, matrix, alpha); | ||
123 | } | ||
124 | |||
125 | static void render_layer(struct sway_output *output, | ||
126 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
127 | struct render_data data = { | ||
128 | .output = output, | ||
129 | .damage = damage, | ||
130 | .alpha = 1.0f, | ||
131 | }; | ||
132 | output_layer_for_each_surface(layer_surfaces, &data.root_geo, | ||
133 | render_surface_iterator, &data); | ||
134 | } | ||
135 | |||
136 | static void render_unmanaged(struct sway_output *output, | ||
137 | pixman_region32_t *damage, struct wl_list *unmanaged) { | ||
138 | struct render_data data = { | ||
139 | .output = output, | ||
140 | .damage = damage, | ||
141 | .alpha = 1.0f, | ||
142 | }; | ||
143 | output_unmanaged_for_each_surface(unmanaged, output, &data.root_geo, | ||
144 | render_surface_iterator, &data); | ||
145 | } | ||
146 | |||
147 | static void render_drag_icons(struct sway_output *output, | ||
148 | pixman_region32_t *damage, struct wl_list *drag_icons) { | ||
149 | struct render_data data = { | ||
150 | .output = output, | ||
151 | .damage = damage, | ||
152 | .alpha = 1.0f, | ||
153 | }; | ||
154 | output_drag_icons_for_each_surface(drag_icons, output, &data.root_geo, | ||
155 | render_surface_iterator, &data); | ||
156 | } | ||
157 | |||
158 | static void render_rect(struct wlr_output *wlr_output, | ||
159 | pixman_region32_t *output_damage, const struct wlr_box *_box, | ||
160 | float color[static 4]) { | ||
161 | struct wlr_renderer *renderer = | ||
162 | wlr_backend_get_renderer(wlr_output->backend); | ||
163 | |||
164 | struct wlr_box box; | ||
165 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
166 | box.x -= wlr_output->lx * wlr_output->scale; | ||
167 | box.y -= wlr_output->ly * wlr_output->scale; | ||
168 | |||
169 | pixman_region32_t damage; | ||
170 | pixman_region32_init(&damage); | ||
171 | pixman_region32_union_rect(&damage, &damage, box.x, box.y, | ||
172 | box.width, box.height); | ||
173 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
174 | bool damaged = pixman_region32_not_empty(&damage); | ||
175 | if (!damaged) { | ||
176 | goto damage_finish; | ||
177 | } | ||
178 | |||
179 | int nrects; | ||
180 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
181 | for (int i = 0; i < nrects; ++i) { | ||
182 | scissor_output(wlr_output, &rects[i]); | ||
183 | wlr_render_rect(renderer, &box, color, | ||
184 | wlr_output->transform_matrix); | ||
185 | } | ||
186 | |||
187 | damage_finish: | ||
188 | pixman_region32_fini(&damage); | ||
189 | } | ||
190 | |||
191 | static void premultiply_alpha(float color[4], float opacity) { | ||
192 | color[3] *= opacity; | ||
193 | color[0] *= color[3]; | ||
194 | color[1] *= color[3]; | ||
195 | color[2] *= color[3]; | ||
196 | } | ||
197 | |||
198 | static void render_view_surfaces(struct sway_view *view, | ||
199 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
200 | struct render_data data = { | ||
201 | .output = output, | ||
202 | .damage = damage, | ||
203 | .view = view, | ||
204 | .alpha = alpha, | ||
205 | }; | ||
206 | output_view_for_each_surface(view, output, &data.root_geo, | ||
207 | render_surface_iterator, &data); | ||
208 | } | ||
209 | |||
210 | static void render_saved_view(struct sway_view *view, | ||
211 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
212 | struct wlr_output *wlr_output = output->wlr_output; | ||
213 | |||
214 | int width, height; | ||
215 | struct wlr_texture *texture = | ||
216 | transaction_get_saved_texture(view, &width, &height); | ||
217 | if (!texture) { | ||
218 | return; | ||
219 | } | ||
220 | struct wlr_box box = { | ||
221 | .x = view->swayc->current.view_x - output->swayc->current.swayc_x, | ||
222 | .y = view->swayc->current.view_y - output->swayc->current.swayc_y, | ||
223 | .width = width, | ||
224 | .height = height, | ||
225 | }; | ||
226 | |||
227 | struct wlr_box output_box = { | ||
228 | .width = output->swayc->current.swayc_width, | ||
229 | .height = output->swayc->current.swayc_height, | ||
230 | }; | ||
231 | |||
232 | struct wlr_box intersection; | ||
233 | bool intersects = wlr_box_intersection(&output_box, &box, &intersection); | ||
234 | if (!intersects) { | ||
235 | return; | ||
236 | } | ||
237 | |||
238 | scale_box(&box, wlr_output->scale); | ||
239 | |||
240 | float matrix[9]; | ||
241 | wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, | ||
242 | wlr_output->transform_matrix); | ||
243 | |||
244 | render_texture(wlr_output, damage, texture, &box, matrix, alpha); | ||
245 | } | ||
246 | |||
247 | /** | ||
248 | * Render a view's surface and left/bottom/right borders. | ||
249 | */ | ||
250 | static void render_view(struct sway_output *output, pixman_region32_t *damage, | ||
251 | struct sway_container *con, struct border_colors *colors) { | ||
252 | struct sway_view *view = con->sway_view; | ||
253 | if (view->swayc->instructions->length) { | ||
254 | render_saved_view(view, output, damage, view->swayc->alpha); | ||
255 | } else { | ||
256 | render_view_surfaces(view, output, damage, view->swayc->alpha); | ||
257 | } | ||
258 | |||
259 | if (view->using_csd) { | ||
260 | return; | ||
261 | } | ||
262 | |||
263 | struct wlr_box box; | ||
264 | float output_scale = output->wlr_output->scale; | ||
265 | float color[4]; | ||
266 | struct sway_container_state *state = &con->current; | ||
267 | |||
268 | if (state->border != B_NONE) { | ||
269 | if (state->border_left) { | ||
270 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
271 | premultiply_alpha(color, con->alpha); | ||
272 | box.x = state->swayc_x; | ||
273 | box.y = state->view_y; | ||
274 | box.width = state->border_thickness; | ||
275 | box.height = state->view_height; | ||
276 | scale_box(&box, output_scale); | ||
277 | render_rect(output->wlr_output, damage, &box, color); | ||
278 | } | ||
279 | |||
280 | if (state->border_right) { | ||
281 | if (state->parent->current.children->length == 1 | ||
282 | && state->parent->current.layout == L_HORIZ) { | ||
283 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
284 | } else { | ||
285 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
286 | } | ||
287 | premultiply_alpha(color, con->alpha); | ||
288 | box.x = state->view_x + state->view_width; | ||
289 | box.y = state->view_y; | ||
290 | box.width = state->border_thickness; | ||
291 | box.height = state->view_height; | ||
292 | scale_box(&box, output_scale); | ||
293 | render_rect(output->wlr_output, damage, &box, color); | ||
294 | } | ||
295 | |||
296 | if (state->border_bottom) { | ||
297 | if (state->parent->current.children->length == 1 | ||
298 | && con->current.parent->current.layout == L_VERT) { | ||
299 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
300 | } else { | ||
301 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
302 | } | ||
303 | premultiply_alpha(color, con->alpha); | ||
304 | box.x = state->swayc_x; | ||
305 | box.y = state->view_y + state->view_height; | ||
306 | box.width = state->swayc_width; | ||
307 | box.height = state->border_thickness; | ||
308 | scale_box(&box, output_scale); | ||
309 | render_rect(output->wlr_output, damage, &box, color); | ||
310 | } | ||
311 | } | ||
312 | } | ||
313 | |||
314 | /** | ||
315 | * Render a titlebar. | ||
316 | * | ||
317 | * Care must be taken not to render over the same pixel multiple times, | ||
318 | * otherwise the colors will be incorrect when using opacity. | ||
319 | * | ||
320 | * The height is: 1px border, 3px padding, font height, 3px padding, 1px border | ||
321 | * The left side for L_TABBED is: 1px border, 2px padding, title | ||
322 | * The left side for other layouts is: 3px padding, title | ||
323 | */ | ||
324 | static void render_titlebar(struct sway_output *output, | ||
325 | pixman_region32_t *output_damage, struct sway_container *con, | ||
326 | int x, int y, int width, | ||
327 | struct border_colors *colors, struct wlr_texture *title_texture, | ||
328 | struct wlr_texture *marks_texture) { | ||
329 | struct wlr_box box; | ||
330 | float color[4]; | ||
331 | struct sway_container_state *state = &con->current; | ||
332 | float output_scale = output->wlr_output->scale; | ||
333 | enum sway_container_layout layout = state->parent->current.layout; | ||
334 | list_t *children = state->parent->current.children; | ||
335 | bool is_last_child = children->items[children->length - 1] == con; | ||
336 | double output_x = output->swayc->current.swayc_x; | ||
337 | double output_y = output->swayc->current.swayc_y; | ||
338 | |||
339 | // Single pixel bar above title | ||
340 | memcpy(&color, colors->border, sizeof(float) * 4); | ||
341 | premultiply_alpha(color, con->alpha); | ||
342 | box.x = x; | ||
343 | box.y = y; | ||
344 | box.width = width; | ||
345 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
346 | scale_box(&box, output_scale); | ||
347 | render_rect(output->wlr_output, output_damage, &box, color); | ||
348 | |||
349 | // Single pixel bar below title | ||
350 | size_t left_offset = 0, right_offset = 0; | ||
351 | bool connects_sides = false; | ||
352 | if (layout == L_HORIZ || layout == L_VERT || | ||
353 | (layout == L_STACKED && is_last_child)) { | ||
354 | if (con->type == C_VIEW) { | ||
355 | left_offset = state->border_left * state->border_thickness; | ||
356 | right_offset = state->border_right * state->border_thickness; | ||
357 | connects_sides = true; | ||
358 | } | ||
359 | } | ||
360 | box.x = x + left_offset; | ||
361 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; | ||
362 | box.width = width - left_offset - right_offset; | ||
363 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
364 | scale_box(&box, output_scale); | ||
365 | render_rect(output->wlr_output, output_damage, &box, color); | ||
366 | |||
367 | if (layout == L_TABBED) { | ||
368 | // Single pixel left edge | ||
369 | box.x = x; | ||
370 | box.y = y + TITLEBAR_BORDER_THICKNESS; | ||
371 | box.width = TITLEBAR_BORDER_THICKNESS; | ||
372 | box.height = | ||
373 | container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2; | ||
374 | scale_box(&box, output_scale); | ||
375 | render_rect(output->wlr_output, output_damage, &box, color); | ||
376 | |||
377 | // Single pixel right edge | ||
378 | box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale; | ||
379 | render_rect(output->wlr_output, output_damage, &box, color); | ||
380 | } | ||
381 | |||
382 | size_t inner_width = width - TITLEBAR_H_PADDING * 2; | ||
383 | |||
384 | // Marks | ||
385 | size_t marks_ob_width = 0; // output-buffer-local | ||
386 | if (config->show_marks && marks_texture) { | ||
387 | struct wlr_box texture_box; | ||
388 | wlr_texture_get_size(marks_texture, | ||
389 | &texture_box.width, &texture_box.height); | ||
390 | texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING) | ||
391 | * output_scale - texture_box.width; | ||
392 | texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; | ||
393 | |||
394 | float matrix[9]; | ||
395 | wlr_matrix_project_box(matrix, &texture_box, | ||
396 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
397 | 0.0, output->wlr_output->transform_matrix); | ||
398 | |||
399 | if (inner_width * output_scale < texture_box.width) { | ||
400 | texture_box.width = inner_width * output_scale; | ||
401 | } | ||
402 | render_texture(output->wlr_output, output_damage, marks_texture, | ||
403 | &texture_box, matrix, con->alpha); | ||
404 | marks_ob_width = texture_box.width; | ||
405 | |||
406 | // Gap between the marks and bottom padding, for when the marks texture | ||
407 | // height is smaller than the config's font height | ||
408 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
409 | premultiply_alpha(color, con->alpha); | ||
410 | box.x = texture_box.x; | ||
411 | box.y = texture_box.y + texture_box.height; | ||
412 | box.width = texture_box.width; | ||
413 | box.height = config->font_height * output_scale - texture_box.height; | ||
414 | if (box.height > 0) { | ||
415 | render_rect(output->wlr_output, output_damage, &box, color); | ||
416 | } | ||
417 | } | ||
418 | |||
419 | // Title text | ||
420 | size_t title_ob_width = 0; // output-buffer-local | ||
421 | if (title_texture) { | ||
422 | struct wlr_box texture_box; | ||
423 | wlr_texture_get_size(title_texture, | ||
424 | &texture_box.width, &texture_box.height); | ||
425 | texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale; | ||
426 | texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; | ||
427 | |||
428 | float matrix[9]; | ||
429 | wlr_matrix_project_box(matrix, &texture_box, | ||
430 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
431 | 0.0, output->wlr_output->transform_matrix); | ||
432 | |||
433 | if (inner_width * output_scale - marks_ob_width < texture_box.width) { | ||
434 | texture_box.width = inner_width * output_scale - marks_ob_width; | ||
435 | } | ||
436 | render_texture(output->wlr_output, output_damage, title_texture, | ||
437 | &texture_box, matrix, con->alpha); | ||
438 | title_ob_width = texture_box.width; | ||
439 | |||
440 | // Gap between the title and bottom padding, for when the title texture | ||
441 | // height is smaller than the config's font height | ||
442 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
443 | premultiply_alpha(color, con->alpha); | ||
444 | box.x = texture_box.x; | ||
445 | box.y = texture_box.y + texture_box.height; | ||
446 | box.width = texture_box.width; | ||
447 | box.height = config->font_height * output_scale - texture_box.height; | ||
448 | if (box.height > 0) { | ||
449 | render_rect(output->wlr_output, output_damage, &box, color); | ||
450 | } | ||
451 | } | ||
452 | |||
453 | // Padding above title | ||
454 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
455 | premultiply_alpha(color, con->alpha); | ||
456 | box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; | ||
457 | box.y = y + TITLEBAR_BORDER_THICKNESS; | ||
458 | box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2; | ||
459 | box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS; | ||
460 | scale_box(&box, output_scale); | ||
461 | render_rect(output->wlr_output, output_damage, &box, color); | ||
462 | |||
463 | // Padding below title | ||
464 | box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale; | ||
465 | render_rect(output->wlr_output, output_damage, &box, color); | ||
466 | |||
467 | // Filler between title and marks | ||
468 | box.width = inner_width * output_scale - title_ob_width - marks_ob_width; | ||
469 | if (box.width > 0) { | ||
470 | box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_ob_width; | ||
471 | box.y = (y + TITLEBAR_V_PADDING) * output_scale; | ||
472 | box.height = config->font_height * output_scale; | ||
473 | render_rect(output->wlr_output, output_damage, &box, color); | ||
474 | } | ||
475 | |||
476 | // Padding left of title | ||
477 | left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; | ||
478 | box.x = x + left_offset; | ||
479 | box.y = y + TITLEBAR_V_PADDING; | ||
480 | box.width = TITLEBAR_H_PADDING - left_offset; | ||
481 | box.height = config->font_height; | ||
482 | scale_box(&box, output_scale); | ||
483 | render_rect(output->wlr_output, output_damage, &box, color); | ||
484 | |||
485 | // Padding right of marks | ||
486 | right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; | ||
487 | box.x = x + width - TITLEBAR_H_PADDING; | ||
488 | box.y = y + TITLEBAR_V_PADDING; | ||
489 | box.width = TITLEBAR_H_PADDING - right_offset; | ||
490 | box.height = config->font_height; | ||
491 | scale_box(&box, output_scale); | ||
492 | render_rect(output->wlr_output, output_damage, &box, color); | ||
493 | |||
494 | if (connects_sides) { | ||
495 | // Left pixel in line with bottom bar | ||
496 | box.x = x; | ||
497 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; | ||
498 | box.width = state->border_thickness * state->border_left; | ||
499 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
500 | scale_box(&box, output_scale); | ||
501 | render_rect(output->wlr_output, output_damage, &box, color); | ||
502 | |||
503 | // Right pixel in line with bottom bar | ||
504 | box.x = x + width - state->border_thickness * state->border_right; | ||
505 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; | ||
506 | box.width = state->border_thickness * state->border_right; | ||
507 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
508 | scale_box(&box, output_scale); | ||
509 | render_rect(output->wlr_output, output_damage, &box, color); | ||
510 | } | ||
511 | } | ||
512 | |||
513 | /** | ||
514 | * Render the top border line for a view using "border pixel". | ||
515 | */ | ||
516 | static void render_top_border(struct sway_output *output, | ||
517 | pixman_region32_t *output_damage, struct sway_container *con, | ||
518 | struct border_colors *colors) { | ||
519 | struct sway_container_state *state = &con->current; | ||
520 | if (!state->border_top) { | ||
521 | return; | ||
522 | } | ||
523 | struct wlr_box box; | ||
524 | float color[4]; | ||
525 | float output_scale = output->wlr_output->scale; | ||
526 | |||
527 | // Child border - top edge | ||
528 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
529 | premultiply_alpha(color, con->alpha); | ||
530 | box.x = state->swayc_x; | ||
531 | box.y = state->swayc_y; | ||
532 | box.width = state->swayc_width; | ||
533 | box.height = state->border_thickness; | ||
534 | scale_box(&box, output_scale); | ||
535 | render_rect(output->wlr_output, output_damage, &box, color); | ||
536 | } | ||
537 | |||
538 | static void render_container(struct sway_output *output, | ||
539 | pixman_region32_t *damage, struct sway_container *con, bool parent_focused); | ||
540 | |||
541 | /** | ||
542 | * Render a container's children using a L_HORIZ or L_VERT layout. | ||
543 | * | ||
544 | * Wrap child views in borders and leave child containers borderless because | ||
545 | * they'll apply their own borders to their children. | ||
546 | */ | ||
547 | static void render_container_simple(struct sway_output *output, | ||
548 | pixman_region32_t *damage, struct sway_container *con, | ||
549 | bool parent_focused) { | ||
550 | for (int i = 0; i < con->current.children->length; ++i) { | ||
551 | struct sway_container *child = con->current.children->items[i]; | ||
552 | |||
553 | if (child->type == C_VIEW) { | ||
554 | struct sway_view *view = child->sway_view; | ||
555 | struct border_colors *colors; | ||
556 | struct wlr_texture *title_texture; | ||
557 | struct wlr_texture *marks_texture; | ||
558 | struct sway_container_state *state = &child->current; | ||
559 | |||
560 | if (view_is_urgent(view)) { | ||
561 | colors = &config->border_colors.urgent; | ||
562 | title_texture = child->title_urgent; | ||
563 | marks_texture = view->marks_urgent; | ||
564 | } else if (state->focused || parent_focused) { | ||
565 | colors = &config->border_colors.focused; | ||
566 | title_texture = child->title_focused; | ||
567 | marks_texture = view->marks_focused; | ||
568 | } else if (con->current.focused_inactive_child == child) { | ||
569 | colors = &config->border_colors.focused_inactive; | ||
570 | title_texture = child->title_focused_inactive; | ||
571 | marks_texture = view->marks_focused_inactive; | ||
572 | } else { | ||
573 | colors = &config->border_colors.unfocused; | ||
574 | title_texture = child->title_unfocused; | ||
575 | marks_texture = view->marks_unfocused; | ||
576 | } | ||
577 | |||
578 | if (!view->using_csd) { | ||
579 | if (state->border == B_NORMAL) { | ||
580 | render_titlebar(output, damage, child, state->swayc_x, | ||
581 | state->swayc_y, state->swayc_width, colors, | ||
582 | title_texture, marks_texture); | ||
583 | } else { | ||
584 | render_top_border(output, damage, child, colors); | ||
585 | } | ||
586 | } | ||
587 | render_view(output, damage, child, colors); | ||
588 | } else { | ||
589 | render_container(output, damage, child, | ||
590 | parent_focused || child->current.focused); | ||
591 | } | ||
592 | } | ||
593 | } | ||
594 | |||
595 | /** | ||
596 | * Render a container's children using the L_TABBED layout. | ||
597 | */ | ||
598 | static void render_container_tabbed(struct sway_output *output, | ||
599 | pixman_region32_t *damage, struct sway_container *con, | ||
600 | bool parent_focused) { | ||
601 | if (!con->current.children->length) { | ||
602 | return; | ||
603 | } | ||
604 | struct sway_container_state *pstate = &con->current; | ||
605 | struct sway_container *current = pstate->focused_inactive_child; | ||
606 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
607 | |||
608 | double width_gap_adjustment = 2 * pstate->current_gaps; | ||
609 | int tab_width = | ||
610 | (pstate->swayc_width - width_gap_adjustment) / pstate->children->length; | ||
611 | |||
612 | // Render tabs | ||
613 | for (int i = 0; i < pstate->children->length; ++i) { | ||
614 | struct sway_container *child = pstate->children->items[i]; | ||
615 | struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; | ||
616 | struct sway_container_state *cstate = &child->current; | ||
617 | struct border_colors *colors; | ||
618 | struct wlr_texture *title_texture; | ||
619 | struct wlr_texture *marks_texture; | ||
620 | bool urgent = view ? | ||
621 | view_is_urgent(view) : container_has_urgent_child(child); | ||
622 | |||
623 | if (urgent) { | ||
624 | colors = &config->border_colors.urgent; | ||
625 | title_texture = child->title_urgent; | ||
626 | marks_texture = view ? view->marks_urgent : NULL; | ||
627 | } else if (cstate->focused || parent_focused) { | ||
628 | colors = &config->border_colors.focused; | ||
629 | title_texture = child->title_focused; | ||
630 | marks_texture = view ? view->marks_focused : NULL; | ||
631 | } else if (child == pstate->focused_inactive_child) { | ||
632 | colors = &config->border_colors.focused_inactive; | ||
633 | title_texture = child->title_focused_inactive; | ||
634 | marks_texture = view ? view->marks_focused_inactive : NULL; | ||
635 | } else { | ||
636 | colors = &config->border_colors.unfocused; | ||
637 | title_texture = child->title_unfocused; | ||
638 | marks_texture = view ? view->marks_unfocused : NULL; | ||
639 | } | ||
640 | |||
641 | int x = cstate->swayc_x + tab_width * i; | ||
642 | |||
643 | // Make last tab use the remaining width of the parent | ||
644 | if (i == pstate->children->length - 1) { | ||
645 | tab_width = | ||
646 | pstate->swayc_width - width_gap_adjustment - tab_width * i; | ||
647 | } | ||
648 | |||
649 | render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width, | ||
650 | colors, title_texture, marks_texture); | ||
651 | |||
652 | if (child == current) { | ||
653 | current_colors = colors; | ||
654 | } | ||
655 | } | ||
656 | |||
657 | // Render surface and left/right/bottom borders | ||
658 | if (current->type == C_VIEW) { | ||
659 | render_view(output, damage, current, current_colors); | ||
660 | } else { | ||
661 | render_container(output, damage, current, | ||
662 | parent_focused || current->current.focused); | ||
663 | } | ||
664 | } | ||
665 | |||
666 | /** | ||
667 | * Render a container's children using the L_STACKED layout. | ||
668 | */ | ||
669 | static void render_container_stacked(struct sway_output *output, | ||
670 | pixman_region32_t *damage, struct sway_container *con, | ||
671 | bool parent_focused) { | ||
672 | if (!con->current.children->length) { | ||
673 | return; | ||
674 | } | ||
675 | struct sway_container_state *pstate = &con->current; | ||
676 | struct sway_container *current = pstate->focused_inactive_child; | ||
677 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
678 | |||
679 | size_t titlebar_height = container_titlebar_height(); | ||
680 | |||
681 | // Render titles | ||
682 | for (int i = 0; i < pstate->children->length; ++i) { | ||
683 | struct sway_container *child = pstate->children->items[i]; | ||
684 | struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; | ||
685 | struct sway_container_state *cstate = &child->current; | ||
686 | struct border_colors *colors; | ||
687 | struct wlr_texture *title_texture; | ||
688 | struct wlr_texture *marks_texture; | ||
689 | bool urgent = view ? | ||
690 | view_is_urgent(view) : container_has_urgent_child(child); | ||
691 | |||
692 | if (urgent) { | ||
693 | colors = &config->border_colors.urgent; | ||
694 | title_texture = child->title_urgent; | ||
695 | marks_texture = view ? view->marks_urgent : NULL; | ||
696 | } else if (cstate->focused || parent_focused) { | ||
697 | colors = &config->border_colors.focused; | ||
698 | title_texture = child->title_focused; | ||
699 | marks_texture = view ? view->marks_focused : NULL; | ||
700 | } else if (child == pstate->focused_inactive_child) { | ||
701 | colors = &config->border_colors.focused_inactive; | ||
702 | title_texture = child->title_focused_inactive; | ||
703 | marks_texture = view ? view->marks_focused_inactive : NULL; | ||
704 | } else { | ||
705 | colors = &config->border_colors.unfocused; | ||
706 | title_texture = child->title_unfocused; | ||
707 | marks_texture = view ? view->marks_unfocused : NULL; | ||
708 | } | ||
709 | |||
710 | int y = cstate->swayc_y + titlebar_height * i; | ||
711 | render_titlebar(output, damage, child, cstate->swayc_x, y, | ||
712 | cstate->swayc_width, colors, title_texture, marks_texture); | ||
713 | |||
714 | if (child == current) { | ||
715 | current_colors = colors; | ||
716 | } | ||
717 | } | ||
718 | |||
719 | // Render surface and left/right/bottom borders | ||
720 | if (current->type == C_VIEW) { | ||
721 | render_view(output, damage, current, current_colors); | ||
722 | } else { | ||
723 | render_container(output, damage, current, | ||
724 | parent_focused || current->current.focused); | ||
725 | } | ||
726 | } | ||
727 | |||
728 | static void render_container(struct sway_output *output, | ||
729 | pixman_region32_t *damage, struct sway_container *con, | ||
730 | bool parent_focused) { | ||
731 | switch (con->current.layout) { | ||
732 | case L_NONE: | ||
733 | case L_HORIZ: | ||
734 | case L_VERT: | ||
735 | render_container_simple(output, damage, con, parent_focused); | ||
736 | break; | ||
737 | case L_STACKED: | ||
738 | render_container_stacked(output, damage, con, parent_focused); | ||
739 | break; | ||
740 | case L_TABBED: | ||
741 | render_container_tabbed(output, damage, con, parent_focused); | ||
742 | break; | ||
743 | case L_FLOATING: | ||
744 | sway_assert(false, "Didn't expect to see floating here"); | ||
745 | } | ||
746 | } | ||
747 | |||
748 | static void render_floating_container(struct sway_output *soutput, | ||
749 | pixman_region32_t *damage, struct sway_container *con) { | ||
750 | if (con->type == C_VIEW) { | ||
751 | struct sway_view *view = con->sway_view; | ||
752 | struct border_colors *colors; | ||
753 | struct wlr_texture *title_texture; | ||
754 | struct wlr_texture *marks_texture; | ||
755 | |||
756 | if (view_is_urgent(view)) { | ||
757 | colors = &config->border_colors.urgent; | ||
758 | title_texture = con->title_urgent; | ||
759 | marks_texture = view->marks_urgent; | ||
760 | } else if (con->current.focused) { | ||
761 | colors = &config->border_colors.focused; | ||
762 | title_texture = con->title_focused; | ||
763 | marks_texture = view->marks_focused; | ||
764 | } else { | ||
765 | colors = &config->border_colors.unfocused; | ||
766 | title_texture = con->title_unfocused; | ||
767 | marks_texture = view->marks_unfocused; | ||
768 | } | ||
769 | |||
770 | if (!view->using_csd) { | ||
771 | if (con->current.border == B_NORMAL) { | ||
772 | render_titlebar(soutput, damage, con, con->current.swayc_x, | ||
773 | con->current.swayc_y, con->current.swayc_width, colors, | ||
774 | title_texture, marks_texture); | ||
775 | } else if (con->current.border != B_NONE) { | ||
776 | render_top_border(soutput, damage, con, colors); | ||
777 | } | ||
778 | } | ||
779 | render_view(soutput, damage, con, colors); | ||
780 | } else { | ||
781 | render_container(soutput, damage, con, false); | ||
782 | } | ||
783 | } | ||
784 | |||
785 | static void render_floating(struct sway_output *soutput, | ||
786 | pixman_region32_t *damage) { | ||
787 | for (int i = 0; i < root_container.current.children->length; ++i) { | ||
788 | struct sway_container *output = | ||
789 | root_container.current.children->items[i]; | ||
790 | for (int j = 0; j < output->current.children->length; ++j) { | ||
791 | struct sway_container *ws = output->current.children->items[j]; | ||
792 | if (!workspace_is_visible(ws)) { | ||
793 | continue; | ||
794 | } | ||
795 | list_t *floating = | ||
796 | ws->current.ws_floating->current.children; | ||
797 | for (int k = 0; k < floating->length; ++k) { | ||
798 | struct sway_container *floater = floating->items[k]; | ||
799 | render_floating_container(soutput, damage, floater); | ||
800 | } | ||
801 | } | ||
802 | } | ||
803 | } | ||
804 | |||
805 | const char *damage_debug = NULL; | ||
806 | |||
807 | void output_render(struct sway_output *output, struct timespec *when, | ||
808 | pixman_region32_t *damage) { | ||
809 | struct wlr_output *wlr_output = output->wlr_output; | ||
810 | |||
811 | struct wlr_renderer *renderer = | ||
812 | wlr_backend_get_renderer(wlr_output->backend); | ||
813 | if (!sway_assert(renderer != NULL, | ||
814 | "expected the output backend to have a renderer")) { | ||
815 | return; | ||
816 | } | ||
817 | |||
818 | wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); | ||
819 | |||
820 | bool damage_whole_before_swap = false; | ||
821 | if (!pixman_region32_not_empty(damage)) { | ||
822 | // Output isn't damaged but needs buffer swap | ||
823 | goto renderer_end; | ||
824 | } | ||
825 | |||
826 | if (damage_debug != NULL) { | ||
827 | if (strcmp(damage_debug, "highlight") == 0) { | ||
828 | wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); | ||
829 | damage_whole_before_swap = true; | ||
830 | } else if (strcmp(damage_debug, "rerender") == 0) { | ||
831 | int width, height; | ||
832 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
833 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
834 | } | ||
835 | } | ||
836 | |||
837 | struct sway_container *workspace = output_get_active_workspace(output); | ||
838 | struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; | ||
839 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
840 | |||
841 | if (output_has_opaque_lockscreen(output, seat) && seat->focused_layer) { | ||
842 | struct wlr_layer_surface *wlr_layer_surface = seat->focused_layer; | ||
843 | struct sway_layer_surface *sway_layer_surface = | ||
844 | layer_from_wlr_layer_surface(seat->focused_layer); | ||
845 | struct render_data data = { | ||
846 | .output = output, | ||
847 | .damage = damage, | ||
848 | .alpha = 1.0f, | ||
849 | }; | ||
850 | output_surface_for_each_surface(wlr_layer_surface->surface, | ||
851 | sway_layer_surface->geo.x, sway_layer_surface->geo.y, | ||
852 | &data.root_geo, render_surface_iterator, &data); | ||
853 | } else if (fullscreen_view) { | ||
854 | float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; | ||
855 | |||
856 | int nrects; | ||
857 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
858 | for (int i = 0; i < nrects; ++i) { | ||
859 | scissor_output(wlr_output, &rects[i]); | ||
860 | wlr_renderer_clear(renderer, clear_color); | ||
861 | } | ||
862 | |||
863 | // TODO: handle views smaller than the output | ||
864 | if (fullscreen_view->swayc->instructions->length) { | ||
865 | render_saved_view(fullscreen_view, output, damage, 1.0f); | ||
866 | } else { | ||
867 | render_view_surfaces(fullscreen_view, output, damage, 1.0f); | ||
868 | } | ||
869 | |||
870 | if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { | ||
871 | render_unmanaged(output, damage, | ||
872 | &root_container.sway_root->xwayland_unmanaged); | ||
873 | } | ||
874 | } else { | ||
875 | float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; | ||
876 | |||
877 | int nrects; | ||
878 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
879 | for (int i = 0; i < nrects; ++i) { | ||
880 | scissor_output(wlr_output, &rects[i]); | ||
881 | wlr_renderer_clear(renderer, clear_color); | ||
882 | } | ||
883 | |||
884 | render_layer(output, damage, | ||
885 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
886 | render_layer(output, damage, | ||
887 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
888 | |||
889 | render_container(output, damage, workspace, workspace->current.focused); | ||
890 | render_floating(output, damage); | ||
891 | |||
892 | render_unmanaged(output, damage, | ||
893 | &root_container.sway_root->xwayland_unmanaged); | ||
894 | render_layer(output, damage, | ||
895 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
896 | } | ||
897 | render_layer(output, damage, | ||
898 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
899 | render_drag_icons(output, damage, &root_container.sway_root->drag_icons); | ||
900 | |||
901 | renderer_end: | ||
902 | if (root_container.sway_root->debug_tree) { | ||
903 | wlr_render_texture(renderer, root_container.sway_root->debug_tree, | ||
904 | wlr_output->transform_matrix, 0, 0, 1); | ||
905 | } | ||
906 | |||
907 | if (damage_whole_before_swap || root_container.sway_root->debug_tree) { | ||
908 | int width, height; | ||
909 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
910 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
911 | } | ||
912 | |||
913 | wlr_renderer_scissor(renderer, NULL); | ||
914 | wlr_renderer_end(renderer); | ||
915 | if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { | ||
916 | return; | ||
917 | } | ||
918 | output->last_frame = *when; | ||
919 | } | ||
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index d2932c87..2a89880a 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c | |||
@@ -4,8 +4,8 @@ | |||
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <time.h> | 5 | #include <time.h> |
6 | #include <wlr/types/wlr_buffer.h> | 6 | #include <wlr/types/wlr_buffer.h> |
7 | #include <wlr/types/wlr_linux_dmabuf.h> | ||
8 | #include "sway/debug.h" | 7 | #include "sway/debug.h" |
8 | #include "sway/desktop/idle_inhibit_v1.h" | ||
9 | #include "sway/desktop/transaction.h" | 9 | #include "sway/desktop/transaction.h" |
10 | #include "sway/output.h" | 10 | #include "sway/output.h" |
11 | #include "sway/tree/container.h" | 11 | #include "sway/tree/container.h" |
@@ -18,14 +18,14 @@ | |||
18 | * How long we should wait for views to respond to the configure before giving | 18 | * How long we should wait for views to respond to the configure before giving |
19 | * up and applying the transaction anyway. | 19 | * up and applying the transaction anyway. |
20 | */ | 20 | */ |
21 | #define TIMEOUT_MS 200 | 21 | int txn_timeout_ms = 200; |
22 | 22 | ||
23 | /** | 23 | /** |
24 | * If enabled, sway will always wait for the transaction timeout before | 24 | * If enabled, sway will always wait for the transaction timeout before |
25 | * applying it, rather than applying it when the views are ready. This allows us | 25 | * applying it, rather than applying it when the views are ready. This allows us |
26 | * to observe the rendered state while a transaction is in progress. | 26 | * to observe the rendered state while a transaction is in progress. |
27 | */ | 27 | */ |
28 | #define TRANSACTION_DEBUG false | 28 | bool txn_debug = false; |
29 | 29 | ||
30 | struct sway_transaction { | 30 | struct sway_transaction { |
31 | struct wl_event_source *timer; | 31 | struct wl_event_source *timer; |
@@ -46,7 +46,7 @@ struct sway_transaction_instruction { | |||
46 | bool ready; | 46 | bool ready; |
47 | }; | 47 | }; |
48 | 48 | ||
49 | struct sway_transaction *transaction_create() { | 49 | static struct sway_transaction *transaction_create() { |
50 | struct sway_transaction *transaction = | 50 | struct sway_transaction *transaction = |
51 | calloc(1, sizeof(struct sway_transaction)); | 51 | calloc(1, sizeof(struct sway_transaction)); |
52 | transaction->instructions = create_list(); | 52 | transaction->instructions = create_list(); |
@@ -72,8 +72,8 @@ static void save_view_buffer(struct sway_view *view, | |||
72 | } | 72 | } |
73 | if (view->surface && wlr_surface_has_buffer(view->surface)) { | 73 | if (view->surface && wlr_surface_has_buffer(view->surface)) { |
74 | instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer); | 74 | instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer); |
75 | instruction->saved_buffer_width = view->surface->current->width; | 75 | instruction->saved_buffer_width = view->surface->current.width; |
76 | instruction->saved_buffer_height = view->surface->current->height; | 76 | instruction->saved_buffer_height = view->surface->current.height; |
77 | } | 77 | } |
78 | } | 78 | } |
79 | 79 | ||
@@ -138,25 +138,18 @@ static void copy_pending_state(struct sway_container *container, | |||
138 | state->children = create_list(); | 138 | state->children = create_list(); |
139 | list_cat(state->children, container->children); | 139 | list_cat(state->children, container->children); |
140 | } | 140 | } |
141 | } | ||
142 | 141 | ||
143 | static bool transaction_has_container(struct sway_transaction *transaction, | 142 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
144 | struct sway_container *container) { | 143 | state->focused = seat_get_focus(seat) == container; |
145 | for (int i = 0; i < transaction->instructions->length; ++i) { | 144 | |
146 | struct sway_transaction_instruction *instruction = | 145 | if (container->type != C_VIEW) { |
147 | transaction->instructions->items[i]; | 146 | state->focused_inactive_child = |
148 | if (instruction->container == container) { | 147 | seat_get_active_child(seat, container); |
149 | return true; | ||
150 | } | ||
151 | } | 148 | } |
152 | return false; | ||
153 | } | 149 | } |
154 | 150 | ||
155 | void transaction_add_container(struct sway_transaction *transaction, | 151 | static void transaction_add_container(struct sway_transaction *transaction, |
156 | struct sway_container *container) { | 152 | struct sway_container *container) { |
157 | if (transaction_has_container(transaction, container)) { | ||
158 | return; | ||
159 | } | ||
160 | struct sway_transaction_instruction *instruction = | 153 | struct sway_transaction_instruction *instruction = |
161 | calloc(1, sizeof(struct sway_transaction_instruction)); | 154 | calloc(1, sizeof(struct sway_transaction_instruction)); |
162 | instruction->transaction = transaction; | 155 | instruction->transaction = transaction; |
@@ -174,7 +167,7 @@ void transaction_add_container(struct sway_transaction *transaction, | |||
174 | * Apply a transaction to the "current" state of the tree. | 167 | * Apply a transaction to the "current" state of the tree. |
175 | */ | 168 | */ |
176 | static void transaction_apply(struct sway_transaction *transaction) { | 169 | static void transaction_apply(struct sway_transaction *transaction) { |
177 | wlr_log(L_DEBUG, "Applying transaction %p", transaction); | 170 | wlr_log(WLR_DEBUG, "Applying transaction %p", transaction); |
178 | if (server.debug_txn_timings) { | 171 | if (server.debug_txn_timings) { |
179 | struct timespec now; | 172 | struct timespec now; |
180 | clock_gettime(CLOCK_MONOTONIC, &now); | 173 | clock_gettime(CLOCK_MONOTONIC, &now); |
@@ -185,9 +178,9 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
185 | float ms_waiting = (now.tv_sec - commit->tv_sec) * 1000 + | 178 | float ms_waiting = (now.tv_sec - commit->tv_sec) * 1000 + |
186 | (now.tv_nsec - commit->tv_nsec) / 1000000.0; | 179 | (now.tv_nsec - commit->tv_nsec) / 1000000.0; |
187 | float ms_total = ms_arranging + ms_waiting; | 180 | float ms_total = ms_arranging + ms_waiting; |
188 | wlr_log(L_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, " | 181 | wlr_log(WLR_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, " |
189 | "%.1fms total (%.1f frames if 60Hz)", transaction, | 182 | "%.1fms total (%.1f frames if 60Hz)", transaction, |
190 | ms_arranging, ms_waiting, ms_total, ms_total / (1000 / 60)); | 183 | ms_arranging, ms_waiting, ms_total, ms_total / (1000.0f / 60)); |
191 | } | 184 | } |
192 | 185 | ||
193 | // Apply the instruction state to the container's current state | 186 | // Apply the instruction state to the container's current state |
@@ -209,10 +202,12 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
209 | .width = instruction->state.swayc_width, | 202 | .width = instruction->state.swayc_width, |
210 | .height = instruction->state.swayc_height, | 203 | .height = instruction->state.swayc_height, |
211 | }; | 204 | }; |
212 | for (int j = 0; j < root_container.children->length; ++j) { | 205 | for (int j = 0; j < root_container.current.children->length; ++j) { |
213 | struct sway_container *output = root_container.children->items[j]; | 206 | struct sway_container *output = root_container.current.children->items[j]; |
214 | output_damage_box(output->sway_output, &old_box); | 207 | if (output->sway_output) { |
215 | output_damage_box(output->sway_output, &new_box); | 208 | output_damage_box(output->sway_output, &old_box); |
209 | output_damage_box(output->sway_output, &new_box); | ||
210 | } | ||
216 | } | 211 | } |
217 | 212 | ||
218 | // There are separate children lists for each instruction state, the | 213 | // There are separate children lists for each instruction state, the |
@@ -227,29 +222,22 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
227 | } | 222 | } |
228 | } | 223 | } |
229 | 224 | ||
230 | /** | ||
231 | * For simplicity, we only progress the queue if it can be completely flushed. | ||
232 | */ | ||
233 | static void transaction_progress_queue() { | 225 | static void transaction_progress_queue() { |
234 | // We iterate this list in reverse because we're more likely to find a | 226 | while (server.transactions->length) { |
235 | // waiting transactions at the end of the list. | 227 | struct sway_transaction *transaction = server.transactions->items[0]; |
236 | for (int i = server.transactions->length - 1; i >= 0; --i) { | ||
237 | struct sway_transaction *transaction = server.transactions->items[i]; | ||
238 | if (transaction->num_waiting) { | 228 | if (transaction->num_waiting) { |
239 | return; | 229 | return; |
240 | } | 230 | } |
241 | } | ||
242 | for (int i = 0; i < server.transactions->length; ++i) { | ||
243 | struct sway_transaction *transaction = server.transactions->items[i]; | ||
244 | transaction_apply(transaction); | 231 | transaction_apply(transaction); |
245 | transaction_destroy(transaction); | 232 | transaction_destroy(transaction); |
233 | list_del(server.transactions, 0); | ||
246 | } | 234 | } |
247 | server.transactions->length = 0; | 235 | idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); |
248 | } | 236 | } |
249 | 237 | ||
250 | static int handle_timeout(void *data) { | 238 | static int handle_timeout(void *data) { |
251 | struct sway_transaction *transaction = data; | 239 | struct sway_transaction *transaction = data; |
252 | wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting)", | 240 | wlr_log(WLR_DEBUG, "Transaction %p timed out (%li waiting)", |
253 | transaction, transaction->num_waiting); | 241 | transaction, transaction->num_waiting); |
254 | transaction->num_waiting = 0; | 242 | transaction->num_waiting = 0; |
255 | transaction_progress_queue(); | 243 | transaction_progress_queue(); |
@@ -283,8 +271,8 @@ static bool should_configure(struct sway_container *con, | |||
283 | return true; | 271 | return true; |
284 | } | 272 | } |
285 | 273 | ||
286 | void transaction_commit(struct sway_transaction *transaction) { | 274 | static void transaction_commit(struct sway_transaction *transaction) { |
287 | wlr_log(L_DEBUG, "Transaction %p committing with %i instructions", | 275 | wlr_log(WLR_DEBUG, "Transaction %p committing with %i instructions", |
288 | transaction, transaction->instructions->length); | 276 | transaction, transaction->instructions->length); |
289 | transaction->num_waiting = 0; | 277 | transaction->num_waiting = 0; |
290 | for (int i = 0; i < transaction->instructions->length; ++i) { | 278 | for (int i = 0; i < transaction->instructions->length; ++i) { |
@@ -317,9 +305,10 @@ void transaction_commit(struct sway_transaction *transaction) { | |||
317 | } else { | 305 | } else { |
318 | // There are no other transactions in progress, and this one has nothing | 306 | // There are no other transactions in progress, and this one has nothing |
319 | // to wait for, so we can skip the queue. | 307 | // to wait for, so we can skip the queue. |
320 | wlr_log(L_DEBUG, "Transaction %p has nothing to wait for", transaction); | 308 | wlr_log(WLR_DEBUG, "Transaction %p has nothing to wait for", transaction); |
321 | transaction_apply(transaction); | 309 | transaction_apply(transaction); |
322 | transaction_destroy(transaction); | 310 | transaction_destroy(transaction); |
311 | idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); | ||
323 | return; | 312 | return; |
324 | } | 313 | } |
325 | 314 | ||
@@ -327,7 +316,7 @@ void transaction_commit(struct sway_transaction *transaction) { | |||
327 | // Set up a timer which the views must respond within | 316 | // Set up a timer which the views must respond within |
328 | transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, | 317 | transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, |
329 | handle_timeout, transaction); | 318 | handle_timeout, transaction); |
330 | wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); | 319 | wl_event_source_timer_update(transaction->timer, txn_timeout_ms); |
331 | } | 320 | } |
332 | 321 | ||
333 | // The debug tree shows the pending/live tree. Here is a good place to | 322 | // The debug tree shows the pending/live tree. Here is a good place to |
@@ -347,7 +336,7 @@ static void set_instruction_ready( | |||
347 | struct timespec *start = &transaction->commit_time; | 336 | struct timespec *start = &transaction->commit_time; |
348 | float ms = (now.tv_sec - start->tv_sec) * 1000 + | 337 | float ms = (now.tv_sec - start->tv_sec) * 1000 + |
349 | (now.tv_nsec - start->tv_nsec) / 1000000.0; | 338 | (now.tv_nsec - start->tv_nsec) / 1000000.0; |
350 | wlr_log(L_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)", | 339 | wlr_log(WLR_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)", |
351 | transaction, | 340 | transaction, |
352 | transaction->num_configures - transaction->num_waiting + 1, | 341 | transaction->num_configures - transaction->num_waiting + 1, |
353 | transaction->num_configures, ms, | 342 | transaction->num_configures, ms, |
@@ -358,11 +347,11 @@ static void set_instruction_ready( | |||
358 | // If all views are ready, apply the transaction. | 347 | // If all views are ready, apply the transaction. |
359 | // If the transaction has timed out then its num_waiting will be 0 already. | 348 | // If the transaction has timed out then its num_waiting will be 0 already. |
360 | if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { | 349 | if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { |
361 | #if !TRANSACTION_DEBUG | 350 | if (!txn_debug) { |
362 | wlr_log(L_DEBUG, "Transaction %p is ready", transaction); | 351 | wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction); |
363 | wl_event_source_timer_update(transaction->timer, 0); | 352 | wl_event_source_timer_update(transaction->timer, 0); |
364 | transaction_progress_queue(); | 353 | transaction_progress_queue(); |
365 | #endif | 354 | } |
366 | } | 355 | } |
367 | } | 356 | } |
368 | 357 | ||
@@ -374,7 +363,9 @@ static void set_instructions_ready(struct sway_view *view, int index) { | |||
374 | for (int i = 0; i <= index; ++i) { | 363 | for (int i = 0; i <= index; ++i) { |
375 | struct sway_transaction_instruction *instruction = | 364 | struct sway_transaction_instruction *instruction = |
376 | view->swayc->instructions->items[i]; | 365 | view->swayc->instructions->items[i]; |
377 | set_instruction_ready(instruction); | 366 | if (!instruction->ready) { |
367 | set_instruction_ready(instruction); | ||
368 | } | ||
378 | } | 369 | } |
379 | } | 370 | } |
380 | 371 | ||
@@ -413,3 +404,17 @@ struct wlr_texture *transaction_get_saved_texture(struct sway_view *view, | |||
413 | *height = instruction->saved_buffer_height; | 404 | *height = instruction->saved_buffer_height; |
414 | return instruction->saved_buffer->texture; | 405 | return instruction->saved_buffer->texture; |
415 | } | 406 | } |
407 | |||
408 | void transaction_commit_dirty(void) { | ||
409 | if (!server.dirty_containers->length) { | ||
410 | return; | ||
411 | } | ||
412 | struct sway_transaction *transaction = transaction_create(); | ||
413 | for (int i = 0; i < server.dirty_containers->length; ++i) { | ||
414 | struct sway_container *container = server.dirty_containers->items[i]; | ||
415 | transaction_add_container(transaction, container); | ||
416 | container->dirty = false; | ||
417 | } | ||
418 | server.dirty_containers->length = 0; | ||
419 | transaction_commit(transaction); | ||
420 | } | ||
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 47604c31..f3e4fef8 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 199309L | 1 | #define _POSIX_C_SOURCE 199309L |
2 | #include <float.h> | ||
2 | #include <stdbool.h> | 3 | #include <stdbool.h> |
3 | #include <stdlib.h> | 4 | #include <stdlib.h> |
4 | #include <wayland-server.h> | 5 | #include <wayland-server.h> |
@@ -45,6 +46,24 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) { | |||
45 | view_child_destroy(&popup->child); | 46 | view_child_destroy(&popup->child); |
46 | } | 47 | } |
47 | 48 | ||
49 | static void popup_unconstrain(struct sway_xdg_popup *popup) { | ||
50 | struct sway_view *view = popup->child.view; | ||
51 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; | ||
52 | |||
53 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); | ||
54 | |||
55 | // the output box expressed in the coordinate system of the toplevel parent | ||
56 | // of the popup | ||
57 | struct wlr_box output_toplevel_sx_box = { | ||
58 | .x = output->x - view->x, | ||
59 | .y = output->y - view->y, | ||
60 | .width = output->width, | ||
61 | .height = output->height, | ||
62 | }; | ||
63 | |||
64 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); | ||
65 | } | ||
66 | |||
48 | static struct sway_xdg_popup *popup_create( | 67 | static struct sway_xdg_popup *popup_create( |
49 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { | 68 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { |
50 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; | 69 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; |
@@ -55,12 +74,15 @@ static struct sway_xdg_popup *popup_create( | |||
55 | return NULL; | 74 | return NULL; |
56 | } | 75 | } |
57 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); | 76 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); |
77 | popup->wlr_xdg_surface = xdg_surface; | ||
58 | 78 | ||
59 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); | 79 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); |
60 | popup->new_popup.notify = popup_handle_new_popup; | 80 | popup->new_popup.notify = popup_handle_new_popup; |
61 | wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); | 81 | wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); |
62 | popup->destroy.notify = popup_handle_destroy; | 82 | popup->destroy.notify = popup_handle_destroy; |
63 | 83 | ||
84 | popup_unconstrain(popup); | ||
85 | |||
64 | return popup; | 86 | return popup; |
65 | } | 87 | } |
66 | 88 | ||
@@ -74,6 +96,16 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view( | |||
74 | return (struct sway_xdg_shell_view *)view; | 96 | return (struct sway_xdg_shell_view *)view; |
75 | } | 97 | } |
76 | 98 | ||
99 | static void get_constraints(struct sway_view *view, double *min_width, | ||
100 | double *max_width, double *min_height, double *max_height) { | ||
101 | struct wlr_xdg_toplevel_state *state = | ||
102 | &view->wlr_xdg_surface->toplevel->current; | ||
103 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; | ||
104 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; | ||
105 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; | ||
106 | *max_height = state->max_height > 0 ? state->max_height : DBL_MAX; | ||
107 | } | ||
108 | |||
77 | static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { | 109 | static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { |
78 | if (xdg_shell_view_from_view(view) == NULL) { | 110 | if (xdg_shell_view_from_view(view) == NULL) { |
79 | return NULL; | 111 | return NULL; |
@@ -167,6 +199,7 @@ static void destroy(struct sway_view *view) { | |||
167 | } | 199 | } |
168 | 200 | ||
169 | static const struct sway_view_impl view_impl = { | 201 | static const struct sway_view_impl view_impl = { |
202 | .get_constraints = get_constraints, | ||
170 | .get_string_prop = get_string_prop, | 203 | .get_string_prop = get_string_prop, |
171 | .configure = configure, | 204 | .configure = configure, |
172 | .set_activated = set_activated, | 205 | .set_activated = set_activated, |
@@ -192,10 +225,24 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
192 | transaction_notify_view_ready(view, xdg_surface->configure_serial); | 225 | transaction_notify_view_ready(view, xdg_surface->configure_serial); |
193 | } | 226 | } |
194 | 227 | ||
195 | view_update_title(view, false); | ||
196 | view_damage_from(view); | 228 | view_damage_from(view); |
197 | } | 229 | } |
198 | 230 | ||
231 | static void handle_set_title(struct wl_listener *listener, void *data) { | ||
232 | struct sway_xdg_shell_view *xdg_shell_view = | ||
233 | wl_container_of(listener, xdg_shell_view, set_title); | ||
234 | struct sway_view *view = &xdg_shell_view->view; | ||
235 | view_update_title(view, false); | ||
236 | view_execute_criteria(view); | ||
237 | } | ||
238 | |||
239 | static void handle_set_app_id(struct wl_listener *listener, void *data) { | ||
240 | struct sway_xdg_shell_view *xdg_shell_view = | ||
241 | wl_container_of(listener, xdg_shell_view, set_app_id); | ||
242 | struct sway_view *view = &xdg_shell_view->view; | ||
243 | view_execute_criteria(view); | ||
244 | } | ||
245 | |||
199 | static void handle_new_popup(struct wl_listener *listener, void *data) { | 246 | static void handle_new_popup(struct wl_listener *listener, void *data) { |
200 | struct sway_xdg_shell_view *xdg_shell_view = | 247 | struct sway_xdg_shell_view *xdg_shell_view = |
201 | wl_container_of(listener, xdg_shell_view, new_popup); | 248 | wl_container_of(listener, xdg_shell_view, new_popup); |
@@ -222,8 +269,37 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
222 | 269 | ||
223 | view_set_fullscreen(view, e->fullscreen); | 270 | view_set_fullscreen(view, e->fullscreen); |
224 | 271 | ||
225 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 272 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); |
226 | arrange_and_commit(ws); | 273 | arrange_windows(output); |
274 | transaction_commit_dirty(); | ||
275 | } | ||
276 | |||
277 | static void handle_request_move(struct wl_listener *listener, void *data) { | ||
278 | struct sway_xdg_shell_view *xdg_shell_view = | ||
279 | wl_container_of(listener, xdg_shell_view, request_move); | ||
280 | struct sway_view *view = &xdg_shell_view->view; | ||
281 | if (!container_is_floating(view->swayc)) { | ||
282 | return; | ||
283 | } | ||
284 | struct wlr_xdg_toplevel_move_event *e = data; | ||
285 | struct sway_seat *seat = e->seat->seat->data; | ||
286 | if (e->serial == seat->last_button_serial) { | ||
287 | seat_begin_move(seat, view->swayc, seat->last_button); | ||
288 | } | ||
289 | } | ||
290 | |||
291 | static void handle_request_resize(struct wl_listener *listener, void *data) { | ||
292 | struct sway_xdg_shell_view *xdg_shell_view = | ||
293 | wl_container_of(listener, xdg_shell_view, request_resize); | ||
294 | struct sway_view *view = &xdg_shell_view->view; | ||
295 | if (!container_is_floating(view->swayc)) { | ||
296 | return; | ||
297 | } | ||
298 | struct wlr_xdg_toplevel_resize_event *e = data; | ||
299 | struct sway_seat *seat = e->seat->seat->data; | ||
300 | if (e->serial == seat->last_button_serial) { | ||
301 | seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); | ||
302 | } | ||
227 | } | 303 | } |
228 | 304 | ||
229 | static void handle_unmap(struct wl_listener *listener, void *data) { | 305 | static void handle_unmap(struct wl_listener *listener, void *data) { |
@@ -240,6 +316,10 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
240 | wl_list_remove(&xdg_shell_view->commit.link); | 316 | wl_list_remove(&xdg_shell_view->commit.link); |
241 | wl_list_remove(&xdg_shell_view->new_popup.link); | 317 | wl_list_remove(&xdg_shell_view->new_popup.link); |
242 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | 318 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); |
319 | wl_list_remove(&xdg_shell_view->request_move.link); | ||
320 | wl_list_remove(&xdg_shell_view->request_resize.link); | ||
321 | wl_list_remove(&xdg_shell_view->set_title.link); | ||
322 | wl_list_remove(&xdg_shell_view->set_app_id.link); | ||
243 | } | 323 | } |
244 | 324 | ||
245 | static void handle_map(struct wl_listener *listener, void *data) { | 325 | static void handle_map(struct wl_listener *listener, void *data) { |
@@ -251,8 +331,8 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
251 | view->natural_width = view->wlr_xdg_surface->geometry.width; | 331 | view->natural_width = view->wlr_xdg_surface->geometry.width; |
252 | view->natural_height = view->wlr_xdg_surface->geometry.height; | 332 | view->natural_height = view->wlr_xdg_surface->geometry.height; |
253 | if (!view->natural_width && !view->natural_height) { | 333 | if (!view->natural_width && !view->natural_height) { |
254 | view->natural_width = view->wlr_xdg_surface->surface->current->width; | 334 | view->natural_width = view->wlr_xdg_surface->surface->current.width; |
255 | view->natural_height = view->wlr_xdg_surface->surface->current->height; | 335 | view->natural_height = view->wlr_xdg_surface->surface->current.height; |
256 | } | 336 | } |
257 | 337 | ||
258 | view_map(view, view->wlr_xdg_surface->surface); | 338 | view_map(view, view->wlr_xdg_surface->surface); |
@@ -260,10 +340,11 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
260 | if (xdg_surface->toplevel->client_pending.fullscreen) { | 340 | if (xdg_surface->toplevel->client_pending.fullscreen) { |
261 | view_set_fullscreen(view, true); | 341 | view_set_fullscreen(view, true); |
262 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 342 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
263 | arrange_and_commit(ws); | 343 | arrange_windows(ws); |
264 | } else { | 344 | } else { |
265 | arrange_and_commit(view->swayc->parent); | 345 | arrange_windows(view->swayc->parent); |
266 | } | 346 | } |
347 | transaction_commit_dirty(); | ||
267 | 348 | ||
268 | xdg_shell_view->commit.notify = handle_commit; | 349 | xdg_shell_view->commit.notify = handle_commit; |
269 | wl_signal_add(&xdg_surface->surface->events.commit, | 350 | wl_signal_add(&xdg_surface->surface->events.commit, |
@@ -276,6 +357,22 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
276 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; | 357 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; |
277 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, | 358 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, |
278 | &xdg_shell_view->request_fullscreen); | 359 | &xdg_shell_view->request_fullscreen); |
360 | |||
361 | xdg_shell_view->request_move.notify = handle_request_move; | ||
362 | wl_signal_add(&xdg_surface->toplevel->events.request_move, | ||
363 | &xdg_shell_view->request_move); | ||
364 | |||
365 | xdg_shell_view->request_resize.notify = handle_request_resize; | ||
366 | wl_signal_add(&xdg_surface->toplevel->events.request_resize, | ||
367 | &xdg_shell_view->request_resize); | ||
368 | |||
369 | xdg_shell_view->set_title.notify = handle_set_title; | ||
370 | wl_signal_add(&xdg_surface->toplevel->events.set_title, | ||
371 | &xdg_shell_view->set_title); | ||
372 | |||
373 | xdg_shell_view->set_app_id.notify = handle_set_app_id; | ||
374 | wl_signal_add(&xdg_surface->toplevel->events.set_app_id, | ||
375 | &xdg_shell_view->set_app_id); | ||
279 | } | 376 | } |
280 | 377 | ||
281 | static void handle_destroy(struct wl_listener *listener, void *data) { | 378 | static void handle_destroy(struct wl_listener *listener, void *data) { |
@@ -304,11 +401,11 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | |||
304 | struct wlr_xdg_surface *xdg_surface = data; | 401 | struct wlr_xdg_surface *xdg_surface = data; |
305 | 402 | ||
306 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { | 403 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { |
307 | wlr_log(L_DEBUG, "New xdg_shell popup"); | 404 | wlr_log(WLR_DEBUG, "New xdg_shell popup"); |
308 | return; | 405 | return; |
309 | } | 406 | } |
310 | 407 | ||
311 | wlr_log(L_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", | 408 | wlr_log(WLR_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", |
312 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); | 409 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); |
313 | wlr_xdg_surface_ping(xdg_surface); | 410 | wlr_xdg_surface_ping(xdg_surface); |
314 | 411 | ||
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index b28c4b9c..46fd4769 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 199309L | 1 | #define _POSIX_C_SOURCE 199309L |
2 | #include <float.h> | ||
2 | #include <stdbool.h> | 3 | #include <stdbool.h> |
3 | #include <stdlib.h> | 4 | #include <stdlib.h> |
4 | #include <wayland-server.h> | 5 | #include <wayland-server.h> |
@@ -44,6 +45,24 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) { | |||
44 | view_child_destroy(&popup->child); | 45 | view_child_destroy(&popup->child); |
45 | } | 46 | } |
46 | 47 | ||
48 | static void popup_unconstrain(struct sway_xdg_popup_v6 *popup) { | ||
49 | struct sway_view *view = popup->child.view; | ||
50 | struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup; | ||
51 | |||
52 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); | ||
53 | |||
54 | // the output box expressed in the coordinate system of the toplevel parent | ||
55 | // of the popup | ||
56 | struct wlr_box output_toplevel_sx_box = { | ||
57 | .x = output->x - view->x, | ||
58 | .y = output->y - view->y, | ||
59 | .width = output->width, | ||
60 | .height = output->height, | ||
61 | }; | ||
62 | |||
63 | wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); | ||
64 | } | ||
65 | |||
47 | static struct sway_xdg_popup_v6 *popup_create( | 66 | static struct sway_xdg_popup_v6 *popup_create( |
48 | struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) { | 67 | struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) { |
49 | struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base; | 68 | struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base; |
@@ -54,12 +73,15 @@ static struct sway_xdg_popup_v6 *popup_create( | |||
54 | return NULL; | 73 | return NULL; |
55 | } | 74 | } |
56 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); | 75 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); |
76 | popup->wlr_xdg_surface_v6 = xdg_surface; | ||
57 | 77 | ||
58 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); | 78 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); |
59 | popup->new_popup.notify = popup_handle_new_popup; | 79 | popup->new_popup.notify = popup_handle_new_popup; |
60 | wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); | 80 | wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); |
61 | popup->destroy.notify = popup_handle_destroy; | 81 | popup->destroy.notify = popup_handle_destroy; |
62 | 82 | ||
83 | popup_unconstrain(popup); | ||
84 | |||
63 | return popup; | 85 | return popup; |
64 | } | 86 | } |
65 | 87 | ||
@@ -73,6 +95,16 @@ static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view( | |||
73 | return (struct sway_xdg_shell_v6_view *)view; | 95 | return (struct sway_xdg_shell_v6_view *)view; |
74 | } | 96 | } |
75 | 97 | ||
98 | static void get_constraints(struct sway_view *view, double *min_width, | ||
99 | double *max_width, double *min_height, double *max_height) { | ||
100 | struct wlr_xdg_toplevel_v6_state *state = | ||
101 | &view->wlr_xdg_surface_v6->toplevel->current; | ||
102 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; | ||
103 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; | ||
104 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; | ||
105 | *max_height = state->max_height > 0 ? state->max_height : DBL_MAX; | ||
106 | } | ||
107 | |||
76 | static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { | 108 | static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { |
77 | if (xdg_shell_v6_view_from_view(view) == NULL) { | 109 | if (xdg_shell_v6_view_from_view(view) == NULL) { |
78 | return NULL; | 110 | return NULL; |
@@ -163,6 +195,7 @@ static void destroy(struct sway_view *view) { | |||
163 | } | 195 | } |
164 | 196 | ||
165 | static const struct sway_view_impl view_impl = { | 197 | static const struct sway_view_impl view_impl = { |
198 | .get_constraints = get_constraints, | ||
166 | .get_string_prop = get_string_prop, | 199 | .get_string_prop = get_string_prop, |
167 | .configure = configure, | 200 | .configure = configure, |
168 | .set_activated = set_activated, | 201 | .set_activated = set_activated, |
@@ -187,10 +220,24 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
187 | transaction_notify_view_ready(view, xdg_surface_v6->configure_serial); | 220 | transaction_notify_view_ready(view, xdg_surface_v6->configure_serial); |
188 | } | 221 | } |
189 | 222 | ||
190 | view_update_title(view, false); | ||
191 | view_damage_from(view); | 223 | view_damage_from(view); |
192 | } | 224 | } |
193 | 225 | ||
226 | static void handle_set_title(struct wl_listener *listener, void *data) { | ||
227 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
228 | wl_container_of(listener, xdg_shell_v6_view, set_title); | ||
229 | struct sway_view *view = &xdg_shell_v6_view->view; | ||
230 | view_update_title(view, false); | ||
231 | view_execute_criteria(view); | ||
232 | } | ||
233 | |||
234 | static void handle_set_app_id(struct wl_listener *listener, void *data) { | ||
235 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
236 | wl_container_of(listener, xdg_shell_v6_view, set_app_id); | ||
237 | struct sway_view *view = &xdg_shell_v6_view->view; | ||
238 | view_execute_criteria(view); | ||
239 | } | ||
240 | |||
194 | static void handle_new_popup(struct wl_listener *listener, void *data) { | 241 | static void handle_new_popup(struct wl_listener *listener, void *data) { |
195 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | 242 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = |
196 | wl_container_of(listener, xdg_shell_v6_view, new_popup); | 243 | wl_container_of(listener, xdg_shell_v6_view, new_popup); |
@@ -217,8 +264,37 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
217 | 264 | ||
218 | view_set_fullscreen(view, e->fullscreen); | 265 | view_set_fullscreen(view, e->fullscreen); |
219 | 266 | ||
220 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 267 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); |
221 | arrange_and_commit(ws); | 268 | arrange_windows(output); |
269 | transaction_commit_dirty(); | ||
270 | } | ||
271 | |||
272 | static void handle_request_move(struct wl_listener *listener, void *data) { | ||
273 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
274 | wl_container_of(listener, xdg_shell_v6_view, request_move); | ||
275 | struct sway_view *view = &xdg_shell_v6_view->view; | ||
276 | if (!container_is_floating(view->swayc)) { | ||
277 | return; | ||
278 | } | ||
279 | struct wlr_xdg_toplevel_v6_move_event *e = data; | ||
280 | struct sway_seat *seat = e->seat->seat->data; | ||
281 | if (e->serial == seat->last_button_serial) { | ||
282 | seat_begin_move(seat, view->swayc, seat->last_button); | ||
283 | } | ||
284 | } | ||
285 | |||
286 | static void handle_request_resize(struct wl_listener *listener, void *data) { | ||
287 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
288 | wl_container_of(listener, xdg_shell_v6_view, request_resize); | ||
289 | struct sway_view *view = &xdg_shell_v6_view->view; | ||
290 | if (!container_is_floating(view->swayc)) { | ||
291 | return; | ||
292 | } | ||
293 | struct wlr_xdg_toplevel_v6_resize_event *e = data; | ||
294 | struct sway_seat *seat = e->seat->seat->data; | ||
295 | if (e->serial == seat->last_button_serial) { | ||
296 | seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); | ||
297 | } | ||
222 | } | 298 | } |
223 | 299 | ||
224 | static void handle_unmap(struct wl_listener *listener, void *data) { | 300 | static void handle_unmap(struct wl_listener *listener, void *data) { |
@@ -235,6 +311,10 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
235 | wl_list_remove(&xdg_shell_v6_view->commit.link); | 311 | wl_list_remove(&xdg_shell_v6_view->commit.link); |
236 | wl_list_remove(&xdg_shell_v6_view->new_popup.link); | 312 | wl_list_remove(&xdg_shell_v6_view->new_popup.link); |
237 | wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); | 313 | wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); |
314 | wl_list_remove(&xdg_shell_v6_view->request_move.link); | ||
315 | wl_list_remove(&xdg_shell_v6_view->request_resize.link); | ||
316 | wl_list_remove(&xdg_shell_v6_view->set_title.link); | ||
317 | wl_list_remove(&xdg_shell_v6_view->set_app_id.link); | ||
238 | } | 318 | } |
239 | 319 | ||
240 | static void handle_map(struct wl_listener *listener, void *data) { | 320 | static void handle_map(struct wl_listener *listener, void *data) { |
@@ -246,8 +326,8 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
246 | view->natural_width = view->wlr_xdg_surface_v6->geometry.width; | 326 | view->natural_width = view->wlr_xdg_surface_v6->geometry.width; |
247 | view->natural_height = view->wlr_xdg_surface_v6->geometry.height; | 327 | view->natural_height = view->wlr_xdg_surface_v6->geometry.height; |
248 | if (!view->natural_width && !view->natural_height) { | 328 | if (!view->natural_width && !view->natural_height) { |
249 | view->natural_width = view->wlr_xdg_surface_v6->surface->current->width; | 329 | view->natural_width = view->wlr_xdg_surface_v6->surface->current.width; |
250 | view->natural_height = view->wlr_xdg_surface_v6->surface->current->height; | 330 | view->natural_height = view->wlr_xdg_surface_v6->surface->current.height; |
251 | } | 331 | } |
252 | 332 | ||
253 | view_map(view, view->wlr_xdg_surface_v6->surface); | 333 | view_map(view, view->wlr_xdg_surface_v6->surface); |
@@ -255,10 +335,11 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
255 | if (xdg_surface->toplevel->client_pending.fullscreen) { | 335 | if (xdg_surface->toplevel->client_pending.fullscreen) { |
256 | view_set_fullscreen(view, true); | 336 | view_set_fullscreen(view, true); |
257 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 337 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
258 | arrange_and_commit(ws); | 338 | arrange_windows(ws); |
259 | } else { | 339 | } else { |
260 | arrange_and_commit(view->swayc->parent); | 340 | arrange_windows(view->swayc->parent); |
261 | } | 341 | } |
342 | transaction_commit_dirty(); | ||
262 | 343 | ||
263 | xdg_shell_v6_view->commit.notify = handle_commit; | 344 | xdg_shell_v6_view->commit.notify = handle_commit; |
264 | wl_signal_add(&xdg_surface->surface->events.commit, | 345 | wl_signal_add(&xdg_surface->surface->events.commit, |
@@ -271,6 +352,22 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
271 | xdg_shell_v6_view->request_fullscreen.notify = handle_request_fullscreen; | 352 | xdg_shell_v6_view->request_fullscreen.notify = handle_request_fullscreen; |
272 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, | 353 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, |
273 | &xdg_shell_v6_view->request_fullscreen); | 354 | &xdg_shell_v6_view->request_fullscreen); |
355 | |||
356 | xdg_shell_v6_view->request_move.notify = handle_request_move; | ||
357 | wl_signal_add(&xdg_surface->toplevel->events.request_move, | ||
358 | &xdg_shell_v6_view->request_move); | ||
359 | |||
360 | xdg_shell_v6_view->request_resize.notify = handle_request_resize; | ||
361 | wl_signal_add(&xdg_surface->toplevel->events.request_resize, | ||
362 | &xdg_shell_v6_view->request_resize); | ||
363 | |||
364 | xdg_shell_v6_view->set_title.notify = handle_set_title; | ||
365 | wl_signal_add(&xdg_surface->toplevel->events.set_title, | ||
366 | &xdg_shell_v6_view->set_title); | ||
367 | |||
368 | xdg_shell_v6_view->set_app_id.notify = handle_set_app_id; | ||
369 | wl_signal_add(&xdg_surface->toplevel->events.set_app_id, | ||
370 | &xdg_shell_v6_view->set_app_id); | ||
274 | } | 371 | } |
275 | 372 | ||
276 | static void handle_destroy(struct wl_listener *listener, void *data) { | 373 | static void handle_destroy(struct wl_listener *listener, void *data) { |
@@ -295,11 +392,11 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { | |||
295 | struct wlr_xdg_surface_v6 *xdg_surface = data; | 392 | struct wlr_xdg_surface_v6 *xdg_surface = data; |
296 | 393 | ||
297 | if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { | 394 | if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { |
298 | wlr_log(L_DEBUG, "New xdg_shell_v6 popup"); | 395 | wlr_log(WLR_DEBUG, "New xdg_shell_v6 popup"); |
299 | return; | 396 | return; |
300 | } | 397 | } |
301 | 398 | ||
302 | wlr_log(L_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'", | 399 | wlr_log(WLR_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'", |
303 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); | 400 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); |
304 | wlr_xdg_surface_v6_ping(xdg_surface); | 401 | wlr_xdg_surface_v6_ping(xdg_surface); |
305 | 402 | ||
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index b3b1473d..65d4fcd4 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c | |||
@@ -69,16 +69,11 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { | |||
69 | surface->ly = xsurface->y; | 69 | surface->ly = xsurface->y; |
70 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); | 70 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); |
71 | 71 | ||
72 | if (!wlr_xwayland_surface_is_unmanaged(xsurface)) { | 72 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
73 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 73 | struct wlr_xwayland *xwayland = |
74 | struct wlr_xwayland *xwayland = | 74 | seat->input->server->xwayland.wlr_xwayland; |
75 | seat->input->server->xwayland.wlr_xwayland; | 75 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); |
76 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | 76 | seat_set_focus_surface(seat, xsurface->surface, false); |
77 | seat_set_focus_surface(seat, xsurface->surface); | ||
78 | } | ||
79 | |||
80 | // TODO: we don't send surface enter/leave events to xwayland unmanaged | ||
81 | // surfaces, but xwayland doesn't support HiDPI anyway | ||
82 | } | 77 | } |
83 | 78 | ||
84 | static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | 79 | static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { |
@@ -89,18 +84,16 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | |||
89 | wl_list_remove(&surface->link); | 84 | wl_list_remove(&surface->link); |
90 | wl_list_remove(&surface->commit.link); | 85 | wl_list_remove(&surface->commit.link); |
91 | 86 | ||
92 | if (!wlr_xwayland_surface_is_unmanaged(xsurface)) { | 87 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
93 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 88 | if (seat->wlr_seat->keyboard_state.focused_surface == |
94 | if (seat->wlr_seat->keyboard_state.focused_surface == | 89 | xsurface->surface) { |
95 | xsurface->surface) { | 90 | // Restore focus |
96 | // Restore focus | 91 | struct sway_container *previous = |
97 | struct sway_container *previous = | 92 | seat_get_focus_inactive(seat, &root_container); |
98 | seat_get_focus_inactive(seat, &root_container); | 93 | if (previous) { |
99 | if (previous) { | 94 | // Hack to get seat to re-focus the return value of get_focus |
100 | // Hack to get seat to re-focus the return value of get_focus | 95 | seat_set_focus(seat, previous->parent); |
101 | seat_set_focus(seat, previous->parent); | 96 | seat_set_focus(seat, previous); |
102 | seat_set_focus(seat, previous); | ||
103 | } | ||
104 | } | 97 | } |
105 | } | 98 | } |
106 | } | 99 | } |
@@ -119,7 +112,7 @@ static struct sway_xwayland_unmanaged *create_unmanaged( | |||
119 | struct sway_xwayland_unmanaged *surface = | 112 | struct sway_xwayland_unmanaged *surface = |
120 | calloc(1, sizeof(struct sway_xwayland_unmanaged)); | 113 | calloc(1, sizeof(struct sway_xwayland_unmanaged)); |
121 | if (surface == NULL) { | 114 | if (surface == NULL) { |
122 | wlr_log(L_ERROR, "Allocation failed"); | 115 | wlr_log(WLR_ERROR, "Allocation failed"); |
123 | return NULL; | 116 | return NULL; |
124 | } | 117 | } |
125 | 118 | ||
@@ -246,6 +239,14 @@ static bool wants_floating(struct sway_view *view) { | |||
246 | return false; | 239 | return false; |
247 | } | 240 | } |
248 | 241 | ||
242 | static bool has_client_side_decorations(struct sway_view *view) { | ||
243 | if (xwayland_view_from_view(view) == NULL) { | ||
244 | return false; | ||
245 | } | ||
246 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; | ||
247 | return surface->decorations != WLR_XWAYLAND_SURFACE_DECORATIONS_ALL; | ||
248 | } | ||
249 | |||
249 | static void _close(struct sway_view *view) { | 250 | static void _close(struct sway_view *view) { |
250 | if (xwayland_view_from_view(view) == NULL) { | 251 | if (xwayland_view_from_view(view) == NULL) { |
251 | return; | 252 | return; |
@@ -269,6 +270,7 @@ static const struct sway_view_impl view_impl = { | |||
269 | .set_tiled = set_tiled, | 270 | .set_tiled = set_tiled, |
270 | .set_fullscreen = set_fullscreen, | 271 | .set_fullscreen = set_fullscreen, |
271 | .wants_floating = wants_floating, | 272 | .wants_floating = wants_floating, |
273 | .has_client_side_decorations = has_client_side_decorations, | ||
272 | .close = _close, | 274 | .close = _close, |
273 | .destroy = destroy, | 275 | .destroy = destroy, |
274 | }; | 276 | }; |
@@ -278,15 +280,42 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
278 | wl_container_of(listener, xwayland_view, commit); | 280 | wl_container_of(listener, xwayland_view, commit); |
279 | struct sway_view *view = &xwayland_view->view; | 281 | struct sway_view *view = &xwayland_view->view; |
280 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 282 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
281 | struct wlr_surface_state *surface_state = xsurface->surface->current; | 283 | struct wlr_surface_state *surface_state = &xsurface->surface->current; |
282 | 284 | ||
283 | if (view->swayc->instructions->length) { | 285 | if (view->swayc->instructions->length) { |
284 | transaction_notify_view_ready_by_size(view, | 286 | transaction_notify_view_ready_by_size(view, |
285 | surface_state->width, surface_state->height); | 287 | surface_state->width, surface_state->height); |
288 | } else if (container_is_floating(view->swayc)) { | ||
289 | view_update_size(view, surface_state->width, surface_state->height); | ||
286 | } | 290 | } |
291 | |||
287 | view_damage_from(view); | 292 | view_damage_from(view); |
288 | } | 293 | } |
289 | 294 | ||
295 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
296 | struct sway_xwayland_view *xwayland_view = | ||
297 | wl_container_of(listener, xwayland_view, destroy); | ||
298 | struct sway_view *view = &xwayland_view->view; | ||
299 | |||
300 | if (view->surface) { | ||
301 | view_unmap(view); | ||
302 | wl_list_remove(&xwayland_view->commit.link); | ||
303 | } | ||
304 | |||
305 | wl_list_remove(&xwayland_view->destroy.link); | ||
306 | wl_list_remove(&xwayland_view->request_configure.link); | ||
307 | wl_list_remove(&xwayland_view->request_fullscreen.link); | ||
308 | wl_list_remove(&xwayland_view->request_move.link); | ||
309 | wl_list_remove(&xwayland_view->request_resize.link); | ||
310 | wl_list_remove(&xwayland_view->set_title.link); | ||
311 | wl_list_remove(&xwayland_view->set_class.link); | ||
312 | wl_list_remove(&xwayland_view->set_window_type.link); | ||
313 | wl_list_remove(&xwayland_view->set_hints.link); | ||
314 | wl_list_remove(&xwayland_view->map.link); | ||
315 | wl_list_remove(&xwayland_view->unmap.link); | ||
316 | view_destroy(&xwayland_view->view); | ||
317 | } | ||
318 | |||
290 | static void handle_unmap(struct wl_listener *listener, void *data) { | 319 | static void handle_unmap(struct wl_listener *listener, void *data) { |
291 | struct sway_xwayland_view *xwayland_view = | 320 | struct sway_xwayland_view *xwayland_view = |
292 | wl_container_of(listener, xwayland_view, unmap); | 321 | wl_container_of(listener, xwayland_view, unmap); |
@@ -307,6 +336,15 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
307 | struct wlr_xwayland_surface *xsurface = data; | 336 | struct wlr_xwayland_surface *xsurface = data; |
308 | struct sway_view *view = &xwayland_view->view; | 337 | struct sway_view *view = &xwayland_view->view; |
309 | 338 | ||
339 | if (xsurface->override_redirect) { | ||
340 | // This window used not to have the override redirect flag and has it | ||
341 | // now. Switch to unmanaged. | ||
342 | handle_destroy(&xwayland_view->destroy, view); | ||
343 | struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); | ||
344 | unmanaged_handle_map(&unmanaged->map, xsurface); | ||
345 | return; | ||
346 | } | ||
347 | |||
310 | view->natural_width = xsurface->width; | 348 | view->natural_width = xsurface->width; |
311 | view->natural_height = xsurface->height; | 349 | view->natural_height = xsurface->height; |
312 | 350 | ||
@@ -321,31 +359,11 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
321 | if (xsurface->fullscreen) { | 359 | if (xsurface->fullscreen) { |
322 | view_set_fullscreen(view, true); | 360 | view_set_fullscreen(view, true); |
323 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 361 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
324 | arrange_and_commit(ws); | 362 | arrange_windows(ws); |
325 | } else { | 363 | } else { |
326 | arrange_and_commit(view->swayc->parent); | 364 | arrange_windows(view->swayc->parent); |
327 | } | ||
328 | } | ||
329 | |||
330 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
331 | struct sway_xwayland_view *xwayland_view = | ||
332 | wl_container_of(listener, xwayland_view, destroy); | ||
333 | struct sway_view *view = &xwayland_view->view; | ||
334 | |||
335 | if (view->surface) { | ||
336 | view_unmap(view); | ||
337 | wl_list_remove(&xwayland_view->commit.link); | ||
338 | } | 365 | } |
339 | 366 | transaction_commit_dirty(); | |
340 | wl_list_remove(&xwayland_view->destroy.link); | ||
341 | wl_list_remove(&xwayland_view->request_configure.link); | ||
342 | wl_list_remove(&xwayland_view->request_fullscreen.link); | ||
343 | wl_list_remove(&xwayland_view->set_title.link); | ||
344 | wl_list_remove(&xwayland_view->set_class.link); | ||
345 | wl_list_remove(&xwayland_view->set_window_type.link); | ||
346 | wl_list_remove(&xwayland_view->map.link); | ||
347 | wl_list_remove(&xwayland_view->unmap.link); | ||
348 | view_destroy(&xwayland_view->view); | ||
349 | } | 367 | } |
350 | 368 | ||
351 | static void handle_request_configure(struct wl_listener *listener, void *data) { | 369 | static void handle_request_configure(struct wl_listener *listener, void *data) { |
@@ -379,8 +397,40 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
379 | } | 397 | } |
380 | view_set_fullscreen(view, xsurface->fullscreen); | 398 | view_set_fullscreen(view, xsurface->fullscreen); |
381 | 399 | ||
382 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 400 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); |
383 | arrange_and_commit(ws); | 401 | arrange_windows(output); |
402 | transaction_commit_dirty(); | ||
403 | } | ||
404 | |||
405 | static void handle_request_move(struct wl_listener *listener, void *data) { | ||
406 | struct sway_xwayland_view *xwayland_view = | ||
407 | wl_container_of(listener, xwayland_view, request_move); | ||
408 | struct sway_view *view = &xwayland_view->view; | ||
409 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
410 | if (!xsurface->mapped) { | ||
411 | return; | ||
412 | } | ||
413 | if (!container_is_floating(view->swayc)) { | ||
414 | return; | ||
415 | } | ||
416 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
417 | seat_begin_move(seat, view->swayc, seat->last_button); | ||
418 | } | ||
419 | |||
420 | static void handle_request_resize(struct wl_listener *listener, void *data) { | ||
421 | struct sway_xwayland_view *xwayland_view = | ||
422 | wl_container_of(listener, xwayland_view, request_resize); | ||
423 | struct sway_view *view = &xwayland_view->view; | ||
424 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
425 | if (!xsurface->mapped) { | ||
426 | return; | ||
427 | } | ||
428 | if (!container_is_floating(view->swayc)) { | ||
429 | return; | ||
430 | } | ||
431 | struct wlr_xwayland_resize_event *e = data; | ||
432 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
433 | seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); | ||
384 | } | 434 | } |
385 | 435 | ||
386 | static void handle_set_title(struct wl_listener *listener, void *data) { | 436 | static void handle_set_title(struct wl_listener *listener, void *data) { |
@@ -417,6 +467,25 @@ static void handle_set_window_type(struct wl_listener *listener, void *data) { | |||
417 | view_execute_criteria(view); | 467 | view_execute_criteria(view); |
418 | } | 468 | } |
419 | 469 | ||
470 | static void handle_set_hints(struct wl_listener *listener, void *data) { | ||
471 | struct sway_xwayland_view *xwayland_view = | ||
472 | wl_container_of(listener, xwayland_view, set_hints); | ||
473 | struct sway_view *view = &xwayland_view->view; | ||
474 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
475 | if (!xsurface->mapped) { | ||
476 | return; | ||
477 | } | ||
478 | if (!xsurface->hints_urgency && view->urgent_timer) { | ||
479 | // The view is is in the timeout period. We'll ignore the request to | ||
480 | // unset urgency so that the view remains urgent until the timer clears | ||
481 | // it. | ||
482 | return; | ||
483 | } | ||
484 | if (view->allow_request_urgent) { | ||
485 | view_set_urgent(view, (bool)xsurface->hints_urgency); | ||
486 | } | ||
487 | } | ||
488 | |||
420 | struct sway_view *view_from_wlr_xwayland_surface( | 489 | struct sway_view *view_from_wlr_xwayland_surface( |
421 | struct wlr_xwayland_surface *xsurface) { | 490 | struct wlr_xwayland_surface *xsurface) { |
422 | return xsurface->data; | 491 | return xsurface->data; |
@@ -427,14 +496,13 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { | |||
427 | xwayland_surface); | 496 | xwayland_surface); |
428 | struct wlr_xwayland_surface *xsurface = data; | 497 | struct wlr_xwayland_surface *xsurface = data; |
429 | 498 | ||
430 | if (wlr_xwayland_surface_is_unmanaged(xsurface) || | 499 | if (xsurface->override_redirect) { |
431 | xsurface->override_redirect) { | 500 | wlr_log(WLR_DEBUG, "New xwayland unmanaged surface"); |
432 | wlr_log(L_DEBUG, "New xwayland unmanaged surface"); | ||
433 | create_unmanaged(xsurface); | 501 | create_unmanaged(xsurface); |
434 | return; | 502 | return; |
435 | } | 503 | } |
436 | 504 | ||
437 | wlr_log(L_DEBUG, "New xwayland surface title='%s' class='%s'", | 505 | wlr_log(WLR_DEBUG, "New xwayland surface title='%s' class='%s'", |
438 | xsurface->title, xsurface->class); | 506 | xsurface->title, xsurface->class); |
439 | 507 | ||
440 | struct sway_xwayland_view *xwayland_view = | 508 | struct sway_xwayland_view *xwayland_view = |
@@ -457,6 +525,14 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { | |||
457 | &xwayland_view->request_fullscreen); | 525 | &xwayland_view->request_fullscreen); |
458 | xwayland_view->request_fullscreen.notify = handle_request_fullscreen; | 526 | xwayland_view->request_fullscreen.notify = handle_request_fullscreen; |
459 | 527 | ||
528 | wl_signal_add(&xsurface->events.request_move, | ||
529 | &xwayland_view->request_move); | ||
530 | xwayland_view->request_move.notify = handle_request_move; | ||
531 | |||
532 | wl_signal_add(&xsurface->events.request_resize, | ||
533 | &xwayland_view->request_resize); | ||
534 | xwayland_view->request_resize.notify = handle_request_resize; | ||
535 | |||
460 | wl_signal_add(&xsurface->events.set_title, &xwayland_view->set_title); | 536 | wl_signal_add(&xsurface->events.set_title, &xwayland_view->set_title); |
461 | xwayland_view->set_title.notify = handle_set_title; | 537 | xwayland_view->set_title.notify = handle_set_title; |
462 | 538 | ||
@@ -467,6 +543,9 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { | |||
467 | &xwayland_view->set_window_type); | 543 | &xwayland_view->set_window_type); |
468 | xwayland_view->set_window_type.notify = handle_set_window_type; | 544 | xwayland_view->set_window_type.notify = handle_set_window_type; |
469 | 545 | ||
546 | wl_signal_add(&xsurface->events.set_hints, &xwayland_view->set_hints); | ||
547 | xwayland_view->set_hints.notify = handle_set_hints; | ||
548 | |||
470 | wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); | 549 | wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); |
471 | xwayland_view->unmap.notify = handle_unmap; | 550 | xwayland_view->unmap.notify = handle_unmap; |
472 | 551 | ||
@@ -484,7 +563,7 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) { | |||
484 | xcb_connection_t *xcb_conn = xcb_connect(NULL, NULL); | 563 | xcb_connection_t *xcb_conn = xcb_connect(NULL, NULL); |
485 | int err = xcb_connection_has_error(xcb_conn); | 564 | int err = xcb_connection_has_error(xcb_conn); |
486 | if (err) { | 565 | if (err) { |
487 | wlr_log(L_ERROR, "XCB connect failed: %d", err); | 566 | wlr_log(WLR_ERROR, "XCB connect failed: %d", err); |
488 | return; | 567 | return; |
489 | } | 568 | } |
490 | 569 | ||
@@ -503,7 +582,7 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) { | |||
503 | free(reply); | 582 | free(reply); |
504 | 583 | ||
505 | if (error != NULL) { | 584 | if (error != NULL) { |
506 | wlr_log(L_ERROR, "could not resolve atom %s, X11 error code %d", | 585 | wlr_log(WLR_ERROR, "could not resolve atom %s, X11 error code %d", |
507 | atom_map[i], error->error_code); | 586 | atom_map[i], error->error_code); |
508 | free(error); | 587 | free(error); |
509 | break; | 588 | break; |