2 * Copyright © 2011 Intel Corporation
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.
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.
28 #include <sys/socket.h>
37 #include <wayland-server.h>
39 #include "compositor.h"
40 #include "xserver-server-protocol.h"
44 * - Clean X socket and lock file on exit
45 * - Nuke lock file if process doesn't exist.
48 * - Send take focus, hook into wlsc_surface_activate.
52 struct wl_resource resource;
56 struct wl_display *wl_display;
57 struct wl_event_loop *loop;
58 struct wl_event_source *sigchld_source;
60 struct wl_event_source *abstract_source;
62 struct wl_event_source *unix_source;
64 struct wlsc_process process;
66 struct xserver xserver;
72 xcb_connection_t *conn;
73 struct wl_event_source *source;
75 struct wl_hash_table *window_hash;
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;
88 struct wlsc_wm_window {
90 struct wlsc_surface *surface;
94 wlsc_wm_handle_configure_request(struct wlsc_wm *wm, xcb_generic_event_t *event)
96 xcb_configure_request_event_t *configure_request =
97 (xcb_configure_request_event_t *) event;
101 fprintf(stderr, "XCB_CONFIGURE_REQUEST\n");
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;
118 xcb_configure_window(wm->conn,
119 configure_request->window,
120 configure_request->value_mask, values);
124 get_atom_name(xcb_connection_t *c, xcb_atom_t atom)
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];
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));
142 wlsc_wm_get_properties(struct wlsc_wm *wm, xcb_window_t window)
144 xcb_generic_error_t *e;
145 xcb_get_property_reply_t *reply;
151 xcb_get_property_cookie_t cookie;
153 { XCB_ATOM_WM_CLASS, },
154 { XCB_ATOM_WM_TRANSIENT_FOR },
155 { wm->atom.wm_protocols, },
156 { wm->atom.net_wm_name, },
159 for (i = 0; i < ARRAY_LENGTH(props); i++)
161 xcb_get_property (wm->conn,
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);
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)");
184 wlsc_wm_activate(struct wlsc_wm *wm,
185 struct wlsc_wm_window *window, xcb_timestamp_t time)
187 xcb_set_input_focus (wm->conn,
188 XCB_INPUT_FOCUS_POINTER_ROOT, window->id, time);
192 wlsc_wm_handle_map_request(struct wlsc_wm *wm, xcb_generic_event_t *event)
194 xcb_map_request_event_t *map_request =
195 (xcb_map_request_event_t *) event;
198 fprintf(stderr, "XCB_MAP_REQUEST\n");
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);
205 xcb_map_window(wm->conn, map_request->window);
209 wlsc_wm_handle_map_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
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;
216 fprintf(stderr, "XCB_MAP_NOTIFY\n");
218 wlsc_wm_get_properties(wm, map_notify->window);
220 window = wl_hash_table_lookup(wm->window_hash, map_notify->window);
221 wlsc_wm_activate(wm, window, XCB_TIME_CURRENT_TIME);
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;
230 xcb_send_event(wm->conn, 0, window->id,
231 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
232 (char *) &client_message);
237 wlsc_wm_handle_property_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
239 xcb_property_notify_event_t *property_notify =
240 (xcb_property_notify_event_t *) event;
242 fprintf(stderr, "XCB_PROPERTY_NOTIFY\n");
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");
253 fprintf(stderr, "unhandled property change: %s\n",
254 get_atom_name(wm->conn, property_notify->atom));
259 wlsc_wm_handle_create_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
261 xcb_create_notify_event_t *create_notify =
262 (xcb_create_notify_event_t *) event;
263 struct wlsc_wm_window *window;
265 fprintf(stderr, "XCB_CREATE_NOTIFY, win %d\n", create_notify->window);
267 window = malloc(sizeof *window);
268 if (window == NULL) {
269 fprintf(stderr, "failed to allocate window\n");
273 window->id = create_notify->window;
274 wl_hash_table_insert(wm->window_hash, window->id, window);
276 fprintf(stderr, "created window %p\n", window);
280 wlsc_wm_handle_destroy_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
282 xcb_destroy_notify_event_t *destroy_notify =
283 (xcb_destroy_notify_event_t *) event;
284 struct wlsc_wm_window *window;
286 fprintf(stderr, "XCB_DESTROY_NOTIFY, win %d\n",
287 destroy_notify->window);
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);
296 fprintf(stderr, "destroy window %p\n", window);
297 wl_hash_table_remove(wm->window_hash, window->id);
302 wlsc_wm_handle_event(int fd, uint32_t mask, void *data)
304 struct wlsc_wm *wm = data;
305 xcb_generic_event_t *event;
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);
313 case XCB_MAP_REQUEST:
314 wlsc_wm_handle_map_request(wm, event);
317 wlsc_wm_handle_map_notify(wm, event);
319 case XCB_UNMAP_NOTIFY:
320 fprintf(stderr, "XCB_UNMAP_NOTIFY\n");
322 case XCB_CONFIGURE_REQUEST:
323 wlsc_wm_handle_configure_request(wm, event);
325 case XCB_CONFIGURE_NOTIFY:
326 fprintf(stderr, "XCB_CONFIGURE_NOTIFY\n");
328 case XCB_DESTROY_NOTIFY:
329 wlsc_wm_handle_destroy_notify(wm, event);
331 case XCB_MAPPING_NOTIFY:
332 fprintf(stderr, "XCB_MAPPING_NOTIFY\n");
334 case XCB_PROPERTY_NOTIFY:
335 wlsc_wm_handle_property_notify(wm, event);
338 fprintf(stderr, "Unhandled event %d\n",
339 event->response_type);
352 wxs_wm_get_resources(struct wlsc_wm *wm)
355 #define F(field) offsetof(struct wlsc_wm, field)
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) },
368 xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
369 xcb_intern_atom_reply_t *reply;
372 for (i = 0; i < ARRAY_LENGTH(atoms); i++)
373 cookies[i] = xcb_intern_atom (wm->conn, 0,
374 strlen(atoms[i].name),
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;
384 static struct wlsc_wm *
385 wlsc_wm_create(struct wlsc_xserver *wxs)
388 struct wl_event_loop *loop;
389 xcb_screen_iterator_t s;
393 wm = malloc(sizeof *wm);
397 wm->window_hash = wl_hash_table_create();
398 if (wm->window_hash == NULL) {
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);
410 wl_resource_post_event(&wxs->xserver.resource, XSERVER_CLIENT, sv[1]);
411 wl_client_flush(wxs->xserver.resource.client);
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");
419 wl_hash_table_destroy(wm->window_hash);
424 s = xcb_setup_roots_iterator(xcb_get_setup(wm->conn));
427 loop = wl_display_get_event_loop(wxs->wl_display);
429 wl_event_loop_add_fd(loop, sv[0],
431 wlsc_wm_handle_event, wm);
432 wl_event_source_check(wm->source);
434 wxs_wm_get_resources(wm);
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);
446 fprintf(stderr, "created wm\n");
452 wlsc_wm_destroy(struct wlsc_wm *wm)
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);
462 wlsc_xserver_bind(struct wl_client *client,
463 struct wl_object *global, uint32_t version)
465 struct wlsc_xserver *wxs =
466 container_of(global, struct wlsc_xserver,
467 xserver.resource.object);
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)
474 wxs->wm = wlsc_wm_create(wxs);
476 fprintf(stderr, "failed to create wm\n");
479 wl_resource_post_event(&wxs->xserver.resource,
480 XSERVER_LISTEN_SOCKET, wxs->abstract_fd);
482 wl_resource_post_event(&wxs->xserver.resource,
483 XSERVER_LISTEN_SOCKET, wxs->unix_fd);
487 wlsc_xserver_handle_event(int listen_fd, uint32_t mask, void *data)
489 struct wlsc_xserver *mxs = data;
490 char display[8], s[8], logfile[32];
493 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
494 fprintf(stderr, "socketpair failed\n");
498 mxs->process.pid = fork();
499 switch (mxs->process.pid) {
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);
505 fcntl(sv[1], F_SETFD, flags & ~FD_CLOEXEC);
507 snprintf(s, sizeof s, "%d", sv[1]);
508 setenv("WAYLAND_SOCKET", s, 1);
510 snprintf(display, sizeof display, ":%d", mxs->display);
511 snprintf(logfile, sizeof logfile,
512 "/tmp/x-log-%d", mxs->display);
514 if (execl(XSERVER_PATH,
524 fprintf(stderr, "exec failed: %m\n");
528 fprintf(stderr, "forked X server, pid %d\n", mxs->process.pid);
531 mxs->xserver.resource.client =
532 wl_client_create(mxs->wl_display, sv[0]);
534 wlsc_watch_process(&mxs->process);
536 wl_event_source_remove(mxs->abstract_source);
537 wl_event_source_remove(mxs->unix_source);
541 fprintf(stderr, "failed to fork\n");
549 wlsc_xserver_shutdown(struct wlsc_xserver *wxs)
553 snprintf(path, sizeof path, "/tmp/.X%d-lock", wxs->display);
555 snprintf(path, sizeof path, "/tmp/.X11-unix/X%d", wxs->display);
557 close(wxs->abstract_fd);
558 wl_event_source_remove(wxs->abstract_source);
560 wl_event_source_remove(wxs->unix_source);
565 wlsc_xserver_cleanup(struct wlsc_process *process, int status)
567 struct wlsc_xserver *mxs =
568 container_of(process, struct wlsc_xserver, process);
570 mxs->process.pid = 0;
571 mxs->xserver.resource.client = NULL;
573 mxs->abstract_source =
574 wl_event_loop_add_fd(mxs->loop, mxs->abstract_fd,
576 wlsc_xserver_handle_event, mxs);
579 wl_event_loop_add_fd(mxs->loop, mxs->unix_fd,
581 wlsc_xserver_handle_event, mxs);
584 fprintf(stderr, "xserver exited, code %d\n", status);
585 wlsc_wm_destroy(mxs->wm);
588 /* If the X server crashes before it binds to the
589 * xserver interface, shut down and don't try
591 fprintf(stderr, "xserver crashing too fast: %d\n", status);
592 wlsc_xserver_shutdown(mxs);
597 xserver_set_window_id(struct wl_client *client, struct wl_resource *resource,
598 struct wl_surface *surface, uint32_t id)
600 struct wlsc_xserver *wxs = resource->data;
601 struct wlsc_wm *wm = wxs->wm;
602 struct wlsc_wm_window *window;
604 if (client != wxs->xserver.resource.client)
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);
613 fprintf(stderr, "set_window_id %d for surface %p\n", id, surface);
615 window->surface = (struct wlsc_surface *) surface;
616 /* FIXME: Do we need a surface destroy listener? */
619 static const struct xserver_interface xserver_implementation = {
620 xserver_set_window_id
624 bind_to_abstract_socket(int display)
626 struct sockaddr_un addr;
627 socklen_t size, name_size;
630 fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
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));
645 if (listen(fd, 1) < 0) {
654 bind_to_unix_socket(int display)
656 struct sockaddr_un addr;
657 socklen_t size, name_size;
660 fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
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));
676 if (listen(fd, 1) < 0) {
677 unlink(addr.sun_path);
686 create_lockfile(int display, char *lockfile, size_t lsize)
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));
703 other = strtol(pid, &end, 0);
704 if (end != pid + 10) {
705 fprintf(stderr, "can't parse lock file %s\n",
711 if (kill(other, 0) < 0 && errno == ESRCH) {
712 /* stale lock file; unlink and try again */
714 "unlinking stale lock file %s\n", lockfile);
723 fprintf(stderr, "failed to create lock file %s: %s\n",
724 lockfile, strerror(errno));
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) {
743 wlsc_xserver_init(struct wlsc_compositor *compositor)
745 struct wl_display *display = compositor->wl_display;
746 struct wlsc_xserver *mxs;
749 mxs = malloc(sizeof *mxs);
750 memset(mxs, 0, sizeof *mxs);
752 mxs->process.cleanup = wlsc_xserver_cleanup;
753 mxs->wl_display = display;
758 if (create_lockfile(mxs->display, lockfile, sizeof lockfile) < 0) {
759 if (errno == EAGAIN) {
761 } else if (errno == EEXIST) {
770 mxs->abstract_fd = bind_to_abstract_socket(mxs->display);
771 if (mxs->abstract_fd < 0 && errno == EADDRINUSE) {
777 mxs->unix_fd = bind_to_unix_socket(mxs->display);
778 if (mxs->unix_fd < 0) {
780 close(mxs->abstract_fd);
785 fprintf(stderr, "xserver listening on display :%d\n", mxs->display);
787 mxs->loop = wl_display_get_event_loop(display);
788 mxs->abstract_source =
789 wl_event_loop_add_fd(mxs->loop, mxs->abstract_fd,
791 wlsc_xserver_handle_event, mxs);
793 wl_event_loop_add_fd(mxs->loop, mxs->unix_fd,
795 wlsc_xserver_handle_event, mxs);
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,
804 compositor->wxs = mxs;
810 wlsc_xserver_destroy(struct wlsc_compositor *compositor)
812 struct wlsc_xserver *wxs = compositor->wxs;
815 wlsc_xserver_shutdown(wxs);