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_object object;
56 struct wl_display *wl_display;
57 struct wl_event_loop *loop;
58 struct wl_event_source *source;
59 struct wl_event_source *sigchld_source;
60 struct wl_client *client;
62 struct sockaddr_un addr;
65 struct wlsc_process process;
67 struct xserver xserver;
73 xcb_connection_t *conn;
74 struct wl_event_source *source;
76 struct wl_hash_table *window_hash;
78 xcb_atom_t wm_protocols;
79 xcb_atom_t wm_take_focus;
80 xcb_atom_t wm_delete_window;
81 xcb_atom_t net_wm_name;
82 xcb_atom_t net_wm_icon;
83 xcb_atom_t net_wm_state;
84 xcb_atom_t net_wm_state_fullscreen;
85 xcb_atom_t utf8_string;
89 struct wlsc_wm_window {
91 struct wlsc_surface *surface;
95 wlsc_wm_handle_configure_request(struct wlsc_wm *wm, xcb_generic_event_t *event)
97 xcb_configure_request_event_t *configure_request =
98 (xcb_configure_request_event_t *) event;
102 fprintf(stderr, "XCB_CONFIGURE_REQUEST\n");
104 if (configure_request->value_mask & XCB_CONFIG_WINDOW_X)
105 values[i++] = configure_request->x;
106 if (configure_request->value_mask & XCB_CONFIG_WINDOW_Y)
107 values[i++] = configure_request->y;
108 if (configure_request->value_mask & XCB_CONFIG_WINDOW_WIDTH)
109 values[i++] = configure_request->width;
110 if (configure_request->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
111 values[i++] = configure_request->height;
112 if (configure_request->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH)
113 values[i++] = configure_request->border_width;
114 if (configure_request->value_mask & XCB_CONFIG_WINDOW_SIBLING)
115 values[i++] = configure_request->sibling;
116 if (configure_request->value_mask & XCB_CONFIG_WINDOW_STACK_MODE)
117 values[i++] = configure_request->stack_mode;
119 xcb_configure_window(wm->conn,
120 configure_request->window,
121 configure_request->value_mask, values);
125 wlsc_wm_get_properties(struct wlsc_wm *wm, xcb_window_t window)
127 xcb_generic_error_t *e;
128 xcb_get_property_reply_t *reply;
134 xcb_get_property_cookie_t cookie;
136 { XCB_ATOM_WM_CLASS, },
137 { XCB_ATOM_WM_TRANSIENT_FOR },
138 { wm->atom.wm_protocols, },
139 { wm->atom.net_wm_name, },
142 for (i = 0; i < ARRAY_LENGTH(props); i++)
144 xcb_get_property (wm->conn,
151 for (i = 0; i < ARRAY_LENGTH(props); i++) {
152 reply = xcb_get_property_reply(wm->conn, props[i].cookie, &e);
153 value = xcb_get_property_value(reply);
155 fprintf(stderr, "property %d, type %d, format %d, "
156 "length %d (value_len %d), value \"%s\"\n",
158 reply->type, reply->format,
159 xcb_get_property_value_length(reply), reply->value_len,
160 reply->type ? (char *) value : "(nil)");
167 wlsc_wm_activate(struct wlsc_wm *wm,
168 struct wlsc_wm_window *window, xcb_timestamp_t time)
170 xcb_set_input_focus (wm->conn,
171 XCB_INPUT_FOCUS_POINTER_ROOT, window->id, time);
175 wlsc_wm_handle_map_request(struct wlsc_wm *wm, xcb_generic_event_t *event)
177 xcb_map_request_event_t *map_request =
178 (xcb_map_request_event_t *) event;
181 fprintf(stderr, "XCB_MAP_REQUEST\n");
183 wlsc_wm_get_properties(wm, map_request->window);
184 values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
185 xcb_change_window_attributes(wm->conn, map_request->window,
186 XCB_CW_EVENT_MASK, values);
188 xcb_map_window(wm->conn, map_request->window);
192 wlsc_wm_handle_map_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
194 xcb_map_notify_event_t *map_notify =
195 (xcb_map_notify_event_t *) event;
196 struct wlsc_wm_window *window;
197 xcb_client_message_event_t client_message;
199 fprintf(stderr, "XCB_MAP_NOTIFY\n");
201 wlsc_wm_get_properties(wm, map_notify->window);
203 window = wl_hash_table_lookup(wm->window_hash, map_notify->window);
204 wlsc_wm_activate(wm, window, XCB_TIME_CURRENT_TIME);
206 client_message.response_type = XCB_CLIENT_MESSAGE;
207 client_message.format = 32;
208 client_message.window = window->id;
209 client_message.type = wm->atom.wm_protocols;
210 client_message.data.data32[0] = wm->atom.wm_take_focus;
211 client_message.data.data32[1] = XCB_TIME_CURRENT_TIME;
213 xcb_send_event(wm->conn, 0, window->id,
214 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
215 (char *) &client_message);
220 wlsc_wm_handle_property_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
222 xcb_property_notify_event_t *property_notify =
223 (xcb_property_notify_event_t *) event;
225 fprintf(stderr, "XCB_PROPERTY_NOTIFY\n");
227 if (property_notify->atom == XCB_ATOM_WM_CLASS) {
228 fprintf(stderr, "wm_class changed\n");
229 } else if (property_notify->atom == XCB_ATOM_WM_TRANSIENT_FOR) {
230 fprintf(stderr, "wm_transient_for changed\n");
231 } else if (property_notify->atom == wm->atom.wm_protocols) {
232 fprintf(stderr, "wm_protocols changed\n");
233 } else if (property_notify->atom == wm->atom.net_wm_name) {
234 fprintf(stderr, "wm_class changed\n");
236 fprintf(stderr, "unhandled property change: %d\n",
237 property_notify->atom);
242 wlsc_wm_handle_create_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
244 xcb_create_notify_event_t *create_notify =
245 (xcb_create_notify_event_t *) event;
246 struct wlsc_wm_window *window;
248 fprintf(stderr, "XCB_CREATE_NOTIFY, win %d\n", create_notify->window);
250 window = malloc(sizeof *window);
251 if (window == NULL) {
252 fprintf(stderr, "failed to allocate window\n");
256 window->id = create_notify->window;
257 wl_hash_table_insert(wm->window_hash, window->id, window);
259 fprintf(stderr, "created window %p\n", window);
263 wlsc_wm_handle_destroy_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
265 xcb_destroy_notify_event_t *destroy_notify =
266 (xcb_destroy_notify_event_t *) event;
267 struct wlsc_wm_window *window;
269 fprintf(stderr, "XCB_DESTROY_NOTIFY, win %d\n",
270 destroy_notify->window);
272 window = wl_hash_table_lookup(wm->window_hash, destroy_notify->window);
273 if (window == NULL) {
274 fprintf(stderr, "destroy notify for unknow window %d\n",
275 destroy_notify->window);
279 fprintf(stderr, "destroy window %p\n", window);
280 wl_hash_table_remove(wm->window_hash, window->id);
285 wlsc_wm_handle_event(int fd, uint32_t mask, void *data)
287 struct wlsc_wm *wm = data;
288 xcb_generic_event_t *event;
291 while (event = xcb_poll_for_event(wm->conn), event != NULL) {
292 switch (event->response_type) {
293 case XCB_CREATE_NOTIFY:
294 wlsc_wm_handle_create_notify(wm, event);
296 case XCB_MAP_REQUEST:
297 wlsc_wm_handle_map_request(wm, event);
300 wlsc_wm_handle_map_notify(wm, event);
302 case XCB_UNMAP_NOTIFY:
303 fprintf(stderr, "XCB_UNMAP_NOTIFY\n");
305 case XCB_CONFIGURE_REQUEST:
306 wlsc_wm_handle_configure_request(wm, event);
308 case XCB_CONFIGURE_NOTIFY:
309 fprintf(stderr, "XCB_CONFIGURE_NOTIFY\n");
311 case XCB_DESTROY_NOTIFY:
312 wlsc_wm_handle_destroy_notify(wm, event);
314 case XCB_MAPPING_NOTIFY:
315 fprintf(stderr, "XCB_MAPPING_NOTIFY\n");
317 case XCB_PROPERTY_NOTIFY:
318 wlsc_wm_handle_property_notify(wm, event);
321 fprintf(stderr, "Unhandled event %d\n",
322 event->response_type);
335 wxs_wm_get_resources(struct wlsc_wm *wm)
338 #define F(field) offsetof(struct wlsc_wm, field)
340 static const struct { const char *name; int offset; } atoms[] = {
341 { "WM_PROTOCOLS", F(atom.wm_protocols) },
342 { "WM_TAKE_FOCUS", F(atom.wm_take_focus) },
343 { "WM_DELETE_WINDOW", F(atom.wm_delete_window) },
344 { "_NET_WM_NAME", F(atom.net_wm_name) },
345 { "_NET_WM_ICON", F(atom.net_wm_icon) },
346 { "_NET_WM_STATE", F(atom.net_wm_state) },
347 { "_NET_WM_STATE_FULLSCREEN", F(atom.net_wm_state_fullscreen) },
348 { "UTF8_STRING", F(atom.utf8_string) },
351 xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
352 xcb_intern_atom_reply_t *reply;
355 for (i = 0; i < ARRAY_LENGTH(atoms); i++)
356 cookies[i] = xcb_intern_atom (wm->conn, 0,
357 strlen(atoms[i].name),
360 for (i = 0; i < ARRAY_LENGTH(atoms); i++) {
361 reply = xcb_intern_atom_reply (wm->conn, cookies[i], NULL);
362 *(xcb_atom_t *) ((char *) wm + atoms[i].offset) = reply->atom;
367 static struct wlsc_wm *
368 wlsc_wm_create(struct wlsc_xserver *wxs)
371 struct wl_event_loop *loop;
372 xcb_screen_iterator_t s;
376 wm = malloc(sizeof *wm);
380 wm->window_hash = wl_hash_table_create();
381 if (wm->window_hash == NULL) {
386 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
387 fprintf(stderr, "socketpair failed\n");
388 wl_hash_table_destroy(wm->window_hash);
393 wl_client_post_event(wxs->client,
394 &wxs->xserver.object,
395 XSERVER_CLIENT, sv[1]);
396 wl_client_flush(wxs->client);
399 wm->conn = xcb_connect_to_fd(sv[0], NULL);
400 if (xcb_connection_has_error(wm->conn)) {
401 fprintf(stderr, "xcb_connect_to_fd failed\n");
403 wl_hash_table_destroy(wm->window_hash);
408 s = xcb_setup_roots_iterator(xcb_get_setup(wm->conn));
411 loop = wl_display_get_event_loop(wxs->wl_display);
413 wl_event_loop_add_fd(loop, sv[0],
415 wlsc_wm_handle_event, wm);
416 wl_event_source_check(wm->source);
418 wxs_wm_get_resources(wm);
421 XCB_EVENT_MASK_STRUCTURE_NOTIFY |
422 XCB_EVENT_MASK_RESIZE_REDIRECT |
423 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
424 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
425 XCB_EVENT_MASK_PROPERTY_CHANGE;
426 xcb_change_window_attributes(wm->conn, wm->screen->root,
427 XCB_CW_EVENT_MASK, values);
430 fprintf(stderr, "created wm\n");
436 wlsc_xserver_bind(struct wl_client *client,
437 struct wl_object *global,
440 struct wlsc_xserver *wxs =
441 container_of(global, struct wlsc_xserver, xserver.object);
443 wxs->wm = wlsc_wm_create(wxs);
445 fprintf(stderr, "failed to create wm\n");
448 wl_client_post_event(wxs->client,
449 &wxs->xserver.object,
450 XSERVER_LISTEN_SOCKET, wxs->fd);
454 wlsc_xserver_handle_event(int listen_fd, uint32_t mask, void *data)
456 struct wlsc_xserver *mxs = data;
457 char display[8], s[8], logfile[32];
460 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
461 fprintf(stderr, "socketpair failed\n");
465 mxs->process.pid = fork();
466 switch (mxs->process.pid) {
468 /* SOCK_CLOEXEC closes both ends, so we need to unset
469 * the flag on the client fd. */
470 flags = fcntl(sv[1], F_GETFD);
472 fcntl(sv[1], F_SETFD, flags & ~FD_CLOEXEC);
474 snprintf(s, sizeof s, "%d", sv[1]);
475 setenv("WAYLAND_SOCKET", s, 1);
477 snprintf(display, sizeof display, ":%d", mxs->display);
478 snprintf(logfile, sizeof logfile,
479 "/tmp/x-log-%d", mxs->display);
481 if (execl("/usr/bin/Xorg",
491 fprintf(stderr, "exec failed: %m\n");
495 fprintf(stderr, "forked X server, pid %d\n", mxs->process.pid);
498 mxs->client = wl_client_create(mxs->wl_display, sv[0]);
500 wlsc_watch_process(&mxs->process);
502 wl_event_source_remove(mxs->source);
506 fprintf(stderr, "failed to fork\n");
514 wlsc_xserver_cleanup(struct wlsc_process *process, int status)
516 struct wlsc_xserver *mxs =
517 container_of(process, struct wlsc_xserver, process);
519 fprintf(stderr, "xserver exited, code %d\n", status);
520 mxs->process.pid = 0;
522 wl_event_loop_add_fd(mxs->loop, mxs->fd, WL_EVENT_READABLE,
523 wlsc_xserver_handle_event, mxs);
527 xserver_set_window_id(struct wl_client *client, struct xserver *xserver,
528 struct wl_surface *surface, uint32_t id)
530 struct wlsc_xserver *wxs =
531 container_of(xserver, struct wlsc_xserver, xserver);
532 struct wlsc_wm *wm = wxs->wm;
533 struct wlsc_wm_window *window;
535 window = wl_hash_table_lookup(wm->window_hash, id);
536 if (window == NULL) {
537 fprintf(stderr, "set_window_id for unknown window %d\n", id);
541 fprintf(stderr, "set_window_id %d for surface %p\n", id, surface);
543 window->surface = (struct wlsc_surface *) surface;
544 /* FIXME: Do we need a surface destroy listener? */
547 static const struct xserver_interface xserver_implementation = {
548 xserver_set_window_id
552 wlsc_xserver_init(struct wlsc_compositor *compositor)
554 struct wl_display *display = compositor->wl_display;
555 struct wlsc_xserver *mxs;
556 char lockfile[256], pid[16], *end;
557 socklen_t size, name_size;
561 mxs = malloc(sizeof *mxs);
562 memset(mxs, 0, sizeof mxs);
564 mxs->process.cleanup = wlsc_xserver_cleanup;
565 mxs->wl_display = display;
566 mxs->addr.sun_family = AF_LOCAL;
567 mxs->fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
575 snprintf(lockfile, sizeof lockfile,
576 "/tmp/.X%d-lock", mxs->display);
578 O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0444);
579 if (fd < 0 && errno == EEXIST) {
580 fd = open(lockfile, O_CLOEXEC, O_RDONLY);
581 if (fd < 0 || read(fd, pid, 11) != 11) {
583 "can't read lock file %s: %s\n",
584 lockfile, strerror(errno));
589 other = strtol(pid, &end, 0);
590 if (end != pid + 10) {
591 fprintf(stderr, "can't parse lock file %s\n",
597 if (kill(other, 0) < 0 && errno == ESRCH) {
598 /* stale lock file; unlink and try again */
600 "unlinking stale lock file %s\n",
609 fprintf(stderr, "failed to create lock file %s: %s\n",
610 lockfile, strerror(errno));
616 /* Subtle detail: we use the pid of the wayland
617 * compositor, not the xserver in the lock file. */
618 size = snprintf(pid, sizeof pid, "%10d\n", getpid());
619 write(fd, pid, size);
622 name_size = snprintf(mxs->addr.sun_path,
623 sizeof mxs->addr.sun_path,
624 "/tmp/.X11-unix/X%d", mxs->display) + 1;
625 size = offsetof(struct sockaddr_un, sun_path) + name_size;
626 unlink(mxs->addr.sun_path);
627 if (bind(mxs->fd, (struct sockaddr *) &mxs->addr, size) < 0) {
628 fprintf(stderr, "failed to bind to %s (%s)\n",
629 mxs->addr.sun_path, strerror(errno));
636 } while (errno != 0);
638 fprintf(stderr, "xserver listening on display :%d\n", mxs->display);
640 if (listen(mxs->fd, 1) < 0) {
641 unlink(mxs->addr.sun_path);
648 mxs->loop = wl_display_get_event_loop(display);
650 wl_event_loop_add_fd(mxs->loop, mxs->fd, WL_EVENT_READABLE,
651 wlsc_xserver_handle_event, mxs);
653 mxs->xserver.object.interface = &xserver_interface;
654 mxs->xserver.object.implementation =
655 (void (**)(void)) &xserver_implementation;
656 wl_display_add_object(display, &mxs->xserver.object);
657 wl_display_add_global(display,
658 &mxs->xserver.object, wlsc_xserver_bind);
660 compositor->wxs = mxs;
666 wlsc_xserver_destroy(struct wlsc_compositor *compositor)
668 struct wlsc_xserver *wxs = compositor->wxs;
671 snprintf(path, sizeof path, "/tmp/.X%d-lock", wxs->display);
673 snprintf(path, sizeof path, "/tmp/.X11-unix/X%d", wxs->display);
676 wl_event_source_remove(wxs->source);