1 /*
2 * There are a number of oddities with dealing with XWayland, and
3 * its behavior change depending on if you are using rootless mode
4 * or not.
5 *
6 * With 'normal' mode it behaves as a dumb (and buggy) wl_shell
7 * client that basically ignored everything.
8 *
9 * With 'rootless' mode, it creates compositor surfaces and uses
10 * them directly - being basically the only client to do so. The
11 * job then is to pair these surfaces based on a window property
12 * and just treat them as something altogether special by adding
13 * a custom window-manager.
14 */
15 #include <sys/wait.h>
16 #include <sys/types.h>
17 #include <sys/ipc.h>
18 #include <sys/mman.h>
19 #include <sys/time.h>
20 #include <sys/resource.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <stdarg.h>
26
27 static FILE* wmfd_output = NULL;
28 static pid_t xwl_wm_pid = -1;
29 static char* xwl_wm_display;
30
31 static int wmfd_input = -1;
32 static char wmfd_inbuf[1024];
33 static size_t wmfd_ofs = 0;
34
35 /* "known" mapped windows, we trigger the search etc. when a buffer
36 * is commited without a known backing on the compositor, and try
37 * to 'pair' it with ones that we have been told about */
38 struct xwl_window {
39
40 /* Xid for Window */
41 uint32_t id;
42
43 /* Wayland client-local reference ID */
44 uint32_t surface_id;
45
46 /* Parent Xid for Window */
47 uint32_t parent_id;
48
49 /* Track viewport separate from comp_surf viewport as it can be
50 * populated when there is still no surf to pair it to */
51 struct arcan_event viewport;
52
53 /* a window mapping that is PAIRED means that we know both the local
54 * compositor surface and the wmed X surface */
55 bool paired;
56
57 /* a window mapping that is PENDING means that we only know the local
58 * compositor surface and there is a pending commit on that surface */
59 struct wl_resource* pending_res;
60 struct wl_client* pending_client;
61
62 /* when a surface goes pending, we flush a queue of pending wm state
63 * messages, if the size does not suffice here, we should really
64 * just override the ones that have been redefined */
65 size_t queue_count;
66 struct arcan_event queued_message[8];
67
68 char* xtype;
69 struct comp_surf* surf;
70 };
71
72 /* just linear search it for the time being, can copy the UT_HASH
73 * use from the xwlwm side if need be */
74
75 static struct xwl_window xwl_windows[256];
xwl_find(uint32_t id)76 static struct xwl_window* xwl_find(uint32_t id)
77 {
78 for (size_t i = 0; i < COUNT_OF(xwl_windows); i++)
79 if (xwl_windows[i].id == id)
80 return &xwl_windows[i];
81
82 return NULL;
83 }
84
dump_unpaired()85 static void dump_unpaired()
86 {
87 for (size_t i = 0; i < COUNT_OF(xwl_windows); i++){
88 struct xwl_window* wnd = &xwl_windows[i];
89
90 if (!wnd->paired && (wnd->id || wnd->surface_id)){
91 trace(TRACE_XWL, "unpaired_index=%zu:xid=%"PRIu32":wlid=%"PRIu32":type=%s:queue=%zu",
92 i, wnd->id, wnd->surface_id, wnd->xtype ? wnd->xtype : "unknown", wnd->queue_count);
93 }
94 }
95 }
96
wnd_queue(struct xwl_window * wnd,struct arcan_event * ev)97 static int wnd_queue(struct xwl_window* wnd, struct arcan_event* ev)
98 {
99 bool res = false;
100
101 /* messages can come while unpaired, buffer them for the time being */
102 if (!wnd->surf){
103 if (wnd->queue_count < COUNT_OF(wnd->queued_message)){
104 wnd->queued_message[wnd->queue_count++] = *ev;
105 return -1;
106 }
107 return -2;
108 }
109
110 arcan_shmif_enqueue(&wnd->surf->acon, ev);
111 return 0;
112 }
113
wnd_message(struct xwl_window * wnd,const char * fmt,...)114 static void wnd_message(struct xwl_window* wnd, const char* fmt, ...)
115 {
116 struct arcan_event ev = {
117 .category = EVENT_EXTERNAL,
118 .ext.kind = ARCAN_EVENT(MESSAGE)
119 };
120
121 va_list args;
122 va_start(args, fmt);
123 vsnprintf((char*)ev.ext.message.data,
124 COUNT_OF(ev.ext.message.data), fmt, args);
125 va_end(args);
126
127 switch(wnd_queue(wnd, &ev)){
128 case -1:
129 trace(TRACE_XWL,
130 "queue(%zu:message) unpaired: %s", wnd->queue_count, ev.ext.message.data);
131 break;
132 case -2:
133 trace(TRACE_XWL, "queue-full:broken_wm");
134 break;
135 case 0:
136 trace(TRACE_XWL, "message:%s", ev.ext.message.data);
137 break;
138 }
139 }
140
wnd_title(struct xwl_window * wnd,const char * title)141 static void wnd_title(struct xwl_window* wnd, const char* title)
142 {
143 arcan_event ev = {
144 .category = EVENT_EXTERNAL,
145 .ext.kind = ARCAN_EVENT(IDENT)
146 };
147
148 size_t lim = sizeof(ev.ext.message.data)/sizeof(ev.ext.message.data[1]);
149 snprintf((char*)ev.ext.message.data, lim, "%s", title);
150 switch (wnd_queue(wnd, &ev)){
151 case -1:
152 trace(TRACE_XWL,
153 "queue(%zu:title) unpaired: %s", wnd->queue_count, ev.ext.message.data);
154 case -2:
155 case 0:
156 break;
157 }
158 }
159
xwl_find_surface(uint32_t id)160 static struct xwl_window* xwl_find_surface(uint32_t id)
161 {
162 for (size_t i = 0; i < COUNT_OF(xwl_windows); i++)
163 if (xwl_windows[i].surface_id == id)
164 return &xwl_windows[i];
165
166 return NULL;
167 }
168
xwl_find_alloc(uint32_t id)169 static struct xwl_window* xwl_find_alloc(uint32_t id)
170 {
171 struct xwl_window* wnd = xwl_find(id);
172 if (wnd)
173 return wnd;
174
175 /* default to 'toplevel' like behavior */
176 wnd = xwl_find(0);
177 return wnd;
178 }
179
180 static void surf_commit(struct wl_client*, struct wl_resource*);
181 static void wnd_viewport(struct xwl_window* wnd);
182
xwl_wnd_paired(struct xwl_window * wnd)183 static void xwl_wnd_paired(struct xwl_window* wnd)
184 {
185 /*
186 * we will be sent here if there existed a surface id before the id mapping was
187 * known, and there might be an id before the surface id. Find the 'orphan'
188 * and copy / merge
189 */
190 ssize_t match = -1;
191
192 for (size_t i = 0; i < COUNT_OF(xwl_windows); i++){
193 if (&xwl_windows[i] == wnd){
194 continue;
195 }
196 if (xwl_windows[i].id == wnd->id || xwl_windows[i].id == wnd->surface_id){
197 match = i;
198 break;
199 }
200 }
201
202 /* merge orphan states */
203 if (-1 != match){
204 struct xwl_window tmp = *wnd;
205 struct xwl_window* xwnd = &xwl_windows[match];
206
207 *wnd = *xwnd;
208 wnd->id = tmp.id;
209 wnd->surface_id = tmp.surface_id;
210 wnd->pending_res = tmp.pending_res;
211 wnd->pending_client = tmp.pending_client;
212 *xwnd = (struct xwl_window){};
213 }
214
215 wnd->paired = true;
216 wnd_message(wnd, "pair:%d:%d", wnd->surface_id, wnd->id);
217 surf_commit(wnd->pending_client, wnd->pending_res);
218
219 if (!wnd->surf){
220 trace(TRACE_XWL, "couldn't pair, surface allocation failed");
221 return;
222 }
223
224 wnd->pending_res = NULL;
225 wnd->pending_client = NULL;
226 }
227
wnd_viewport(struct xwl_window * wnd)228 static void wnd_viewport(struct xwl_window* wnd)
229 {
230 /* always re-resolve parent token */
231 wnd->viewport.ext.viewport.parent = 0;
232
233 if (wnd->parent_id > 0){
234 struct xwl_window* pwnd = xwl_find(wnd->parent_id);
235 if (!pwnd || !pwnd->surf){
236 trace(TRACE_XWL, "bad parent id:%"PRIu32, wnd->parent_id);
237 }
238 else{
239 trace(TRACE_XWL, "parent set (%"PRIu32") => (%"PRIu32")",
240 wnd->parent_id, pwnd->surf->acon.segment_token);
241 wnd->viewport.ext.viewport.parent = pwnd->surf->acon.segment_token;
242 }
243 }
244
245 if (!wnd->surf)
246 return;
247
248 arcan_shmif_enqueue(&wnd->surf->acon, &wnd->viewport);
249
250 trace(TRACE_XWL, "viewport id:%"PRIu32",parent:%"PRIu32"@%"PRId32",%"PRId32,
251 wnd->id, wnd->parent_id,
252 wnd->viewport.ext.viewport.x, wnd->viewport.ext.viewport.y
253 );
254 }
255
256 /*
257 * Take an input- line from the window manager, unpack it, and interpret the
258 * command inside. A noticable part here is that the resolved window may be
259 * in an unallocated, unpaired or paired state here and the input itself is
260 * not necessarily trusted.
261 *
262 * Thus any extracted field or update that should propagate as an event to
263 * a backing shmif connection need to be support being deferred until
264 * pairing / allocation - and resist UAF/spoofing. Luckily, there is not
265 * many events that need to be forwarded.
266 */
process_input(const char * msg)267 static int process_input(const char* msg)
268 {
269 /* special handling for multiplexing trace messages */
270 trace(TRACE_XWL, "wm->%s", msg);
271 if (strncmp(msg, "kind=trace", 10) == 0){
272 return 0;
273 }
274
275 struct arg_arr* cmd = arg_unpack(msg);
276 if (!cmd){
277 trace(TRACE_XWL, "malformed message: %s", msg);
278 return 0;
279 }
280
281 /* all commands should have a 'kind' field */
282 const char* arg;
283 if (!arg_lookup(cmd, "kind", 0, &arg) && arg){
284 trace(TRACE_XWL, "malformed argument: %s, missing kind", msg);
285 goto cleanup;
286 }
287 /* pair surface */
288 else if (strcmp(arg, "surface") == 0){
289 if (!arg_lookup(cmd, "id", 0, &arg) && arg){
290 trace(TRACE_XWL, "malformed surface argument: missing id");
291 goto cleanup;
292 }
293 uint32_t id = strtoul(arg, NULL, 10);
294 if (!arg_lookup(cmd, "surface_id", 0, &arg) && arg){
295 trace(TRACE_XWL, "malformed surface argument: missing surface id");
296 goto cleanup;
297 }
298 uint32_t surface_id = strtoul(arg, NULL, 10);
299 trace(TRACE_XWL, "surface id:%"PRIu32"-%"PRIu32, id, surface_id);
300 struct xwl_window* wnd = xwl_find_surface(surface_id);
301 if (!wnd){
302 wnd = xwl_find_alloc(id);
303 dump_unpaired();
304 }
305 if (!wnd)
306 goto cleanup;
307
308 wnd->surface_id = surface_id;
309 wnd->id = id;
310
311 /* if we already know about the compositor-surface, activate it */
312 if (wnd->pending_res){
313 trace(TRACE_XWL, "paired-pending %"PRIu32, id);
314 xwl_wnd_paired(wnd);
315 }
316 wnd->paired = true;
317 }
318 /* window goes from invisible to visible state */
319 else if (strcmp(arg, "create") == 0){
320 if (!arg_lookup(cmd, "id", 0, &arg) && arg){
321 trace(TRACE_XWL, "malformed map argument: missing id");
322 goto cleanup;
323 }
324 uint32_t id = strtoul(arg, NULL, 10);
325 trace(TRACE_XWL, "map id:%"PRIu32, id);
326 struct xwl_window* wnd = xwl_find_alloc(id);
327 if (!wnd)
328 goto cleanup;
329
330 wnd->id = id;
331
332 /* should come with some kind of type information */
333 if (arg_lookup(cmd, "type", 0, &arg) && arg){
334 trace(TRACE_XWL, "created with type %s", arg);
335 wnd->xtype = strdup(arg);
336 wnd_message(wnd, "type:%s", arg);
337 }
338 else{
339 trace(TRACE_XWL, "malformed create argument: missing type");
340 goto cleanup;
341 }
342
343 /* we only viewport when we have a grab or hierarchy relationship change */
344 if (arg_lookup(cmd, "parent", 0, &arg) && arg){
345 uint32_t parent_id = strtoul(arg, NULL, 0);
346 struct xwl_window* wnd = xwl_find(parent_id);
347 if (wnd){
348 trace(TRACE_XWL, "found parent surface: %"PRIu32, parent_id);
349 wnd->parent_id = parent_id;
350 wnd_viewport(wnd);
351 }
352 else
353 trace(TRACE_XWL, "bad parent-id: "PRIu32, parent_id);
354 }
355 }
356 else if (strcmp(arg, "title") == 0 && arg){
357 if (!arg_lookup(cmd, "id", 0, &arg) && arg)
358 goto cleanup;
359
360 uint32_t id = strtoul(arg, NULL, 10);
361 struct xwl_window* wnd = xwl_find(id);
362
363 if (!wnd)
364 goto cleanup;
365
366 const char* msg = NULL;
367 if (!arg_lookup(cmd, "msg", 0, &msg) || !msg){
368 msg = "";
369 }
370 wnd_title(wnd, msg);
371 }
372 /* reparent */
373 else if (strcmp(arg, "parent") == 0){
374 if (!arg_lookup(cmd, "id", 0, &arg) && arg)
375 goto cleanup;
376 uint32_t id = strtoul(arg, NULL, 10);
377
378 if (!arg_lookup(cmd, "parent_id", 0, &arg) && arg)
379 goto cleanup;
380
381 uint32_t parent_id = strtoul(arg, NULL, 10);
382 struct xwl_window* wnd = xwl_find(id);
383
384 if (!wnd)
385 goto cleanup;
386
387 wnd->parent_id = parent_id;
388 trace(TRACE_XWL, "reparent id:%"PRIu32" to %"PRIu32, id, parent_id);
389 wnd_viewport(wnd);
390 }
391 /* invisible -> visible */
392 else if (strcmp(arg, "map") == 0){
393 if (!arg_lookup(cmd, "id", 0, &arg) && arg){
394 trace(TRACE_XWL, "map:status=no_id");
395 goto cleanup;
396 }
397 uint32_t id = strtoul(arg, NULL, 10);
398 struct xwl_window* wnd = xwl_find(id);
399
400 if (!wnd){
401 trace(TRACE_XWL, "map:id=%"PRIu32":status=no_wnd", id);
402 goto cleanup;
403 }
404
405 wnd->viewport.ext.viewport.invisible = false;
406 if (arg_lookup(cmd, "parent_id", 0, &arg)){
407 wnd->parent_id = strtoul(arg, NULL, 10);
408 }
409
410 if (arg_lookup(cmd, "x", 0, &arg)){
411 wnd->viewport.ext.viewport.x = strtol(arg, NULL, 10);
412 }
413 if (arg_lookup(cmd, "y", 0, &arg)){
414 wnd->viewport.ext.viewport.y = strtol(arg, NULL, 10);
415 }
416
417 if (!arg_lookup(cmd, "type", 0, &arg)){
418 trace(TRACE_XWL, "remap id:%"PRIu32", failed, no type information", id);
419 goto cleanup;
420 }
421
422 if (wnd->xtype)
423 free(wnd->xtype);
424 wnd->xtype = strdup(arg);
425 wnd_message(wnd, "type:%s", arg);
426 wnd_viewport(wnd);
427 }
428 /* window goes from visible to invisible state, but resources remain */
429 else if (strcmp(arg, "unmap") == 0){
430 trace(TRACE_XWL, "unmap");
431 uint32_t id = strtoul(arg, NULL, 10);
432 struct xwl_window* wnd = xwl_find(id);
433 if (!wnd)
434 goto cleanup;
435
436 wnd->viewport.ext.viewport.invisible = true;
437 wnd_viewport(wnd);
438 }
439 else if (strcmp(arg, "terminated") == 0){
440 trace(TRACE_XWL, "xwayland died");
441 if (wl.exec_mode)
442 wl.alive = false;
443 }
444 /* window changes position or hierarchy, the size part is tied to the
445 * buffer in shmif- parlerance so we don't really care to match that
446 * here */
447 else if (strcmp(arg, "configure") == 0){
448 if (!arg_lookup(cmd, "id", 0, &arg)){
449 trace(TRACE_XWL, "malformed surface argument: missing surface id");
450 goto cleanup;
451 }
452 uint32_t id = strtoul(arg, NULL, 10);
453 struct xwl_window* wnd = xwl_find(id);
454 if (!wnd){
455 trace(TRACE_XWL, "configure on unknown id %"PRIu32, id);
456 goto cleanup;
457 }
458
459 /* we cache the viewport event for the window as well as for the surface
460 * due to the possibility of the unpaired state */
461 if (arg_lookup(cmd, "x", 0, &arg)){
462 wnd->viewport.ext.viewport.x = strtol(arg, NULL, 10);
463 trace(TRACE_XWL, "x reconfigured %d", wnd->viewport.ext.viewport.x);
464 }
465
466 if (arg_lookup(cmd, "y", 0, &arg)){
467 wnd->viewport.ext.viewport.y = strtol(arg, NULL, 10);
468 trace(TRACE_XWL, "y reconfigured %d", wnd->viewport.ext.viewport.y);
469 }
470
471 /* and either reflect now or later */
472 wnd_viewport(wnd);
473 }
474 else if (strcmp(arg, "destroy") == 0){
475 if (!arg_lookup(cmd, "id", 0, &arg)){
476 trace(TRACE_XWL, "malformed surface argument: missing surface id");
477 goto cleanup;
478 }
479 uint32_t id = strtoul(arg, NULL, 10);
480 struct xwl_window* wnd = xwl_find(id);
481 if (!wnd){
482 trace(TRACE_XWL, "destroy on unknown id %"PRIu32, id);
483 goto cleanup;
484 }
485 *wnd = (struct xwl_window){};
486 }
487 /* just forward */
488 else if (arg_lookup(cmd, "fullscreen", 0, &arg) && arg){
489 if (!arg_lookup(cmd, "id", 0, &arg)){
490 trace(TRACE_XWL, "malformed surface argument: missing surface id");
491 goto cleanup;
492 }
493 uint32_t id = strtoul(arg, NULL, 10);
494 struct xwl_window* wnd = xwl_find(id);
495 if (!wnd){
496 trace(TRACE_XWL, "configure on unknown id %"PRIu32, id);
497 goto cleanup;
498 }
499
500 wnd_message(wnd, "fullscreen=%s", arg);
501 }
502
503 cleanup:
504 arg_cleanup(cmd);
505 return 0;
506 }
507
xwl_spawn_check(const char * msg)508 static int xwl_spawn_check(const char* msg)
509 {
510 struct arg_arr* cmd = arg_unpack(msg);
511 int rv = -1;
512 if (cmd){
513 const char* arg;
514 if (arg_lookup(cmd, "xwayland_fail", 0, &arg)){
515 trace(TRACE_XWL, "couldn't spawn xwayland: %s", arg);
516 rv = -3;
517 }
518 if (arg_lookup(cmd, "xwayland_ok", 0, &arg)){
519 trace(TRACE_XWL, "xwayland on: %s", arg);
520 xwl_wm_display = strdup(arg);
521 }
522 arg_cleanup(cmd);
523 rv = 0;
524 }
525 return rv;
526 }
527
close_xwl()528 static void close_xwl()
529 {
530 fclose(wmfd_output);
531 close(wmfd_input);
532 wmfd_ofs = 0;
533 xwl_wm_pid = -1;
534 wmfd_input = -1;
535 trace(TRACE_XWL, "arcan_xwm died");
536 }
537
xwl_read_wm(int (* callback)(const char * str))538 static int xwl_read_wm(int (*callback)(const char* str))
539 {
540 if (xwl_wm_pid == -1)
541 return -1;
542
543 /* track so we don't accidentally call into ourself */
544 static bool in_wm_input;
545 if (in_wm_input)
546 return 0;
547 in_wm_input = true;
548
549 /* populate inbuffer, look for linefeed */
550 char inbuf[256];
551 int status;
552 ssize_t nr = read(wmfd_input, inbuf, 256);
553 if (-1 == nr){
554 if (errno != EAGAIN && errno != EINTR){
555 trace(TRACE_XWL, "kind=error:code=%d:message=%s", errno, strerror(errno));
556 kill(xwl_wm_pid, SIGKILL);
557 waitpid(xwl_wm_pid, NULL, 0);
558 close_xwl();
559 }
560 else {
561 int res = waitpid(xwl_wm_pid, &status, WNOHANG);
562 if (res == xwl_wm_pid && WIFEXITED(status)){
563 trace(TRACE_XWL, "kind=status:message=exited");
564 close_xwl();
565 }
566 }
567
568 in_wm_input = false;
569 return -1;
570 }
571
572 /* check the new input for a linefeed character, or flush to the buffer */
573 for (size_t i = 0; i < nr; i++){
574 if (inbuf[i] == '\n'){
575 wmfd_inbuf[wmfd_ofs] = '\0';
576 wmfd_ofs = 0;
577
578 /* leave the rest in buffer */
579 if (callback(wmfd_inbuf) != 0){
580 trace(TRACE_XWL, "kind=error:callback_fail:message=%s", wmfd_inbuf);
581 break;
582 }
583 }
584 /* accept crop on overflow (though no command should be this long) */
585 else {
586 wmfd_inbuf[wmfd_ofs] = inbuf[i];
587 wmfd_ofs ++;
588 if (wmfd_ofs == sizeof(wmfd_inbuf)){
589 trace(TRACE_XWL, "kind=error:overflow");
590 wmfd_ofs = 0;
591 }
592 }
593 }
594
595 /* possibly more to flush */
596 in_wm_input = false;
597 if (nr == 256)
598 return xwl_read_wm(callback);
599
600 return 0;
601 }
602
xwl_request_handover()603 static void xwl_request_handover()
604 {
605 /* use the waybridge to request a handover surface that will be used for xwl
606 * debugging bootstrap and for clipboard / selection - it is a bit trickier if
607 * this happens on a wm-crash where we run in service mode as the control
608 * connection is lost, option is to re-open one or just ignore the clipboard
609 * part, start with the later */
610 if (!wl.control.addr){
611 return;
612 }
613
614 arcan_shmif_enqueue(&wl.control,
615 &(struct arcan_event){
616 .ext.kind = ARCAN_EVENT(SEGREQ),
617 .ext.segreq.kind = SEGID_HANDOVER
618 });
619
620 /* incomplete - we need to wait for the handover to arrive before spawning
621 * the WM, then use that as our 'clipboard' / dnd manager - other option is
622 * to switch from transfer pipes to a shared socket and push descriptors
623 * over that as well */
624 }
625
626 /*
627 * Launch the arcan-xwayland-wm (that in turn launches Xwayland) if [block] is
628 * set, wait until arcan-xwayland-wm acknowledges that Xwayland could be
629 * launched or not.
630 *
631 * The other detail is how to handle the clipboard in a way that covers both
632 * cut/paste and drag/drop. Best is probably to request a clipboard/handover
633 * and then exec that into arcan-xwayland-wm.
634 *
635 * Error codes:
636 * -1 : arcan-xwayland-wm connection died
637 * -2 : arcan-xwayland-wm couldn't spawn Xwayland
638 * -3 : resource errors spawning arcan-xwayland-wm
639 * 0 : ok
640 */
xwl_spawn_wm(bool block,char ** argv)641 static int xwl_spawn_wm(bool block, char** argv)
642 {
643 /* already running */
644 if (xwl_wm_pid != -1)
645 return 0;
646
647 trace(TRACE_XWL, "spawning 'arcan-xwayland-wm'");
648 wl.groups[0].xwm->fd = -1;
649 int p2c_pipe[2];
650 int c2p_pipe[2];
651 if (-1 == pipe(p2c_pipe))
652 return -3;
653
654 if (-1 == pipe(c2p_pipe)){
655 close(p2c_pipe[0]);
656 close(p2c_pipe[1]);
657 return -3;
658 }
659
660 wmfd_input = c2p_pipe[0];
661 wmfd_output = fdopen(p2c_pipe[1], "w");
662 setlinebuf(wmfd_output);
663 wl.groups[0].xwm->fd = wmfd_input;
664
665 xwl_wm_pid = fork();
666
667 if (-1 == xwl_wm_pid){
668 fprintf(stderr, "Couldn't spawn wm- process (fork failed)\n");
669 exit(EXIT_FAILURE);
670 }
671
672 /* child, close, dup spawn */
673 if (!xwl_wm_pid){
674 close(p2c_pipe[1]);
675 close(c2p_pipe[0]);
676 dup2(p2c_pipe[0], STDIN_FILENO);
677 dup2(c2p_pipe[1], STDOUT_FILENO);
678 close(p2c_pipe[1]);
679 close(c2p_pipe[0]);
680
681 setsid();
682 int ndev = open("/dev/null", O_WRONLY);
683 dup2(ndev, STDERR_FILENO);
684 close(ndev);
685
686 size_t nargs = 0;
687 while (argv[nargs])
688 nargs++;
689
690 /* need to prepend the binary name so top makes sense */
691 char* newargv[nargs+2];
692 newargv[0] = "arcan_xwm";
693 newargv[nargs+1] = NULL;
694 for (size_t i = 0; i < nargs+1; i++){
695 newargv[i+1] = argv[i];
696 }
697
698 /* want to avoid the situation when building / working from a build dir, the
699 * execvp approach would still the usr/bin one */
700 #ifdef _DEBUG
701 execv("./arcan_xwm", newargv);
702 #endif
703 execvp("arcan_xwm", newargv);
704 exit(EXIT_FAILURE);
705 }
706
707 /* in this case we care about the status of the ability to launch Xwayland,
708 * this happens with -xwl -exec */
709 if (block){
710 int rv = xwl_read_wm(xwl_spawn_check);
711 if (rv < 0)
712 return rv;
713 }
714
715 /* want the input-pipe to work non-blocking here */
716 int flags = fcntl(wmfd_input, F_GETFL);
717 if (-1 != flags)
718 fcntl(wmfd_input, F_SETFL, flags | O_NONBLOCK);
719
720 /* drop child write end, parent read end as the wm process owns these now */
721 close(c2p_pipe[1]);
722 close(p2c_pipe[0]);
723
724 return 0;
725 }
726
xwl_check_wm()727 static void xwl_check_wm()
728 {
729 xwl_read_wm(process_input);
730 }
731
xwlsurf_shmifev_handler(struct comp_surf * surf,struct arcan_event * ev)732 static bool xwlsurf_shmifev_handler(
733 struct comp_surf* surf, struct arcan_event* ev)
734 {
735 if (ev->category != EVENT_TARGET || !wmfd_output)
736 return false;
737
738 /* translate relevant non-input shmif events to the text- based
739 * format used with the wm- process */
740
741 struct xwl_window* wnd =
742 xwl_find_surface(wl_resource_get_id(surf->shell_res));
743 if (!wnd)
744 return false;
745
746 switch (ev->tgt.kind){
747 case TARGET_COMMAND_DISPLAYHINT:{
748 struct surf_state states = surf->states;
749 bool changed = displayhint_handler(surf, &ev->tgt);
750 int rw = ev->tgt.ioevs[0].iv;
751 int rh = ev->tgt.ioevs[1].iv;
752 int dw = rw - (int)surf->acon.w;
753 int dh = rh - (int)surf->acon.h;
754
755 /* split up into resize requests and focus/input changes */
756 if (rw > 0 && rh > 0 && (dw != 0 || dh != 0)){
757 trace(TRACE_XWL, "displayhint: %"PRIu32",%"PRIu32,
758 ev->tgt.ioevs[0].iv, ev->tgt.ioevs[1].iv);
759
760 fprintf(wmfd_output,
761 "id=%"PRIu32":kind=resize:width=%"PRIu32":height=%"PRIu32":ts=%"PRIu64"%s\n",
762 (uint32_t) wnd->id,
763 (uint32_t) abs(ev->tgt.ioevs[0].iv),
764 (uint32_t) abs(ev->tgt.ioevs[1].iv),
765 (uint64_t) ev->tgt.timestamp,
766 (surf->states.drag_resize) ? ":drag" : ""
767 );
768 }
769
770 if (changed){
771 if (states.unfocused != surf->states.unfocused){
772 fprintf(wmfd_output, "id=%"PRIu32":ts=%"PRIu64"%s\n",
773 wnd->id,
774 (uint64_t) ev->tgt.timestamp,
775 surf->states.unfocused ? ":kind=unfocus" : ":kind=focus"
776 );
777 if (surf->states.unfocused){
778 release_all_keys(surf->client);
779 leave_all(surf);
780 }
781 else{
782 enter_all(surf);
783 /*
784 send_pointer_button(surf->client, STEP_SERIAL(),
785 arcan_timemillis(), 0x10f, WL_POINTER_BUTTON_STATE_RELEASED);
786 */
787 }
788 }
789 }
790
791 return true;
792 }
793 /* raw-forward from appl to xwm, doesn't respect multipart */
794 case TARGET_COMMAND_MESSAGE:
795 fprintf(wmfd_output, "id=%"PRIu32":%s\n", (uint32_t) wnd->id, ev->tgt.message);
796 break;
797 case TARGET_COMMAND_EXIT:
798 fprintf(wmfd_output, "kind=destroy:id=%"PRIu32"\n", wnd->id);
799 *wnd = (struct xwl_window){};
800 return true;
801 default:
802 break;
803 }
804
805 return false;
806 }
807
xwl_defer_handler(struct surface_request * req,struct arcan_shmif_cont * con)808 static bool xwl_defer_handler(
809 struct surface_request* req, struct arcan_shmif_cont* con)
810 {
811 if (!req || !con){
812 return false;
813 }
814
815 struct comp_surf* surf = wl_resource_get_user_data(req->target);
816 surf->acon = *con;
817 surf->cookie = 0xfeedface;
818 surf->shell_res = req->target;
819 surf->dispatch = xwlsurf_shmifev_handler;
820 surf->id = wl_resource_get_id(surf->shell_res);
821
822 struct xwl_window* wnd = (req->tag);
823 wnd->surf = surf;
824 wnd->viewport.ext.kind = ARCAN_EVENT(VIEWPORT);
825 trace(TRACE_ALLOC, "kind=X:fd=%d:queue=%zu", con->epipe, wnd->queue_count);
826
827 /* the normal path is:
828 * surface_commit -> check_paired -> request surface -> request ok ->
829 * commit and flush */
830 if (wnd->queue_count > 0){
831 trace(TRACE_XWL, "flush (%zu) queued messages", wnd->queue_count);
832 for (size_t i = 0; i < wnd->queue_count; i++){
833 arcan_shmif_enqueue(con, &wnd->queued_message[i]);
834 }
835 wnd->queue_count = 0;
836 }
837 surf->viewport = wnd->viewport;
838 wnd_viewport(wnd);
839
840 return true;
841 }
842
843 static struct xwl_window*
lookup_surface(struct comp_surf * surf,struct wl_resource * res)844 lookup_surface(struct comp_surf* surf, struct wl_resource* res)
845 {
846 if (!wl.use_xwayland)
847 return NULL;
848
849 /* always start by synching against pending from wm as the surface + atom
850 * mapping might be done there before we actually get to this stage */
851 xwl_check_wm();
852 uint32_t id = wl_resource_get_id(res);
853 struct xwl_window* wnd = xwl_find_surface(id);
854 if (!wnd){
855 wnd = xwl_find(0);
856 if (!wnd){
857 trace(TRACE_XWL, "out-of-memory");
858 return NULL;
859 }
860 wnd->surface_id = id;
861 }
862 return wnd;
863 }
864
865 /* called from the surface being commited when it is not marked as paired */
xwl_pair_surface(struct wl_client * cl,struct comp_surf * surf,struct wl_resource * res)866 static bool xwl_pair_surface(
867 struct wl_client* cl, struct comp_surf* surf, struct wl_resource* res)
868 {
869 /* do we know of a matching xwayland- provided surface? */
870 struct xwl_window* wnd = lookup_surface(surf, res);
871 if (!wnd){
872 trace(TRACE_XWL,
873 "no known X surface for: %"PRIu32, wl_resource_get_id(res));
874 return false;
875 }
876
877 /* are we waiting for the surface-id part? then set as pending so that we can
878 * activate when it arrives - allocation behavior is a bit suspicious here -
879 * other option is also to release the buffer and trigger frame callbacks */
880 if (!wnd->paired){
881 trace(TRACE_XWL,
882 "unpaired surface-ID: %"PRIu32, wl_resource_get_id(res));
883 wnd->pending_res = res;
884 wnd->pending_client = cl;
885 dump_unpaired();
886
887 return false;
888 }
889
890 if (surf->acon.addr){
891 return true;
892 }
893
894 /* if so, allocate the corresponding arcan- side resource */
895 return request_surface(surf->client, &(struct surface_request){
896 .segid = SEGID_BRIDGE_X11,
897 .target = res,
898 .trace = "xwl",
899 .dispatch = xwl_defer_handler,
900 .client = surf->client,
901 .source = surf,
902 .tag = wnd
903 }, 'X');
904 }
905