diff options
Diffstat (limited to 'sway/desktop')
-rw-r--r-- | sway/desktop/desktop.c | 14 | ||||
-rw-r--r-- | sway/desktop/layer_shell.c | 347 | ||||
-rw-r--r-- | sway/desktop/output.c | 579 | ||||
-rw-r--r-- | sway/desktop/wl_shell.c | 131 | ||||
-rw-r--r-- | sway/desktop/xdg_shell_v6.c | 249 | ||||
-rw-r--r-- | sway/desktop/xwayland.c | 310 |
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 | |||
5 | void 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 | |||
17 | static 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 | |||
79 | static 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 | |||
158 | void 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 | |||
219 | static 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 | |||
227 | static 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 | |||
250 | static 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 | |||
260 | static 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 | |||
282 | static 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 | |||
293 | static 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 | |||
299 | void 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 | |||
26 | struct 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 | */ | ||
40 | static 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 | */ | ||
60 | struct root_geometry { | ||
61 | double x, y; | ||
62 | int width, height; | ||
63 | float rotation; | ||
64 | }; | ||
65 | |||
66 | static 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 | |||
102 | static 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 | |||
114 | static 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 | |||
126 | static 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 | |||
139 | static 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 | |||
154 | static 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 | |||
161 | struct render_data { | ||
162 | struct root_geometry root_geo; | ||
163 | struct sway_output *output; | ||
164 | float alpha; | ||
165 | }; | ||
166 | |||
167 | static 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 | |||
204 | static 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 | |||
211 | static 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 | |||
218 | static 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 | |||
229 | static void render_container(struct sway_output *output, | ||
230 | struct sway_container *con) { | ||
231 | container_descendants(con, C_VIEW, render_container_iterator, output); | ||
232 | } | ||
233 | |||
234 | static 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 | |||
250 | static 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 | |||
288 | renderer_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 | |||
301 | struct send_frame_done_data { | ||
302 | struct root_geometry root_geo; | ||
303 | struct sway_output *output; | ||
304 | struct timespec *when; | ||
305 | }; | ||
306 | |||
307 | static 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 | |||
318 | static 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 | |||
324 | static 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 | |||
330 | static 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 | |||
340 | static 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 | |||
346 | static 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 | |||
369 | static 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 | |||
397 | void output_damage_whole(struct sway_output *output) { | ||
398 | wlr_output_damage_add_whole(output->damage); | ||
399 | } | ||
400 | |||
401 | struct damage_data { | ||
402 | struct root_geometry root_geo; | ||
403 | struct sway_output *output; | ||
404 | bool whole; | ||
405 | }; | ||
406 | |||
407 | static 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 | |||
448 | void 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 | |||
459 | void 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 | |||
474 | static 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 | |||
485 | void 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 | |||
500 | static 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 | |||
506 | static 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 | |||
511 | static 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 | |||
517 | static 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 | |||
523 | static 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 | |||
529 | void 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 | |||
14 | static 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 | |||
23 | static 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 | |||
37 | static 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 | |||
49 | static 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 | |||
57 | static 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 | |||
67 | static const struct sway_view_impl view_impl = { | ||
68 | .get_prop = get_prop, | ||
69 | .configure = configure, | ||
70 | .close = _close, | ||
71 | .destroy = destroy, | ||
72 | }; | ||
73 | |||
74 | static 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 | |||
85 | static 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 | |||
91 | void 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 | |||
14 | static const struct sway_view_child_impl popup_impl; | ||
15 | |||
16 | static 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 | |||
28 | static const struct sway_view_child_impl popup_impl = { | ||
29 | .destroy = popup_destroy, | ||
30 | }; | ||
31 | |||
32 | static struct sway_xdg_popup_v6 *popup_create( | ||
33 | struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view); | ||
34 | |||
35 | static 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 | |||
42 | static 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 | |||
47 | static 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 | |||
52 | static 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 | |||
74 | static 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 | |||
83 | static 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 | |||
97 | static 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 | |||
111 | static 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 | |||
121 | static 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 | |||
130 | static 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 | |||
140 | static 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 | |||
152 | static 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 | |||
161 | static 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 | |||
173 | static 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 | |||
180 | static 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 | |||
190 | static 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 | |||
207 | static 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 | |||
213 | void 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 | |||
18 | static 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 | |||
28 | static 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 | |||
47 | static 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 | |||
73 | static 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 | |||
82 | static 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 | |||
95 | static 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 | |||
122 | static 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 | |||
131 | static 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 | |||
145 | static 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 | |||
176 | static 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 | |||
184 | static 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 | |||
191 | static 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 | |||
203 | static 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 | |||
211 | static 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 | |||
222 | static 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 | |||
229 | static 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 | |||
245 | static 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 | |||
256 | static 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 | |||
268 | void 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 | } | ||