72b02dd14a786ad6e9e6c538f09ae205c9b077a6
[profile/ivi/weston.git] / compositor / xserver-launcher.c
1 /*
2  * Copyright © 2011 Intel Corporation
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and
5  * its documentation for any purpose is hereby granted without fee, provided
6  * that the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of the copyright holders not be used in
9  * advertising or publicity pertaining to distribution of the software
10  * without specific, written prior permission.  The copyright holders make
11  * no representations about the suitability of this software for any
12  * purpose.  It is provided "as is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22
23 #define _GNU_SOURCE
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <sys/socket.h>
29 #include <sys/un.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <signal.h>
34
35 #include <xcb/xcb.h>
36
37 #include <wayland-server.h>
38
39 #include "compositor.h"
40 #include "xserver-server-protocol.h"
41
42 /*
43  * TODO:
44  *  - Clean X socket and lock file on exit
45  *  - Nuke lock file if process doesn't exist.
46  *
47  * WM TODO:
48  *  - Send take focus, hook into wlsc_surface_activate.
49  */
50
51 struct xserver {
52         struct wl_resource resource;
53 };
54
55 struct wlsc_xserver {
56         struct wl_display *wl_display;
57         struct wl_event_loop *loop;
58         struct wl_event_source *sigchld_source;
59         int abstract_fd;
60         struct wl_event_source *abstract_source;
61         int unix_fd;
62         struct wl_event_source *unix_source;
63         int display;
64         struct wlsc_process process;
65
66         struct xserver xserver;
67
68         struct wlsc_wm *wm;
69 };
70
71 struct wlsc_wm {
72         xcb_connection_t *conn;
73         struct wl_event_source *source;
74         xcb_screen_t *screen;
75         struct wl_hash_table *window_hash;
76         struct {
77                 xcb_atom_t               wm_protocols;
78                 xcb_atom_t               wm_take_focus;
79                 xcb_atom_t               wm_delete_window;
80                 xcb_atom_t               net_wm_name;
81                 xcb_atom_t               net_wm_icon;
82                 xcb_atom_t               net_wm_state;
83                 xcb_atom_t               net_wm_state_fullscreen;
84                 xcb_atom_t               utf8_string;
85         } atom;
86 };
87
88 struct wlsc_wm_window {
89         xcb_window_t id;
90         struct wlsc_surface *surface;
91 };
92
93 static void
94 wlsc_wm_handle_configure_request(struct wlsc_wm *wm, xcb_generic_event_t *event)
95 {
96         xcb_configure_request_event_t *configure_request = 
97                 (xcb_configure_request_event_t *) event;
98         uint32_t values[16];
99         int i = 0;
100
101         fprintf(stderr, "XCB_CONFIGURE_REQUEST\n");
102
103         if (configure_request->value_mask & XCB_CONFIG_WINDOW_X)
104                 values[i++] = configure_request->x;
105         if (configure_request->value_mask & XCB_CONFIG_WINDOW_Y)
106                 values[i++] = configure_request->y;
107         if (configure_request->value_mask & XCB_CONFIG_WINDOW_WIDTH)
108                 values[i++] = configure_request->width;
109         if (configure_request->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
110                 values[i++] = configure_request->height;
111         if (configure_request->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH)
112                 values[i++] = configure_request->border_width;
113         if (configure_request->value_mask & XCB_CONFIG_WINDOW_SIBLING)
114                 values[i++] = configure_request->sibling;
115         if (configure_request->value_mask & XCB_CONFIG_WINDOW_STACK_MODE)
116                 values[i++] = configure_request->stack_mode;
117
118         xcb_configure_window(wm->conn,
119                              configure_request->window,
120                              configure_request->value_mask, values);
121 }
122
123 static const char *
124 get_atom_name(xcb_connection_t *c, xcb_atom_t atom)
125 {
126         xcb_get_atom_name_cookie_t cookie;
127         xcb_get_atom_name_reply_t *reply;
128         xcb_generic_error_t *e;
129         static char buffer[64];
130
131         cookie = xcb_get_atom_name (c, atom);
132         reply = xcb_get_atom_name_reply (c, cookie, &e);
133         snprintf(buffer, sizeof buffer, "%.*s",
134                  xcb_get_atom_name_name_length (reply),
135                  xcb_get_atom_name_name (reply));
136         free(reply);
137
138         return buffer;
139 }
140
141 static void
142 wlsc_wm_get_properties(struct wlsc_wm *wm, xcb_window_t window)
143 {
144         xcb_generic_error_t *e;
145         xcb_get_property_reply_t *reply;
146         void *value;
147         int i;
148
149         struct {
150                 xcb_atom_t atom;
151                 xcb_get_property_cookie_t cookie;
152         } props[] = {
153                 { XCB_ATOM_WM_CLASS, },
154                 { XCB_ATOM_WM_TRANSIENT_FOR },
155                 { wm->atom.wm_protocols, },
156                 { wm->atom.net_wm_name, },
157         };
158
159         for (i = 0; i < ARRAY_LENGTH(props); i++)
160                 props[i].cookie =
161                         xcb_get_property (wm->conn, 
162                                           0, /* delete */
163                                           window,
164                                           props[i].atom,
165                                           XCB_ATOM_ANY,
166                                           0, 2048);
167
168         for (i = 0; i < ARRAY_LENGTH(props); i++)  {
169                 reply = xcb_get_property_reply(wm->conn, props[i].cookie, &e);
170                 value = xcb_get_property_value(reply);
171
172                 fprintf(stderr, "property %s, type %d, format %d, "
173                         "length %d (value_len %d), value \"%s\"\n",
174                         get_atom_name(wm->conn, props[i].atom),
175                         reply->type, reply->format,
176                         xcb_get_property_value_length(reply), reply->value_len,
177                         reply->type ? (char *) value : "(nil)");
178
179                 free(reply);
180         }
181 }
182
183 static void
184 wlsc_wm_activate(struct wlsc_wm *wm,
185                  struct wlsc_wm_window *window, xcb_timestamp_t time)
186 {
187         xcb_set_input_focus (wm->conn,
188                              XCB_INPUT_FOCUS_POINTER_ROOT, window->id, time);
189 }
190
191 static void
192 wlsc_wm_handle_map_request(struct wlsc_wm *wm, xcb_generic_event_t *event)
193 {
194         xcb_map_request_event_t *map_request =
195                 (xcb_map_request_event_t *) event;
196         uint32_t values[1];
197
198         fprintf(stderr, "XCB_MAP_REQUEST\n");
199
200         wlsc_wm_get_properties(wm, map_request->window);
201         values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
202         xcb_change_window_attributes(wm->conn, map_request->window,
203                                      XCB_CW_EVENT_MASK, values);
204
205         xcb_map_window(wm->conn, map_request->window);
206 }
207
208 static void
209 wlsc_wm_handle_map_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
210 {
211         xcb_map_notify_event_t *map_notify =
212                 (xcb_map_notify_event_t *) event;
213         struct wlsc_wm_window *window;
214         xcb_client_message_event_t client_message;
215
216         fprintf(stderr, "XCB_MAP_NOTIFY\n");
217
218         wlsc_wm_get_properties(wm, map_notify->window);
219
220         window = wl_hash_table_lookup(wm->window_hash, map_notify->window);
221         wlsc_wm_activate(wm, window, XCB_TIME_CURRENT_TIME);
222
223         client_message.response_type = XCB_CLIENT_MESSAGE;
224         client_message.format = 32;
225         client_message.window = window->id;
226         client_message.type = wm->atom.wm_protocols;
227         client_message.data.data32[0] = wm->atom.wm_take_focus;
228         client_message.data.data32[1] = XCB_TIME_CURRENT_TIME;
229
230         xcb_send_event(wm->conn, 0, window->id, 
231                        XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
232                        (char *) &client_message);
233
234 }
235
236 static void
237 wlsc_wm_handle_property_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
238 {
239         xcb_property_notify_event_t *property_notify =
240                 (xcb_property_notify_event_t *) event;
241
242         fprintf(stderr, "XCB_PROPERTY_NOTIFY\n");
243
244         if (property_notify->atom == XCB_ATOM_WM_CLASS) {
245                 fprintf(stderr, "wm_class changed\n");
246         } else if (property_notify->atom == XCB_ATOM_WM_TRANSIENT_FOR) {
247                 fprintf(stderr, "wm_transient_for changed\n");
248         } else if (property_notify->atom == wm->atom.wm_protocols) {
249                 fprintf(stderr, "wm_protocols changed\n");
250         } else if (property_notify->atom == wm->atom.net_wm_name) {
251                 fprintf(stderr, "wm_class changed\n");
252         } else {
253                 fprintf(stderr, "unhandled property change: %s\n",
254                         get_atom_name(wm->conn, property_notify->atom));
255         }
256 }
257
258 static void
259 wlsc_wm_handle_create_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
260 {
261         xcb_create_notify_event_t *create_notify =
262                 (xcb_create_notify_event_t *) event;
263         struct wlsc_wm_window *window;
264
265         fprintf(stderr, "XCB_CREATE_NOTIFY, win %d\n", create_notify->window);
266
267         window = malloc(sizeof *window);
268         if (window == NULL) {
269                 fprintf(stderr, "failed to allocate window\n");
270                 return;
271         }
272
273         window->id = create_notify->window;
274         wl_hash_table_insert(wm->window_hash, window->id, window);
275
276         fprintf(stderr, "created window %p\n", window);
277 }
278
279 static void
280 wlsc_wm_handle_destroy_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
281 {
282         xcb_destroy_notify_event_t *destroy_notify =
283                 (xcb_destroy_notify_event_t *) event;
284         struct wlsc_wm_window *window;
285
286         fprintf(stderr, "XCB_DESTROY_NOTIFY, win %d\n",
287                 destroy_notify->window);
288
289         window = wl_hash_table_lookup(wm->window_hash, destroy_notify->window);
290         if (window == NULL) {
291                 fprintf(stderr, "destroy notify for unknow window %d\n",
292                         destroy_notify->window);
293                 return;
294         }
295
296         fprintf(stderr, "destroy window %p\n", window);
297         wl_hash_table_remove(wm->window_hash, window->id);
298         free(window);
299 }
300
301 static int
302 wlsc_wm_handle_event(int fd, uint32_t mask, void *data)
303 {
304         struct wlsc_wm *wm = data;
305         xcb_generic_event_t *event;
306         int count = 0;
307
308         while (event = xcb_poll_for_event(wm->conn), event != NULL) {
309                 switch (event->response_type) {
310                 case XCB_CREATE_NOTIFY:
311                         wlsc_wm_handle_create_notify(wm, event);
312                         break;
313                 case XCB_MAP_REQUEST:
314                         wlsc_wm_handle_map_request(wm, event);
315                         break;
316                 case XCB_MAP_NOTIFY:
317                         wlsc_wm_handle_map_notify(wm, event);
318                         break;
319                 case XCB_UNMAP_NOTIFY:
320                         fprintf(stderr, "XCB_UNMAP_NOTIFY\n");
321                         break;
322                 case XCB_CONFIGURE_REQUEST:
323                         wlsc_wm_handle_configure_request(wm, event);
324                         break;
325                 case XCB_CONFIGURE_NOTIFY:
326                         fprintf(stderr, "XCB_CONFIGURE_NOTIFY\n");
327                         break;
328                 case XCB_DESTROY_NOTIFY:
329                         wlsc_wm_handle_destroy_notify(wm, event);
330                         break;
331                 case XCB_MAPPING_NOTIFY:
332                         fprintf(stderr, "XCB_MAPPING_NOTIFY\n");
333                         break;
334                 case XCB_PROPERTY_NOTIFY:
335                         wlsc_wm_handle_property_notify(wm, event);
336                         break;
337                 default:
338                         fprintf(stderr, "Unhandled event %d\n",
339                                 event->response_type);
340                         break;
341                 }
342                 free(event);
343                 count++;
344         }
345
346         xcb_flush(wm->conn);
347
348         return count;
349 }
350
351 static void
352 wxs_wm_get_resources(struct wlsc_wm *wm)
353 {
354
355 #define F(field) offsetof(struct wlsc_wm, field)
356
357         static const struct { const char *name; int offset; } atoms[] = {
358                 { "WM_PROTOCOLS",       F(atom.wm_protocols) },
359                 { "WM_TAKE_FOCUS",      F(atom.wm_take_focus) },
360                 { "WM_DELETE_WINDOW",   F(atom.wm_delete_window) },
361                 { "_NET_WM_NAME",       F(atom.net_wm_name) },
362                 { "_NET_WM_ICON",       F(atom.net_wm_icon) },
363                 { "_NET_WM_STATE",      F(atom.net_wm_state) },
364                 { "_NET_WM_STATE_FULLSCREEN", F(atom.net_wm_state_fullscreen) },
365                 { "UTF8_STRING",        F(atom.utf8_string) },
366         };
367
368         xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
369         xcb_intern_atom_reply_t *reply;
370         int i;
371
372         for (i = 0; i < ARRAY_LENGTH(atoms); i++)
373                 cookies[i] = xcb_intern_atom (wm->conn, 0,
374                                               strlen(atoms[i].name),
375                                               atoms[i].name);
376
377         for (i = 0; i < ARRAY_LENGTH(atoms); i++) {
378                 reply = xcb_intern_atom_reply (wm->conn, cookies[i], NULL);
379                 *(xcb_atom_t *) ((char *) wm + atoms[i].offset) = reply->atom;
380                 free(reply);
381         }
382 }
383
384 static struct wlsc_wm *
385 wlsc_wm_create(struct wlsc_xserver *wxs)
386 {
387         struct wlsc_wm *wm;
388         struct wl_event_loop *loop;
389         xcb_screen_iterator_t s;
390         uint32_t values[1];
391         int sv[2];
392
393         wm = malloc(sizeof *wm);
394         if (wm == NULL)
395                 return NULL;
396
397         wm->window_hash = wl_hash_table_create();
398         if (wm->window_hash == NULL) {
399                 free(wm);
400                 return NULL;
401         }
402
403         if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
404                 fprintf(stderr, "socketpair failed\n");
405                 wl_hash_table_destroy(wm->window_hash);
406                 free(wm);
407                 return NULL;
408         }
409
410         wl_resource_post_event(&wxs->xserver.resource, XSERVER_CLIENT, sv[1]);
411         wl_client_flush(wxs->xserver.resource.client);
412         close(sv[1]);
413         
414         /* xcb_connect_to_fd takes ownership of the fd. */
415         wm->conn = xcb_connect_to_fd(sv[0], NULL);
416         if (xcb_connection_has_error(wm->conn)) {
417                 fprintf(stderr, "xcb_connect_to_fd failed\n");
418                 close(sv[0]);
419                 wl_hash_table_destroy(wm->window_hash);
420                 free(wm);
421                 return NULL;
422         }
423
424         s = xcb_setup_roots_iterator(xcb_get_setup(wm->conn));
425         wm->screen = s.data;
426
427         loop = wl_display_get_event_loop(wxs->wl_display);
428         wm->source =
429                 wl_event_loop_add_fd(loop, sv[0],
430                                      WL_EVENT_READABLE,
431                                      wlsc_wm_handle_event, wm);
432         wl_event_source_check(wm->source);
433
434         wxs_wm_get_resources(wm);
435
436         values[0] =
437                 XCB_EVENT_MASK_STRUCTURE_NOTIFY |
438                 XCB_EVENT_MASK_RESIZE_REDIRECT |
439                 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
440                 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
441                 XCB_EVENT_MASK_PROPERTY_CHANGE;
442         xcb_change_window_attributes(wm->conn, wm->screen->root,
443                                      XCB_CW_EVENT_MASK, values);
444
445         xcb_flush(wm->conn);
446         fprintf(stderr, "created wm\n");
447
448         return wm;
449 }
450
451 static void
452 wlsc_wm_destroy(struct wlsc_wm *wm)
453 {
454         /* FIXME: Free windows in hash. */
455         wl_hash_table_destroy(wm->window_hash);
456         xcb_disconnect(wm->conn);
457         wl_event_source_remove(wm->source);
458         free(wm);
459 }
460
461 static void
462 wlsc_xserver_bind(struct wl_client *client,
463                   struct wl_object *global, uint32_t version)
464 {
465         struct wlsc_xserver *wxs =
466                 container_of(global, struct wlsc_xserver,
467                              xserver.resource.object);
468
469         /* If it's a different client than the xserver we launched,
470          * don't start the wm. */
471         if (client != wxs->xserver.resource.client)
472                 return;
473
474         wxs->wm = wlsc_wm_create(wxs);
475         if (wxs == NULL) {
476                 fprintf(stderr, "failed to create wm\n");
477         }
478
479         wl_resource_post_event(&wxs->xserver.resource,
480                                XSERVER_LISTEN_SOCKET, wxs->abstract_fd);
481
482         wl_resource_post_event(&wxs->xserver.resource,
483                                XSERVER_LISTEN_SOCKET, wxs->unix_fd);
484 }
485
486 static int
487 wlsc_xserver_handle_event(int listen_fd, uint32_t mask, void *data)
488 {
489         struct wlsc_xserver *mxs = data;
490         char display[8], s[8], logfile[32];
491         int sv[2], flags;
492
493         if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
494                 fprintf(stderr, "socketpair failed\n");
495                 return 1;
496         }
497
498         mxs->process.pid = fork();
499         switch (mxs->process.pid) {
500         case 0:
501                 /* SOCK_CLOEXEC closes both ends, so we need to unset
502                  * the flag on the client fd. */
503                 flags = fcntl(sv[1], F_GETFD);
504                 if (flags != -1)
505                         fcntl(sv[1], F_SETFD, flags & ~FD_CLOEXEC);
506
507                 snprintf(s, sizeof s, "%d", sv[1]);
508                 setenv("WAYLAND_SOCKET", s, 1);
509
510                 snprintf(display, sizeof display, ":%d", mxs->display);
511                 snprintf(logfile, sizeof logfile,
512                          "/tmp/x-log-%d", mxs->display);
513
514                 if (execl(XSERVER_PATH,
515                           XSERVER_PATH,
516                           display,
517                           "-wayland",
518                           "-rootless",
519                           "-retro",
520                           "-logfile", logfile,
521                           "-nolisten", "all",
522                           "-terminate",
523                           NULL) < 0)
524                         fprintf(stderr, "exec failed: %m\n");
525                 exit(-1);
526
527         default:
528                 fprintf(stderr, "forked X server, pid %d\n", mxs->process.pid);
529
530                 close(sv[1]);
531                 mxs->xserver.resource.client =
532                         wl_client_create(mxs->wl_display, sv[0]);
533
534                 wlsc_watch_process(&mxs->process);
535
536                 wl_event_source_remove(mxs->abstract_source);
537                 wl_event_source_remove(mxs->unix_source);
538                 break;
539
540         case -1:
541                 fprintf(stderr, "failed to fork\n");
542                 break;
543         }
544
545         return 1;
546 }
547
548 static void
549 wlsc_xserver_shutdown(struct wlsc_xserver *wxs)
550 {
551         char path[256];
552
553         snprintf(path, sizeof path, "/tmp/.X%d-lock", wxs->display);
554         unlink(path);
555         snprintf(path, sizeof path, "/tmp/.X11-unix/X%d", wxs->display);
556         unlink(path);
557         close(wxs->abstract_fd);
558         wl_event_source_remove(wxs->abstract_source);
559         close(wxs->unix_fd);
560         wl_event_source_remove(wxs->unix_source);
561         wxs->loop = NULL;
562 }
563
564 static void
565 wlsc_xserver_cleanup(struct wlsc_process *process, int status)
566 {
567         struct wlsc_xserver *mxs =
568                 container_of(process, struct wlsc_xserver, process);
569
570         mxs->process.pid = 0;
571         mxs->xserver.resource.client = NULL;
572
573         mxs->abstract_source =
574                 wl_event_loop_add_fd(mxs->loop, mxs->abstract_fd,
575                                      WL_EVENT_READABLE,
576                                      wlsc_xserver_handle_event, mxs);
577
578         mxs->unix_source =
579                 wl_event_loop_add_fd(mxs->loop, mxs->unix_fd,
580                                      WL_EVENT_READABLE,
581                                      wlsc_xserver_handle_event, mxs);
582
583         if (mxs->wm) {
584                 fprintf(stderr, "xserver exited, code %d\n", status);
585                 wlsc_wm_destroy(mxs->wm);
586                 mxs->wm = NULL;
587         } else {
588                 /* If the X server crashes before it binds to the
589                  * xserver interface, shut down and don't try
590                  * again. */
591                 fprintf(stderr, "xserver crashing too fast: %d\n", status);
592                 wlsc_xserver_shutdown(mxs);
593         }
594 }
595
596 static void
597 xserver_set_window_id(struct wl_client *client, struct wl_resource *resource,
598                       struct wl_surface *surface, uint32_t id)
599 {
600         struct wlsc_xserver *wxs = resource->data;
601         struct wlsc_wm *wm = wxs->wm;
602         struct wlsc_wm_window *window;
603
604         if (client != wxs->xserver.resource.client)
605                 return;
606
607         window = wl_hash_table_lookup(wm->window_hash, id);
608         if (window == NULL) {
609                 fprintf(stderr, "set_window_id for unknown window %d\n", id);
610                 return;
611         }
612
613         fprintf(stderr, "set_window_id %d for surface %p\n", id, surface);
614
615         window->surface = (struct wlsc_surface *) surface;
616         /* FIXME: Do we need a surface destroy listener? */
617 }
618
619 static const struct xserver_interface xserver_implementation = {
620         xserver_set_window_id
621 };
622
623 static int
624 bind_to_abstract_socket(int display)
625 {
626         struct sockaddr_un addr;
627         socklen_t size, name_size;
628         int fd;
629
630         fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
631         if (fd < 0)
632                 return -1;
633
634         addr.sun_family = AF_LOCAL;
635         name_size = snprintf(addr.sun_path, sizeof addr.sun_path,
636                              "%c/tmp/.X11-unix/X%d", 0, display);
637         size = offsetof(struct sockaddr_un, sun_path) + name_size;
638         if (bind(fd, (struct sockaddr *) &addr, size) < 0) {
639                 fprintf(stderr, "failed to bind to @%s: %s\n",
640                         addr.sun_path + 1, strerror(errno));
641                 close(fd);
642                 return -1;
643         }
644
645         if (listen(fd, 1) < 0) {
646                 close(fd);
647                 return -1;
648         }
649
650         return fd;
651 }
652
653 static int
654 bind_to_unix_socket(int display)
655 {
656         struct sockaddr_un addr;
657         socklen_t size, name_size;
658         int fd;
659
660         fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
661         if (fd < 0)
662                 return -1;
663
664         addr.sun_family = AF_LOCAL;
665         name_size = snprintf(addr.sun_path, sizeof addr.sun_path,
666                              "/tmp/.X11-unix/X%d", display) + 1;
667         size = offsetof(struct sockaddr_un, sun_path) + name_size;
668         unlink(addr.sun_path);
669         if (bind(fd, (struct sockaddr *) &addr, size) < 0) {
670                 fprintf(stderr, "failed to bind to %s (%s)\n",
671                         addr.sun_path, strerror(errno));
672                 close(fd);
673                 return -1;
674         }
675
676         if (listen(fd, 1) < 0) {
677                 unlink(addr.sun_path);
678                 close(fd);
679                 return -1;
680         }
681
682         return fd;
683 }
684
685 static int
686 create_lockfile(int display, char *lockfile, size_t lsize)
687 {
688         char pid[16], *end;
689         int fd, size;
690         pid_t other;
691
692         snprintf(lockfile, lsize, "/tmp/.X%d-lock", display);
693         fd = open(lockfile, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0444);
694         if (fd < 0 && errno == EEXIST) {
695                 fd = open(lockfile, O_CLOEXEC, O_RDONLY);
696                 if (fd < 0 || read(fd, pid, 11) != 11) {
697                         fprintf(stderr, "can't read lock file %s: %s\n",
698                                 lockfile, strerror(errno));
699                         errno = EEXIST;
700                         return -1;
701                 }
702
703                 other = strtol(pid, &end, 0);
704                 if (end != pid + 10) {
705                         fprintf(stderr, "can't parse lock file %s\n",
706                                 lockfile);
707                         errno = EEXIST;
708                         return -1;
709                 }
710
711                 if (kill(other, 0) < 0 && errno == ESRCH) {
712                         /* stale lock file; unlink and try again */
713                         fprintf(stderr,
714                                 "unlinking stale lock file %s\n", lockfile);
715                         unlink(lockfile);
716                         errno = EAGAIN;
717                         return -1;
718                 }
719
720                 errno = EEXIST;
721                 return -1;
722         } else if (fd < 0) {
723                 fprintf(stderr, "failed to create lock file %s: %s\n",
724                         lockfile, strerror(errno));
725                 return -1;
726         }
727
728         /* Subtle detail: we use the pid of the wayland
729          * compositor, not the xserver in the lock file. */
730         size = snprintf(pid, sizeof pid, "%10d\n", getpid());
731         if (write(fd, pid, size) != size) {
732                 unlink(lockfile);
733                 close(fd);
734                 return -1;
735         }
736
737         close(fd);
738
739         return 0;
740 }
741
742 int
743 wlsc_xserver_init(struct wlsc_compositor *compositor)
744 {
745         struct wl_display *display = compositor->wl_display;
746         struct wlsc_xserver *mxs;
747         char lockfile[256];
748
749         mxs = malloc(sizeof *mxs);
750         memset(mxs, 0, sizeof *mxs);
751
752         mxs->process.cleanup = wlsc_xserver_cleanup;
753         mxs->wl_display = display;
754
755         mxs->display = 0;
756
757  retry:
758         if (create_lockfile(mxs->display, lockfile, sizeof lockfile) < 0) {
759                 if (errno == EAGAIN) {
760                         goto retry;
761                 } else if (errno == EEXIST) {
762                         mxs->display++;
763                         goto retry;
764                 } else {
765                         free(mxs);
766                         return -1;
767                 }
768         }                               
769
770         mxs->abstract_fd = bind_to_abstract_socket(mxs->display);
771         if (mxs->abstract_fd < 0 && errno == EADDRINUSE) {
772                 mxs->display++;
773                 unlink(lockfile);
774                 goto retry;
775         }
776
777         mxs->unix_fd = bind_to_unix_socket(mxs->display);
778         if (mxs->unix_fd < 0) {
779                 unlink(lockfile);
780                 close(mxs->abstract_fd);
781                 free(mxs);
782                 return -1;
783         }
784
785         fprintf(stderr, "xserver listening on display :%d\n", mxs->display);
786
787         mxs->loop = wl_display_get_event_loop(display);
788         mxs->abstract_source =
789                 wl_event_loop_add_fd(mxs->loop, mxs->abstract_fd,
790                                      WL_EVENT_READABLE,
791                                      wlsc_xserver_handle_event, mxs);
792         mxs->unix_source =
793                 wl_event_loop_add_fd(mxs->loop, mxs->unix_fd,
794                                      WL_EVENT_READABLE,
795                                      wlsc_xserver_handle_event, mxs);
796
797         mxs->xserver.resource.object.interface = &xserver_interface;
798         mxs->xserver.resource.object.implementation =
799                 (void (**)(void)) &xserver_implementation;
800         wl_display_add_object(display, &mxs->xserver.resource.object);
801         wl_display_add_global(display, &mxs->xserver.resource.object,
802                               wlsc_xserver_bind);
803
804         compositor->wxs = mxs;
805
806         return 0;
807 }
808
809 void
810 wlsc_xserver_destroy(struct wlsc_compositor *compositor)
811 {
812         struct wlsc_xserver *wxs = compositor->wxs;
813
814         if (wxs->loop)
815                 wlsc_xserver_shutdown(wxs);
816
817         free(wxs);
818 }