summaryrefslogtreecommitdiffstats
path: root/sway/desktop/xwayland.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/desktop/xwayland.c')
-rw-r--r--sway/desktop/xwayland.c310
1 files changed, 310 insertions, 0 deletions
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}