summaryrefslogtreecommitdiffstats
path: root/sway/desktop
diff options
context:
space:
mode:
Diffstat (limited to 'sway/desktop')
-rw-r--r--sway/desktop/desktop.c14
-rw-r--r--sway/desktop/layer_shell.c347
-rw-r--r--sway/desktop/output.c579
-rw-r--r--sway/desktop/wl_shell.c131
-rw-r--r--sway/desktop/xdg_shell_v6.c249
-rw-r--r--sway/desktop/xwayland.c310
6 files changed, 1630 insertions, 0 deletions
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c
new file mode 100644
index 00000000..66f33151
--- /dev/null
+++ b/sway/desktop/desktop.c
@@ -0,0 +1,14 @@
1#include "sway/tree/container.h"
2#include "sway/desktop.h"
3#include "sway/output.h"
4
5void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
6 bool whole) {
7 for (int i = 0; i < root_container.children->length; ++i) {
8 struct sway_container *cont = root_container.children->items[i];
9 if (cont->type == C_OUTPUT) {
10 output_damage_surface(cont->sway_output, lx - cont->x, ly - cont->y,
11 surface, whole);
12 }
13 }
14}
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
new file mode 100644
index 00000000..f841e5f1
--- /dev/null
+++ b/sway/desktop/layer_shell.c
@@ -0,0 +1,347 @@
1#include <stdbool.h>
2#include <stdlib.h>
3#include <string.h>
4#include <wayland-server.h>
5#include <wlr/types/wlr_box.h>
6#include <wlr/types/wlr_layer_shell.h>
7#include <wlr/types/wlr_output_damage.h>
8#include <wlr/types/wlr_output.h>
9#include <wlr/util/log.h>
10#include "sway/input/input-manager.h"
11#include "sway/input/seat.h"
12#include "sway/layers.h"
13#include "sway/output.h"
14#include "sway/server.h"
15#include "sway/tree/layout.h"
16
17static void apply_exclusive(struct wlr_box *usable_area,
18 uint32_t anchor, int32_t exclusive,
19 int32_t margin_top, int32_t margin_right,
20 int32_t margin_bottom, int32_t margin_left) {
21 if (exclusive <= 0) {
22 return;
23 }
24 struct {
25 uint32_t anchors;
26 int *positive_axis;
27 int *negative_axis;
28 int margin;
29 } edges[] = {
30 {
31 .anchors =
32 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
33 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
34 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP,
35 .positive_axis = &usable_area->y,
36 .negative_axis = &usable_area->height,
37 .margin = margin_top,
38 },
39 {
40 .anchors =
41 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
42 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
43 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
44 .positive_axis = NULL,
45 .negative_axis = &usable_area->height,
46 .margin = margin_bottom,
47 },
48 {
49 .anchors =
50 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
51 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
52 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
53 .positive_axis = &usable_area->x,
54 .negative_axis = &usable_area->width,
55 .margin = margin_left,
56 },
57 {
58 .anchors =
59 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
60 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
61 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
62 .positive_axis = NULL,
63 .negative_axis = &usable_area->width,
64 .margin = margin_right,
65 },
66 };
67 for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) {
68 if ((anchor & edges[i].anchors) == edges[i].anchors) {
69 if (edges[i].positive_axis) {
70 *edges[i].positive_axis += exclusive + edges[i].margin;
71 }
72 if (edges[i].negative_axis) {
73 *edges[i].negative_axis -= exclusive + edges[i].margin;
74 }
75 }
76 }
77}
78
79static void arrange_layer(struct sway_output *output, struct wl_list *list,
80 struct wlr_box *usable_area, bool exclusive) {
81 struct sway_layer_surface *sway_layer;
82 struct wlr_box full_area = { 0 };
83 wlr_output_effective_resolution(output->wlr_output,
84 &full_area.width, &full_area.height);
85 wl_list_for_each(sway_layer, list, link) {
86 struct wlr_layer_surface *layer = sway_layer->layer_surface;
87 struct wlr_layer_surface_state *state = &layer->current;
88 if (exclusive != (state->exclusive_zone > 0)) {
89 continue;
90 }
91 struct wlr_box bounds;
92 if (state->exclusive_zone == -1) {
93 bounds = full_area;
94 } else {
95 bounds = *usable_area;
96 }
97 struct wlr_box box = {
98 .width = state->desired_width,
99 .height = state->desired_height
100 };
101 // Horizontal axis
102 const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
103 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
104 if ((state->anchor & both_horiz) && box.width == 0) {
105 box.x = bounds.x;
106 box.width = bounds.width;
107 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
108 box.x = bounds.x;
109 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
110 box.x = bounds.x + (bounds.width - box.width);
111 } else {
112 box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
113 }
114 // Vertical axis
115 const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
116 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
117 if ((state->anchor & both_vert) && box.height == 0) {
118 box.y = bounds.y;
119 box.height = bounds.height;
120 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
121 box.y = bounds.y;
122 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
123 box.y = bounds.y + (bounds.height - box.height);
124 } else {
125 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
126 }
127 // Margin
128 if ((state->anchor & both_horiz) == both_horiz) {
129 box.x += state->margin.left;
130 box.width -= state->margin.left + state->margin.right;
131 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
132 box.x += state->margin.left;
133 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
134 box.x -= state->margin.right;
135 }
136 if ((state->anchor & both_vert) == both_vert) {
137 box.y += state->margin.top;
138 box.height -= state->margin.top + state->margin.bottom;
139 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
140 box.y += state->margin.top;
141 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
142 box.y -= state->margin.bottom;
143 }
144 if (box.width < 0 || box.height < 0) {
145 // TODO: Bubble up a protocol error?
146 wlr_layer_surface_close(layer);
147 continue;
148 }
149 // Apply
150 sway_layer->geo = box;
151 apply_exclusive(usable_area, state->anchor, state->exclusive_zone,
152 state->margin.top, state->margin.right,
153 state->margin.bottom, state->margin.left);
154 wlr_layer_surface_configure(layer, box.width, box.height);
155 }
156}
157
158void arrange_layers(struct sway_output *output) {
159 struct wlr_box usable_area = { 0 };
160 wlr_output_effective_resolution(output->wlr_output,
161 &usable_area.width, &usable_area.height);
162
163 // Arrange exclusive surfaces from top->bottom
164 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
165 &usable_area, true);
166 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
167 &usable_area, true);
168 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
169 &usable_area, true);
170 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
171 &usable_area, true);
172
173 if (memcmp(&usable_area, &output->usable_area,
174 sizeof(struct wlr_box)) != 0) {
175 wlr_log(L_DEBUG, "Usable area changed, rearranging output");
176 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
177 arrange_windows(output->swayc, -1, -1);
178 }
179
180 // Arrange non-exlusive surfaces from top->bottom
181 usable_area.x = usable_area.y = 0;
182 wlr_output_effective_resolution(output->wlr_output,
183 &usable_area.width, &usable_area.height);
184 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
185 &usable_area, false);
186 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
187 &usable_area, false);
188 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
189 &usable_area, false);
190 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
191 &usable_area, false);
192
193 // Find topmost keyboard interactive layer, if such a layer exists
194 uint32_t layers_above_shell[] = {
195 ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
196 ZWLR_LAYER_SHELL_V1_LAYER_TOP,
197 };
198 size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]);
199 struct sway_layer_surface *layer, *topmost = NULL;
200 for (size_t i = 0; i < nlayers; ++i) {
201 wl_list_for_each_reverse(layer,
202 &output->layers[layers_above_shell[i]], link) {
203 if (layer->layer_surface->current.keyboard_interactive) {
204 topmost = layer;
205 break;
206 }
207 }
208 if (topmost != NULL) {
209 break;
210 }
211 }
212
213 struct sway_seat *seat;
214 wl_list_for_each(seat, &input_manager->seats, link) {
215 seat_set_focus_layer(seat, topmost ? topmost->layer_surface : NULL);
216 }
217}
218
219static void handle_output_destroy(struct wl_listener *listener, void *data) {
220 struct sway_layer_surface *sway_layer =
221 wl_container_of(listener, sway_layer, output_destroy);
222 wl_list_remove(&sway_layer->output_destroy.link);
223 sway_layer->layer_surface->output = NULL;
224 wlr_layer_surface_close(sway_layer->layer_surface);
225}
226
227static void handle_surface_commit(struct wl_listener *listener, void *data) {
228 struct sway_layer_surface *layer =
229 wl_container_of(listener, layer, surface_commit);
230 struct wlr_layer_surface *layer_surface = layer->layer_surface;
231 struct wlr_output *wlr_output = layer_surface->output;
232 if (wlr_output == NULL) {
233 return;
234 }
235
236 struct sway_output *output = wlr_output->data;
237 struct wlr_box old_geo = layer->geo;
238 arrange_layers(output);
239 if (memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0) {
240 output_damage_surface(output, old_geo.x, old_geo.y,
241 layer_surface->surface, true);
242 output_damage_surface(output, layer->geo.x, layer->geo.y,
243 layer_surface->surface, true);
244 } else {
245 output_damage_surface(output, layer->geo.x, layer->geo.y,
246 layer_surface->surface, false);
247 }
248}
249
250static void unmap(struct sway_layer_surface *sway_layer) {
251 struct wlr_output *wlr_output = sway_layer->layer_surface->output;
252 if (wlr_output == NULL) {
253 return;
254 }
255 struct sway_output *output = wlr_output->data;
256 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
257 sway_layer->layer_surface->surface, true);
258}
259
260static void handle_destroy(struct wl_listener *listener, void *data) {
261 struct sway_layer_surface *sway_layer =
262 wl_container_of(listener, sway_layer, destroy);
263 wlr_log(L_DEBUG, "Layer surface destroyed (%s)",
264 sway_layer->layer_surface->namespace);
265 if (sway_layer->layer_surface->mapped) {
266 unmap(sway_layer);
267 }
268 wl_list_remove(&sway_layer->link);
269 wl_list_remove(&sway_layer->destroy.link);
270 wl_list_remove(&sway_layer->map.link);
271 wl_list_remove(&sway_layer->unmap.link);
272 wl_list_remove(&sway_layer->surface_commit.link);
273 if (sway_layer->layer_surface->output != NULL) {
274 struct sway_output *output = sway_layer->layer_surface->output->data;
275 arrange_layers(output);
276
277 wl_list_remove(&sway_layer->output_destroy.link);
278 }
279 free(sway_layer);
280}
281
282static void handle_map(struct wl_listener *listener, void *data) {
283 struct sway_layer_surface *sway_layer = wl_container_of(listener,
284 sway_layer, map);
285 struct sway_output *output = sway_layer->layer_surface->output->data;
286 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
287 sway_layer->layer_surface->surface, true);
288 // TODO: send enter to subsurfaces and popups
289 wlr_surface_send_enter(sway_layer->layer_surface->surface,
290 sway_layer->layer_surface->output);
291}
292
293static void handle_unmap(struct wl_listener *listener, void *data) {
294 struct sway_layer_surface *sway_layer = wl_container_of(
295 listener, sway_layer, unmap);
296 unmap(sway_layer);
297}
298
299void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
300 struct wlr_layer_surface *layer_surface = data;
301 struct sway_server *server =
302 wl_container_of(listener, server, layer_shell_surface);
303 wlr_log(L_DEBUG, "new layer surface: namespace %s layer %d anchor %d "
304 "size %dx%d margin %d,%d,%d,%d",
305 layer_surface->namespace, layer_surface->layer, layer_surface->layer,
306 layer_surface->client_pending.desired_width,
307 layer_surface->client_pending.desired_height,
308 layer_surface->client_pending.margin.top,
309 layer_surface->client_pending.margin.right,
310 layer_surface->client_pending.margin.bottom,
311 layer_surface->client_pending.margin.left);
312
313 struct sway_layer_surface *sway_layer =
314 calloc(1, sizeof(struct sway_layer_surface));
315 if (!sway_layer) {
316 return;
317 }
318
319 sway_layer->surface_commit.notify = handle_surface_commit;
320 wl_signal_add(&layer_surface->surface->events.commit,
321 &sway_layer->surface_commit);
322
323 sway_layer->output_destroy.notify = handle_output_destroy;
324 wl_signal_add(&layer_surface->output->events.destroy,
325 &sway_layer->output_destroy);
326
327 sway_layer->destroy.notify = handle_destroy;
328 wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy);
329 sway_layer->map.notify = handle_map;
330 wl_signal_add(&layer_surface->events.map, &sway_layer->map);
331 sway_layer->unmap.notify = handle_unmap;
332 wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap);
333 // TODO: Listen for subsurfaces
334
335 sway_layer->layer_surface = layer_surface;
336 layer_surface->data = sway_layer;
337
338 struct sway_output *output = layer_surface->output->data;
339 wl_list_insert(&output->layers[layer_surface->layer], &sway_layer->link);
340
341 // Temporarily set the layer's current state to client_pending
342 // So that we can easily arrange it
343 struct wlr_layer_surface_state old_state = layer_surface->current;
344 layer_surface->current = layer_surface->client_pending;
345 arrange_layers(output);
346 layer_surface->current = old_state;
347}
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
new file mode 100644
index 00000000..1b3143d0
--- /dev/null
+++ b/sway/desktop/output.c
@@ -0,0 +1,579 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
3#include <stdlib.h>
4#include <strings.h>
5#include <time.h>
6#include <wayland-server.h>
7#include <wlr/render/wlr_renderer.h>
8#include <wlr/types/wlr_box.h>
9#include <wlr/types/wlr_matrix.h>
10#include <wlr/types/wlr_output_damage.h>
11#include <wlr/types/wlr_output_layout.h>
12#include <wlr/types/wlr_output.h>
13#include <wlr/types/wlr_surface.h>
14#include <wlr/types/wlr_wl_shell.h>
15#include <wlr/util/region.h>
16#include "log.h"
17#include "sway/input/input-manager.h"
18#include "sway/input/seat.h"
19#include "sway/layers.h"
20#include "sway/output.h"
21#include "sway/server.h"
22#include "sway/tree/container.h"
23#include "sway/tree/layout.h"
24#include "sway/tree/view.h"
25
26struct sway_container *output_by_name(const char *name) {
27 for (int i = 0; i < root_container.children->length; ++i) {
28 struct sway_container *output = root_container.children->items[i];
29 if (strcasecmp(output->name, name) == 0) {
30 return output;
31 }
32 }
33 return NULL;
34}
35
36/**
37 * Rotate a child's position relative to a parent. The parent size is (pw, ph),
38 * the child position is (*sx, *sy) and its size is (sw, sh).
39 */
40static void rotate_child_position(double *sx, double *sy, double sw, double sh,
41 double pw, double ph, float rotation) {
42 if (rotation == 0.0f) {
43 return;
44 }
45
46 // Coordinates relative to the center of the subsurface
47 double ox = *sx - pw/2 + sw/2,
48 oy = *sy - ph/2 + sh/2;
49 // Rotated coordinates
50 double rx = cos(-rotation)*ox - sin(-rotation)*oy,
51 ry = cos(-rotation)*oy + sin(-rotation)*ox;
52 *sx = rx + pw/2 - sw/2;
53 *sy = ry + ph/2 - sh/2;
54}
55
56/**
57 * Contains a surface's root geometry information. For instance, when rendering
58 * a popup, this will contain the parent view's position and size.
59 */
60struct root_geometry {
61 double x, y;
62 int width, height;
63 float rotation;
64};
65
66static bool get_surface_box(struct root_geometry *geo,
67 struct sway_output *output, struct wlr_surface *surface, int sx, int sy,
68 struct wlr_box *surface_box) {
69 if (!wlr_surface_has_buffer(surface)) {
70 return false;
71 }
72
73 int sw = surface->current->width;
74 int sh = surface->current->height;
75
76 double _sx = sx, _sy = sy;
77 rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height,
78 geo->rotation);
79
80 struct wlr_box box = {
81 .x = geo->x + _sx,
82 .y = geo->y + _sy,
83 .width = sw,
84 .height = sh,
85 };
86 if (surface_box != NULL) {
87 memcpy(surface_box, &box, sizeof(struct wlr_box));
88 }
89
90 struct wlr_box rotated_box;
91 wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box);
92
93 struct wlr_box output_box = {
94 .width = output->swayc->width,
95 .height = output->swayc->height,
96 };
97
98 struct wlr_box intersection;
99 return wlr_box_intersection(&output_box, &rotated_box, &intersection);
100}
101
102static void surface_for_each_surface(struct wlr_surface *surface,
103 double ox, double oy, struct root_geometry *geo,
104 wlr_surface_iterator_func_t iterator, void *user_data) {
105 geo->x = ox;
106 geo->y = oy;
107 geo->width = surface->current->width;
108 geo->height = surface->current->height;
109 geo->rotation = 0;
110
111 wlr_surface_for_each_surface(surface, iterator, user_data);
112}
113
114static void output_view_for_each_surface(struct sway_view *view,
115 struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
116 void *user_data) {
117 geo->x = view->swayc->x;
118 geo->y = view->swayc->y;
119 geo->width = view->surface->current->width;
120 geo->height = view->surface->current->height;
121 geo->rotation = 0; // TODO
122
123 view_for_each_surface(view, iterator, user_data);
124}
125
126static void layer_for_each_surface(struct wl_list *layer_surfaces,
127 struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
128 void *user_data) {
129 struct sway_layer_surface *layer_surface;
130 wl_list_for_each(layer_surface, layer_surfaces, link) {
131 struct wlr_layer_surface *wlr_layer_surface =
132 layer_surface->layer_surface;
133 surface_for_each_surface(wlr_layer_surface->surface,
134 layer_surface->geo.x, layer_surface->geo.y, geo, iterator,
135 user_data);
136 }
137}
138
139static void unmanaged_for_each_surface(struct wl_list *unmanaged,
140 struct sway_output *output, struct root_geometry *geo,
141 wlr_surface_iterator_func_t iterator, void *user_data) {
142 struct sway_xwayland_unmanaged *unmanaged_surface;
143 wl_list_for_each(unmanaged_surface, unmanaged, link) {
144 struct wlr_xwayland_surface *xsurface =
145 unmanaged_surface->wlr_xwayland_surface;
146 double ox = unmanaged_surface->lx - output->swayc->x;
147 double oy = unmanaged_surface->ly - output->swayc->y;
148
149 surface_for_each_surface(xsurface->surface, ox, oy, geo,
150 iterator, user_data);
151 }
152}
153
154static void scale_box(struct wlr_box *box, float scale) {
155 box->x *= scale;
156 box->y *= scale;
157 box->width *= scale;
158 box->height *= scale;
159}
160
161struct render_data {
162 struct root_geometry root_geo;
163 struct sway_output *output;
164 float alpha;
165};
166
167static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy,
168 void *_data) {
169 struct render_data *data = _data;
170 struct wlr_output *wlr_output = data->output->wlr_output;
171 float rotation = data->root_geo.rotation;
172 float alpha = data->alpha;
173
174 if (!wlr_surface_has_buffer(surface)) {
175 return;
176 }
177
178 struct wlr_box box;
179 bool intersects = get_surface_box(&data->root_geo, data->output, surface,
180 sx, sy, &box);
181 if (!intersects) {
182 return;
183 }
184
185 struct wlr_renderer *renderer =
186 wlr_backend_get_renderer(wlr_output->backend);
187 if (!sway_assert(renderer != NULL,
188 "expected the output backend to have a renderer")) {
189 return;
190 }
191
192 scale_box(&box, wlr_output->scale);
193
194 float matrix[9];
195 enum wl_output_transform transform =
196 wlr_output_transform_invert(surface->current->transform);
197 wlr_matrix_project_box(matrix, &box, transform, rotation,
198 wlr_output->transform_matrix);
199
200 wlr_render_texture_with_matrix(renderer, surface->texture,
201 matrix, alpha);
202}
203
204static void render_layer(struct sway_output *output,
205 struct wl_list *layer_surfaces) {
206 struct render_data data = { .output = output, .alpha = 1.0f };
207 layer_for_each_surface(layer_surfaces, &data.root_geo,
208 render_surface_iterator, &data);
209}
210
211static void render_unmanaged(struct sway_output *output,
212 struct wl_list *unmanaged) {
213 struct render_data data = { .output = output, .alpha = 1.0f };
214 unmanaged_for_each_surface(unmanaged, output, &data.root_geo,
215 render_surface_iterator, &data);
216}
217
218static void render_container_iterator(struct sway_container *con,
219 void *_data) {
220 struct sway_output *output = _data;
221 if (!sway_assert(con->type == C_VIEW, "expected a view")) {
222 return;
223 }
224 struct render_data data = { .output = output, .alpha = con->alpha };
225 output_view_for_each_surface(con->sway_view, &data.root_geo,
226 render_surface_iterator, &data);
227}
228
229static void render_container(struct sway_output *output,
230 struct sway_container *con) {
231 container_descendants(con, C_VIEW, render_container_iterator, output);
232}
233
234static struct sway_container *output_get_active_workspace(
235 struct sway_output *output) {
236 struct sway_seat *seat = input_manager_current_seat(input_manager);
237 struct sway_container *focus =
238 seat_get_focus_inactive(seat, output->swayc);
239 if (!focus) {
240 // We've never been to this output before
241 focus = output->swayc->children->items[0];
242 }
243 struct sway_container *workspace = focus;
244 if (workspace->type != C_WORKSPACE) {
245 workspace = container_parent(workspace, C_WORKSPACE);
246 }
247 return workspace;
248}
249
250static void render_output(struct sway_output *output, struct timespec *when,
251 pixman_region32_t *damage) {
252 struct wlr_output *wlr_output = output->wlr_output;
253
254 struct wlr_renderer *renderer =
255 wlr_backend_get_renderer(wlr_output->backend);
256 if (!sway_assert(renderer != NULL,
257 "expected the output backend to have a renderer")) {
258 return;
259 }
260
261 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
262
263 if (!pixman_region32_not_empty(damage)) {
264 // Output isn't damaged but needs buffer swap
265 goto renderer_end;
266 }
267
268 // TODO: don't damage the whole output
269 int width, height;
270 wlr_output_transformed_resolution(wlr_output, &width, &height);
271 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
272
273 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
274 wlr_renderer_clear(renderer, clear_color);
275
276 render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
277 render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
278
279 struct sway_container *workspace = output_get_active_workspace(output);
280 render_container(output, workspace);
281
282 render_unmanaged(output, &root_container.sway_root->xwayland_unmanaged);
283
284 // TODO: consider revising this when fullscreen windows are supported
285 render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
286 render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
287
288renderer_end:
289 if (root_container.sway_root->debug_tree) {
290 wlr_render_texture(renderer, root_container.sway_root->debug_tree,
291 wlr_output->transform_matrix, 0, 0, 1);
292 }
293
294 wlr_renderer_end(renderer);
295 if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) {
296 return;
297 }
298 output->last_frame = *when;
299}
300
301struct send_frame_done_data {
302 struct root_geometry root_geo;
303 struct sway_output *output;
304 struct timespec *when;
305};
306
307static void send_frame_done_iterator(struct wlr_surface *surface,
308 int sx, int sy, void *_data) {
309 struct send_frame_done_data *data = _data;
310
311 bool intersects = get_surface_box(&data->root_geo, data->output, surface,
312 sx, sy, NULL);
313 if (intersects) {
314 wlr_surface_send_frame_done(surface, data->when);
315 }
316}
317
318static void send_frame_done_layer(struct send_frame_done_data *data,
319 struct wl_list *layer_surfaces) {
320 layer_for_each_surface(layer_surfaces, &data->root_geo,
321 send_frame_done_iterator, data);
322}
323
324static void send_frame_done_unmanaged(struct send_frame_done_data *data,
325 struct wl_list *unmanaged) {
326 unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo,
327 send_frame_done_iterator, data);
328}
329
330static void send_frame_done_container_iterator(struct sway_container *con,
331 void *_data) {
332 struct send_frame_done_data *data = _data;
333 if (!sway_assert(con->type == C_VIEW, "expected a view")) {
334 return;
335 }
336 output_view_for_each_surface(con->sway_view, &data->root_geo,
337 send_frame_done_iterator, data);
338}
339
340static void send_frame_done_container(struct send_frame_done_data *data,
341 struct sway_container *con) {
342 container_descendants(con, C_VIEW,
343 send_frame_done_container_iterator, data);
344}
345
346static void send_frame_done(struct sway_output *output, struct timespec *when) {
347 struct send_frame_done_data data = {
348 .output = output,
349 .when = when,
350 };
351
352 send_frame_done_layer(&data,
353 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
354 send_frame_done_layer(&data,
355 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
356
357 struct sway_container *workspace = output_get_active_workspace(output);
358 send_frame_done_container(&data, workspace);
359
360 send_frame_done_unmanaged(&data,
361 &root_container.sway_root->xwayland_unmanaged);
362
363 send_frame_done_layer(&data,
364 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
365 send_frame_done_layer(&data,
366 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
367}
368
369static void damage_handle_frame(struct wl_listener *listener, void *data) {
370 struct sway_output *output =
371 wl_container_of(listener, output, damage_frame);
372
373 if (!output->wlr_output->enabled) {
374 return;
375 }
376
377 struct timespec now;
378 clock_gettime(CLOCK_MONOTONIC, &now);
379
380 bool needs_swap;
381 pixman_region32_t damage;
382 pixman_region32_init(&damage);
383 if (!wlr_output_damage_make_current(output->damage, &needs_swap, &damage)) {
384 return;
385 }
386
387 if (needs_swap) {
388 render_output(output, &now, &damage);
389 }
390
391 pixman_region32_fini(&damage);
392
393 // Send frame done to all visible surfaces
394 send_frame_done(output, &now);
395}
396
397void output_damage_whole(struct sway_output *output) {
398 wlr_output_damage_add_whole(output->damage);
399}
400
401struct damage_data {
402 struct root_geometry root_geo;
403 struct sway_output *output;
404 bool whole;
405};
406
407static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy,
408 void *_data) {
409 struct damage_data *data = _data;
410 struct sway_output *output = data->output;
411 float rotation = data->root_geo.rotation;
412 bool whole = data->whole;
413
414 struct wlr_box box;
415 bool intersects = get_surface_box(&data->root_geo, data->output, surface,
416 sx, sy, &box);
417 if (!intersects) {
418 return;
419 }
420
421 scale_box(&box, output->wlr_output->scale);
422
423 if (whole) {
424 wlr_box_rotated_bounds(&box, rotation, &box);
425 wlr_output_damage_add_box(output->damage, &box);
426 } else {
427 int center_x = box.x + box.width/2;
428 int center_y = box.y + box.height/2;
429
430 pixman_region32_t damage;
431 pixman_region32_init(&damage);
432 pixman_region32_copy(&damage, &surface->current->surface_damage);
433 wlr_region_scale(&damage, &damage, output->wlr_output->scale);
434 if (ceil(output->wlr_output->scale) > surface->current->scale) {
435 // When scaling up a surface, it'll become blurry so we need to
436 // expand the damage region
437 wlr_region_expand(&damage, &damage,
438 ceil(output->wlr_output->scale) - surface->current->scale);
439 }
440 pixman_region32_translate(&damage, box.x, box.y);
441 wlr_region_rotated_bounds(&damage, &damage, rotation,
442 center_x, center_y);
443 wlr_output_damage_add(output->damage, &damage);
444 pixman_region32_fini(&damage);
445 }
446}
447
448void output_damage_surface(struct sway_output *output, double ox, double oy,
449 struct wlr_surface *surface, bool whole) {
450 struct damage_data data = {
451 .output = output,
452 .whole = whole,
453 };
454
455 surface_for_each_surface(surface, ox, oy, &data.root_geo,
456 damage_surface_iterator, &data);
457}
458
459void output_damage_view(struct sway_output *output, struct sway_view *view,
460 bool whole) {
461 if (!sway_assert(view->swayc != NULL, "expected a view in the tree")) {
462 return;
463 }
464
465 struct damage_data data = {
466 .output = output,
467 .whole = whole,
468 };
469
470 output_view_for_each_surface(view, &data.root_geo,
471 damage_surface_iterator, &data);
472}
473
474static void output_damage_whole_container_iterator(struct sway_container *con,
475 void *data) {
476 struct sway_output *output = data;
477
478 if (!sway_assert(con->type == C_VIEW, "expected a view")) {
479 return;
480 }
481
482 output_damage_view(output, con->sway_view, true);
483}
484
485void output_damage_whole_container(struct sway_output *output,
486 struct sway_container *con) {
487 float scale = output->wlr_output->scale;
488 struct wlr_box box = {
489 .x = con->x * scale,
490 .y = con->y * scale,
491 .width = con->width * scale,
492 .height = con->height * scale,
493 };
494 wlr_output_damage_add_box(output->damage, &box);
495
496 container_descendants(con, C_VIEW, output_damage_whole_container_iterator,
497 output);
498}
499
500static void damage_handle_destroy(struct wl_listener *listener, void *data) {
501 struct sway_output *output =
502 wl_container_of(listener, output, damage_destroy);
503 container_destroy(output->swayc);
504}
505
506static void handle_destroy(struct wl_listener *listener, void *data) {
507 struct sway_output *output = wl_container_of(listener, output, destroy);
508 container_destroy(output->swayc);
509}
510
511static void handle_mode(struct wl_listener *listener, void *data) {
512 struct sway_output *output = wl_container_of(listener, output, mode);
513 arrange_layers(output);
514 arrange_windows(output->swayc, -1, -1);
515}
516
517static void handle_transform(struct wl_listener *listener, void *data) {
518 struct sway_output *output = wl_container_of(listener, output, transform);
519 arrange_layers(output);
520 arrange_windows(output->swayc, -1, -1);
521}
522
523static void handle_scale(struct wl_listener *listener, void *data) {
524 struct sway_output *output = wl_container_of(listener, output, scale);
525 arrange_layers(output);
526 arrange_windows(output->swayc, -1, -1);
527}
528
529void handle_new_output(struct wl_listener *listener, void *data) {
530 struct sway_server *server = wl_container_of(listener, server, new_output);
531 struct wlr_output *wlr_output = data;
532 wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name);
533
534 struct sway_output *output = calloc(1, sizeof(struct sway_output));
535 if (!output) {
536 return;
537 }
538 output->wlr_output = wlr_output;
539 wlr_output->data = output;
540 output->server = server;
541
542 if (!wl_list_empty(&wlr_output->modes)) {
543 struct wlr_output_mode *mode =
544 wl_container_of(wlr_output->modes.prev, mode, link);
545 wlr_output_set_mode(wlr_output, mode);
546 }
547
548 output->damage = wlr_output_damage_create(wlr_output);
549
550 output->swayc = output_create(output);
551 if (!output->swayc) {
552 free(output);
553 return;
554 }
555
556 size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
557 for (size_t i = 0; i < len; ++i) {
558 wl_list_init(&output->layers[i]);
559 }
560
561 input_manager_configure_xcursor(input_manager);
562
563 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
564 output->destroy.notify = handle_destroy;
565 wl_signal_add(&wlr_output->events.mode, &output->mode);
566 output->mode.notify = handle_mode;
567 wl_signal_add(&wlr_output->events.transform, &output->transform);
568 output->transform.notify = handle_transform;
569 wl_signal_add(&wlr_output->events.scale, &output->scale);
570 output->scale.notify = handle_scale;
571
572 wl_signal_add(&output->damage->events.frame, &output->damage_frame);
573 output->damage_frame.notify = damage_handle_frame;
574 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
575 output->damage_destroy.notify = damage_handle_destroy;
576
577 arrange_layers(output);
578 arrange_windows(&root_container, -1, -1);
579}
diff --git a/sway/desktop/wl_shell.c b/sway/desktop/wl_shell.c
new file mode 100644
index 00000000..b63c220c
--- /dev/null
+++ b/sway/desktop/wl_shell.c
@@ -0,0 +1,131 @@
1#define _POSIX_C_SOURCE 199309L
2#include <stdbool.h>
3#include <stdlib.h>
4#include <wayland-server.h>
5#include <wlr/types/wlr_wl_shell.h>
6#include "sway/tree/container.h"
7#include "sway/tree/layout.h"
8#include "sway/server.h"
9#include "sway/tree/view.h"
10#include "sway/input/seat.h"
11#include "sway/input/input-manager.h"
12#include "log.h"
13
14static struct sway_wl_shell_view *wl_shell_view_from_view(
15 struct sway_view *view) {
16 if (!sway_assert(view->type == SWAY_VIEW_WL_SHELL,
17 "Expected wl_shell view")) {
18 return NULL;
19 }
20 return (struct sway_wl_shell_view *)view;
21}
22
23static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) {
24 if (wl_shell_view_from_view(view) == NULL) {
25 return NULL;
26 }
27 switch (prop) {
28 case VIEW_PROP_TITLE:
29 return view->wlr_wl_shell_surface->title;
30 case VIEW_PROP_CLASS:
31 return view->wlr_wl_shell_surface->class;
32 default:
33 return NULL;
34 }
35}
36
37static void configure(struct sway_view *view, double ox, double oy, int width,
38 int height) {
39 struct sway_wl_shell_view *wl_shell_view = wl_shell_view_from_view(view);
40 if (wl_shell_view == NULL) {
41 return;
42 }
43 view_update_position(view, ox, oy);
44 wl_shell_view->pending_width = width;
45 wl_shell_view->pending_height = height;
46 wlr_wl_shell_surface_configure(view->wlr_wl_shell_surface, 0, width, height);
47}
48
49static void _close(struct sway_view *view) {
50 if (wl_shell_view_from_view(view) == NULL) {
51 return;
52 }
53
54 wl_client_destroy(view->wlr_wl_shell_surface->client);
55}
56
57static void destroy(struct sway_view *view) {
58 struct sway_wl_shell_view *wl_shell_view = wl_shell_view_from_view(view);
59 if (wl_shell_view == NULL) {
60 return;
61 }
62 wl_list_remove(&wl_shell_view->commit.link);
63 wl_list_remove(&wl_shell_view->destroy.link);
64 free(wl_shell_view);
65}
66
67static const struct sway_view_impl view_impl = {
68 .get_prop = get_prop,
69 .configure = configure,
70 .close = _close,
71 .destroy = destroy,
72};
73
74static void handle_commit(struct wl_listener *listener, void *data) {
75 struct sway_wl_shell_view *wl_shell_view =
76 wl_container_of(listener, wl_shell_view, commit);
77 struct sway_view *view = &wl_shell_view->view;
78 // NOTE: We intentionally discard the view's desired width here
79 // TODO: Let floating views do whatever
80 view_update_size(view, wl_shell_view->pending_width,
81 wl_shell_view->pending_height);
82 view_damage(view, false);
83}
84
85static void handle_destroy(struct wl_listener *listener, void *data) {
86 struct sway_wl_shell_view *wl_shell_view =
87 wl_container_of(listener, wl_shell_view, destroy);
88 view_destroy(&wl_shell_view->view);
89}
90
91void handle_wl_shell_surface(struct wl_listener *listener, void *data) {
92 struct sway_server *server = wl_container_of(listener, server,
93 wl_shell_surface);
94 struct wlr_wl_shell_surface *shell_surface = data;
95
96 if (shell_surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP) {
97 // popups don't get views
98 wlr_log(L_DEBUG, "New wl_shell popup");
99 return;
100 }
101
102 // TODO: make transient windows floating
103
104 wlr_log(L_DEBUG, "New wl_shell toplevel title='%s' app_id='%s'",
105 shell_surface->title, shell_surface->class);
106 wlr_wl_shell_surface_ping(shell_surface);
107
108 struct sway_wl_shell_view *wl_shell_view =
109 calloc(1, sizeof(struct sway_wl_shell_view));
110 if (!sway_assert(wl_shell_view, "Failed to allocate view")) {
111 return;
112 }
113
114 view_init(&wl_shell_view->view, SWAY_VIEW_WL_SHELL, &view_impl);
115 wl_shell_view->view.wlr_wl_shell_surface = shell_surface;
116
117 // TODO:
118 // - Wire up listeners
119 // - Look up pid and open on appropriate workspace
120 // - Set new view to maximized so it behaves nicely
121 // - Criteria
122
123 wl_shell_view->commit.notify = handle_commit;
124 wl_signal_add(&shell_surface->surface->events.commit,
125 &wl_shell_view->commit);
126
127 wl_shell_view->destroy.notify = handle_destroy;
128 wl_signal_add(&shell_surface->events.destroy, &wl_shell_view->destroy);
129
130 view_map(&wl_shell_view->view, shell_surface->surface);
131}
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
new file mode 100644
index 00000000..e4703040
--- /dev/null
+++ b/sway/desktop/xdg_shell_v6.c
@@ -0,0 +1,249 @@
1#define _POSIX_C_SOURCE 199309L
2#include <stdbool.h>
3#include <stdlib.h>
4#include <wayland-server.h>
5#include <wlr/types/wlr_xdg_shell_v6.h>
6#include "sway/tree/container.h"
7#include "sway/tree/layout.h"
8#include "sway/server.h"
9#include "sway/tree/view.h"
10#include "sway/input/seat.h"
11#include "sway/input/input-manager.h"
12#include "log.h"
13
14static const struct sway_view_child_impl popup_impl;
15
16static void popup_destroy(struct sway_view_child *child) {
17 if (!sway_assert(child->impl == &popup_impl,
18 "Expected an xdg_shell_v6 popup")) {
19 return;
20 }
21 struct sway_xdg_popup_v6 *popup = (struct sway_xdg_popup_v6 *)child;
22 wl_list_remove(&popup->new_popup.link);
23 wl_list_remove(&popup->unmap.link);
24 wl_list_remove(&popup->destroy.link);
25 free(popup);
26}
27
28static const struct sway_view_child_impl popup_impl = {
29 .destroy = popup_destroy,
30};
31
32static struct sway_xdg_popup_v6 *popup_create(
33 struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view);
34
35static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
36 struct sway_xdg_popup_v6 *popup =
37 wl_container_of(listener, popup, new_popup);
38 struct wlr_xdg_popup_v6 *wlr_popup = data;
39 popup_create(wlr_popup, popup->child.view);
40}
41
42static void popup_handle_unmap(struct wl_listener *listener, void *data) {
43 struct sway_xdg_popup_v6 *popup = wl_container_of(listener, popup, unmap);
44 view_child_destroy(&popup->child);
45}
46
47static void popup_handle_destroy(struct wl_listener *listener, void *data) {
48 struct sway_xdg_popup_v6 *popup = wl_container_of(listener, popup, destroy);
49 view_child_destroy(&popup->child);
50}
51
52static struct sway_xdg_popup_v6 *popup_create(
53 struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) {
54 struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base;
55
56 struct sway_xdg_popup_v6 *popup =
57 calloc(1, sizeof(struct sway_xdg_popup_v6));
58 if (popup == NULL) {
59 return NULL;
60 }
61 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
62
63 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
64 popup->new_popup.notify = popup_handle_new_popup;
65 wl_signal_add(&xdg_surface->events.unmap, &popup->unmap);
66 popup->unmap.notify = popup_handle_unmap;
67 wl_signal_add(&xdg_surface->events.destroy, &popup->destroy);
68 popup->destroy.notify = popup_handle_destroy;
69
70 return popup;
71}
72
73
74static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view(
75 struct sway_view *view) {
76 if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL_V6,
77 "Expected xdg_shell_v6 view")) {
78 return NULL;
79 }
80 return (struct sway_xdg_shell_v6_view *)view;
81}
82
83static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) {
84 if (xdg_shell_v6_view_from_view(view) == NULL) {
85 return NULL;
86 }
87 switch (prop) {
88 case VIEW_PROP_TITLE:
89 return view->wlr_xdg_surface_v6->toplevel->title;
90 case VIEW_PROP_APP_ID:
91 return view->wlr_xdg_surface_v6->toplevel->app_id;
92 default:
93 return NULL;
94 }
95}
96
97static void configure(struct sway_view *view, double ox, double oy, int width,
98 int height) {
99 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
100 xdg_shell_v6_view_from_view(view);
101 if (xdg_shell_v6_view == NULL) {
102 return;
103 }
104
105 view_update_position(view, ox, oy);
106 xdg_shell_v6_view->pending_width = width;
107 xdg_shell_v6_view->pending_height = height;
108 wlr_xdg_toplevel_v6_set_size(view->wlr_xdg_surface_v6, width, height);
109}
110
111static void set_activated(struct sway_view *view, bool activated) {
112 if (xdg_shell_v6_view_from_view(view) == NULL) {
113 return;
114 }
115 struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6;
116 if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
117 wlr_xdg_toplevel_v6_set_activated(surface, activated);
118 }
119}
120
121static void for_each_surface(struct sway_view *view,
122 wlr_surface_iterator_func_t iterator, void *user_data) {
123 if (xdg_shell_v6_view_from_view(view) == NULL) {
124 return;
125 }
126 wlr_xdg_surface_v6_for_each_surface(view->wlr_xdg_surface_v6, iterator,
127 user_data);
128}
129
130static void _close(struct sway_view *view) {
131 if (xdg_shell_v6_view_from_view(view) == NULL) {
132 return;
133 }
134 struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6;
135 if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
136 wlr_xdg_surface_v6_send_close(surface);
137 }
138}
139
140static void destroy(struct sway_view *view) {
141 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
142 xdg_shell_v6_view_from_view(view);
143 if (xdg_shell_v6_view == NULL) {
144 return;
145 }
146 wl_list_remove(&xdg_shell_v6_view->destroy.link);
147 wl_list_remove(&xdg_shell_v6_view->map.link);
148 wl_list_remove(&xdg_shell_v6_view->unmap.link);
149 free(xdg_shell_v6_view);
150}
151
152static const struct sway_view_impl view_impl = {
153 .get_prop = get_prop,
154 .configure = configure,
155 .set_activated = set_activated,
156 .for_each_surface = for_each_surface,
157 .close = _close,
158 .destroy = destroy,
159};
160
161static void handle_commit(struct wl_listener *listener, void *data) {
162 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
163 wl_container_of(listener, xdg_shell_v6_view, commit);
164 struct sway_view *view = &xdg_shell_v6_view->view;
165 // NOTE: We intentionally discard the view's desired width here
166 // TODO: Store this for restoration when moving to floating plane
167 // TODO: Let floating views do whatever
168 view_update_size(view, xdg_shell_v6_view->pending_width,
169 xdg_shell_v6_view->pending_height);
170 view_damage(view, false);
171}
172
173static void handle_new_popup(struct wl_listener *listener, void *data) {
174 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
175 wl_container_of(listener, xdg_shell_v6_view, new_popup);
176 struct wlr_xdg_popup_v6 *wlr_popup = data;
177 popup_create(wlr_popup, &xdg_shell_v6_view->view);
178}
179
180static void handle_unmap(struct wl_listener *listener, void *data) {
181 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
182 wl_container_of(listener, xdg_shell_v6_view, unmap);
183
184 view_unmap(&xdg_shell_v6_view->view);
185
186 wl_list_remove(&xdg_shell_v6_view->commit.link);
187 wl_list_remove(&xdg_shell_v6_view->new_popup.link);
188}
189
190static void handle_map(struct wl_listener *listener, void *data) {
191 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
192 wl_container_of(listener, xdg_shell_v6_view, map);
193 struct sway_view *view = &xdg_shell_v6_view->view;
194 struct wlr_xdg_surface_v6 *xdg_surface = view->wlr_xdg_surface_v6;
195
196 view_map(view, view->wlr_xdg_surface_v6->surface);
197
198 xdg_shell_v6_view->commit.notify = handle_commit;
199 wl_signal_add(&xdg_surface->surface->events.commit,
200 &xdg_shell_v6_view->commit);
201
202 xdg_shell_v6_view->new_popup.notify = handle_new_popup;
203 wl_signal_add(&xdg_surface->events.new_popup,
204 &xdg_shell_v6_view->new_popup);
205}
206
207static void handle_destroy(struct wl_listener *listener, void *data) {
208 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
209 wl_container_of(listener, xdg_shell_v6_view, destroy);
210 view_destroy(&xdg_shell_v6_view->view);
211}
212
213void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
214 struct sway_server *server = wl_container_of(listener, server,
215 xdg_shell_v6_surface);
216 struct wlr_xdg_surface_v6 *xdg_surface = data;
217
218 if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) {
219 wlr_log(L_DEBUG, "New xdg_shell_v6 popup");
220 return;
221 }
222
223 wlr_log(L_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'",
224 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id);
225 wlr_xdg_surface_v6_ping(xdg_surface);
226 wlr_xdg_toplevel_v6_set_maximized(xdg_surface, true);
227
228 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
229 calloc(1, sizeof(struct sway_xdg_shell_v6_view));
230 if (!sway_assert(xdg_shell_v6_view, "Failed to allocate view")) {
231 return;
232 }
233
234 view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl);
235 xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface;
236
237 // TODO:
238 // - Look up pid and open on appropriate workspace
239 // - Criteria
240
241 xdg_shell_v6_view->map.notify = handle_map;
242 wl_signal_add(&xdg_surface->events.map, &xdg_shell_v6_view->map);
243
244 xdg_shell_v6_view->unmap.notify = handle_unmap;
245 wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_v6_view->unmap);
246
247 xdg_shell_v6_view->destroy.notify = handle_destroy;
248 wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_v6_view->destroy);
249}
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
new file mode 100644
index 00000000..413dbf8b
--- /dev/null
+++ b/sway/desktop/xwayland.c
@@ -0,0 +1,310 @@
1#define _POSIX_C_SOURCE 199309L
2#include <stdbool.h>
3#include <stdlib.h>
4#include <wayland-server.h>
5#include <wlr/types/wlr_output_layout.h>
6#include <wlr/types/wlr_output.h>
7#include <wlr/xwayland.h>
8#include "log.h"
9#include "sway/desktop.h"
10#include "sway/input/input-manager.h"
11#include "sway/input/seat.h"
12#include "sway/output.h"
13#include "sway/server.h"
14#include "sway/tree/container.h"
15#include "sway/tree/layout.h"
16#include "sway/tree/view.h"
17
18static void unmanaged_handle_request_configure(struct wl_listener *listener,
19 void *data) {
20 struct sway_xwayland_unmanaged *surface =
21 wl_container_of(listener, surface, request_configure);
22 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
23 struct wlr_xwayland_surface_configure_event *ev = data;
24 wlr_xwayland_surface_configure(xsurface, ev->x, ev->y,
25 ev->width, ev->height);
26}
27
28static void unmanaged_handle_commit(struct wl_listener *listener, void *data) {
29 struct sway_xwayland_unmanaged *surface =
30 wl_container_of(listener, surface, commit);
31 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
32
33 if (xsurface->x != surface->lx || xsurface->y != surface->ly) {
34 // Surface has moved
35 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
36 true);
37 surface->lx = xsurface->x;
38 surface->ly = xsurface->y;
39 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
40 true);
41 } else {
42 desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y,
43 false);
44 }
45}
46
47static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
48 struct sway_xwayland_unmanaged *surface =
49 wl_container_of(listener, surface, map);
50 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
51
52 wl_list_insert(&root_container.sway_root->xwayland_unmanaged,
53 &surface->link);
54
55 wl_signal_add(&xsurface->surface->events.commit, &surface->commit);
56 surface->commit.notify = unmanaged_handle_commit;
57
58 surface->lx = xsurface->x;
59 surface->ly = xsurface->y;
60 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true);
61
62 if (!wlr_xwayland_surface_is_unmanaged(xsurface)) {
63 struct sway_seat *seat = input_manager_current_seat(input_manager);
64 struct wlr_xwayland *xwayland = seat->input->server->xwayland;
65 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
66 seat_set_focus_surface(seat, xsurface->surface);
67 }
68
69 // TODO: we don't send surface enter/leave events to xwayland unmanaged
70 // surfaces, but xwayland doesn't support HiDPI anyway
71}
72
73static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
74 struct sway_xwayland_unmanaged *surface =
75 wl_container_of(listener, surface, unmap);
76 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
77 desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true);
78 wl_list_remove(&surface->link);
79 wl_list_remove(&surface->commit.link);
80}
81
82static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
83 struct sway_xwayland_unmanaged *surface =
84 wl_container_of(listener, surface, destroy);
85 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
86 if (xsurface->mapped) {
87 unmanaged_handle_unmap(&surface->unmap, xsurface);
88 }
89 wl_list_remove(&surface->map.link);
90 wl_list_remove(&surface->unmap.link);
91 wl_list_remove(&surface->destroy.link);
92 free(surface);
93}
94
95static struct sway_xwayland_unmanaged *create_unmanaged(
96 struct wlr_xwayland_surface *xsurface) {
97 struct sway_xwayland_unmanaged *surface =
98 calloc(1, sizeof(struct sway_xwayland_unmanaged));
99 if (surface == NULL) {
100 wlr_log(L_ERROR, "Allocation failed");
101 return NULL;
102 }
103
104 surface->wlr_xwayland_surface = xsurface;
105
106 wl_signal_add(&xsurface->events.request_configure,
107 &surface->request_configure);
108 surface->request_configure.notify = unmanaged_handle_request_configure;
109 wl_signal_add(&xsurface->events.map, &surface->map);
110 surface->map.notify = unmanaged_handle_map;
111 wl_signal_add(&xsurface->events.unmap, &surface->unmap);
112 surface->unmap.notify = unmanaged_handle_unmap;
113 wl_signal_add(&xsurface->events.destroy, &surface->destroy);
114 surface->destroy.notify = unmanaged_handle_destroy;
115
116 unmanaged_handle_map(&surface->map, xsurface);
117
118 return surface;
119}
120
121
122static struct sway_xwayland_view *xwayland_view_from_view(
123 struct sway_view *view) {
124 if (!sway_assert(view->type == SWAY_VIEW_XWAYLAND,
125 "Expected xwayland view")) {
126 return NULL;
127 }
128 return (struct sway_xwayland_view *)view;
129}
130
131static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) {
132 if (xwayland_view_from_view(view) == NULL) {
133 return NULL;
134 }
135 switch (prop) {
136 case VIEW_PROP_TITLE:
137 return view->wlr_xwayland_surface->title;
138 case VIEW_PROP_CLASS:
139 return view->wlr_xwayland_surface->class;
140 default:
141 return NULL;
142 }
143}
144
145static void configure(struct sway_view *view, double ox, double oy, int width,
146 int height) {
147 struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view);
148 if (xwayland_view == NULL) {
149 return;
150 }
151 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
152
153 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
154 if (!sway_assert(output, "view must be within tree to set position")) {
155 return;
156 }
157 struct sway_container *root = container_parent(output, C_ROOT);
158 if (!sway_assert(root, "output must be within tree to set position")) {
159 return;
160 }
161 struct wlr_output_layout *layout = root->sway_root->output_layout;
162 struct wlr_output_layout_output *loutput =
163 wlr_output_layout_get(layout, output->sway_output->wlr_output);
164 if (!sway_assert(loutput, "output must be within layout to set position")) {
165 return;
166 }
167
168 view_update_position(view, ox, oy);
169
170 xwayland_view->pending_width = width;
171 xwayland_view->pending_height = height;
172 wlr_xwayland_surface_configure(xsurface, ox + loutput->x, oy + loutput->y,
173 width, height);
174}
175
176static void set_activated(struct sway_view *view, bool activated) {
177 if (xwayland_view_from_view(view) == NULL) {
178 return;
179 }
180 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
181 wlr_xwayland_surface_activate(surface, activated);
182}
183
184static void _close(struct sway_view *view) {
185 if (xwayland_view_from_view(view) == NULL) {
186 return;
187 }
188 wlr_xwayland_surface_close(view->wlr_xwayland_surface);
189}
190
191static void destroy(struct sway_view *view) {
192 struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view);
193 if (xwayland_view == NULL) {
194 return;
195 }
196 wl_list_remove(&xwayland_view->destroy.link);
197 wl_list_remove(&xwayland_view->request_configure.link);
198 wl_list_remove(&xwayland_view->map.link);
199 wl_list_remove(&xwayland_view->unmap.link);
200 free(xwayland_view);
201}
202
203static const struct sway_view_impl view_impl = {
204 .get_prop = get_prop,
205 .configure = configure,
206 .set_activated = set_activated,
207 .close = _close,
208 .destroy = destroy,
209};
210
211static void handle_commit(struct wl_listener *listener, void *data) {
212 struct sway_xwayland_view *xwayland_view =
213 wl_container_of(listener, xwayland_view, commit);
214 struct sway_view *view = &xwayland_view->view;
215 // NOTE: We intentionally discard the view's desired width here
216 // TODO: Let floating views do whatever
217 view_update_size(view, xwayland_view->pending_width,
218 xwayland_view->pending_height);
219 view_damage(view, false);
220}
221
222static void handle_unmap(struct wl_listener *listener, void *data) {
223 struct sway_xwayland_view *xwayland_view =
224 wl_container_of(listener, xwayland_view, unmap);
225 wl_list_remove(&xwayland_view->commit.link);
226 view_unmap(&xwayland_view->view);
227}
228
229static void handle_map(struct wl_listener *listener, void *data) {
230 struct sway_xwayland_view *xwayland_view =
231 wl_container_of(listener, xwayland_view, map);
232 struct wlr_xwayland_surface *xsurface = data;
233 struct sway_view *view = &xwayland_view->view;
234
235 // Wire up the commit listener here, because xwayland map/unmap can change
236 // the underlying wlr_surface
237 wl_signal_add(&xsurface->surface->events.commit, &xwayland_view->commit);
238 xwayland_view->commit.notify = handle_commit;
239
240 // Put it back into the tree
241 wlr_xwayland_surface_set_maximized(xsurface, true);
242 view_map(view, xsurface->surface);
243}
244
245static void handle_destroy(struct wl_listener *listener, void *data) {
246 struct sway_xwayland_view *xwayland_view =
247 wl_container_of(listener, xwayland_view, destroy);
248 struct sway_view *view = &xwayland_view->view;
249 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
250 if (xsurface->mapped) {
251 handle_unmap(&xwayland_view->unmap, xsurface);
252 }
253 view_destroy(&xwayland_view->view);
254}
255
256static void handle_request_configure(struct wl_listener *listener, void *data) {
257 struct sway_xwayland_view *xwayland_view =
258 wl_container_of(listener, xwayland_view, request_configure);
259 struct wlr_xwayland_surface_configure_event *ev = data;
260 struct sway_view *view = &xwayland_view->view;
261 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
262 // TODO: floating windows are allowed to move around like this, but make
263 // sure tiling windows always stay in place.
264 wlr_xwayland_surface_configure(xsurface, ev->x, ev->y,
265 ev->width, ev->height);
266}
267
268void handle_xwayland_surface(struct wl_listener *listener, void *data) {
269 struct sway_server *server = wl_container_of(listener, server,
270 xwayland_surface);
271 struct wlr_xwayland_surface *xsurface = data;
272
273 if (wlr_xwayland_surface_is_unmanaged(xsurface) ||
274 xsurface->override_redirect) {
275 wlr_log(L_DEBUG, "New xwayland unmanaged surface");
276 create_unmanaged(xsurface);
277 return;
278 }
279
280 wlr_log(L_DEBUG, "New xwayland surface title='%s' class='%s'",
281 xsurface->title, xsurface->class);
282
283 struct sway_xwayland_view *xwayland_view =
284 calloc(1, sizeof(struct sway_xwayland_view));
285 if (!sway_assert(xwayland_view, "Failed to allocate view")) {
286 return;
287 }
288
289 view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl);
290 xwayland_view->view.wlr_xwayland_surface = xsurface;
291
292 // TODO:
293 // - Look up pid and open on appropriate workspace
294 // - Criteria
295
296 wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy);
297 xwayland_view->destroy.notify = handle_destroy;
298
299 wl_signal_add(&xsurface->events.request_configure,
300 &xwayland_view->request_configure);
301 xwayland_view->request_configure.notify = handle_request_configure;
302
303 wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap);
304 xwayland_view->unmap.notify = handle_unmap;
305
306 wl_signal_add(&xsurface->events.map, &xwayland_view->map);
307 xwayland_view->map.notify = handle_map;
308
309 handle_map(&xwayland_view->map, xsurface);
310}