95c654680fad5483268663526d36cbb0479feb15
[profile/ivi/weston.git] / src / 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 #include <xcb/xfixes.h>
37
38 #include <wayland-server.h>
39
40 #include "compositor.h"
41 #include "xserver-server-protocol.h"
42 #include "hash.h"
43
44 struct xserver {
45         struct wl_resource resource;
46 };
47
48 struct weston_xserver {
49         struct wl_display *wl_display;
50         struct wl_event_loop *loop;
51         struct wl_event_source *sigchld_source;
52         int abstract_fd;
53         struct wl_event_source *abstract_source;
54         int unix_fd;
55         struct wl_event_source *unix_source;
56         int display;
57         struct weston_process process;
58         struct wl_resource *resource;
59         struct wl_client *client;
60         struct weston_compositor *compositor;
61         struct weston_wm *wm;
62         struct wl_listener activate_listener;
63         struct wl_listener destroy_listener;
64 };
65
66 struct weston_wm {
67         xcb_connection_t *conn;
68         const xcb_query_extension_reply_t *xfixes;
69         struct wl_event_source *source;
70         xcb_screen_t *screen;
71         struct hash_table *window_hash;
72         struct weston_xserver *server;
73         xcb_window_t wm_window;
74
75         xcb_window_t selection_window;
76         int incr;
77         int data_source_fd;
78         struct wl_event_source *property_source;
79         xcb_get_property_reply_t *property_reply;
80         int property_start;
81         struct wl_array source_data;
82         xcb_selection_request_event_t selection_request;
83         xcb_atom_t selection_target;
84         xcb_timestamp_t selection_timestamp;
85         int selection_property_set;
86         int flush_property_on_delete;
87         struct wl_listener selection_listener;
88
89         struct {
90                 xcb_atom_t               wm_protocols;
91                 xcb_atom_t               wm_take_focus;
92                 xcb_atom_t               wm_delete_window;
93                 xcb_atom_t               net_wm_name;
94                 xcb_atom_t               net_wm_icon;
95                 xcb_atom_t               net_wm_state;
96                 xcb_atom_t               net_wm_state_fullscreen;
97                 xcb_atom_t               net_wm_user_time;
98                 xcb_atom_t               net_wm_icon_name;
99                 xcb_atom_t               net_wm_window_type;
100                 xcb_atom_t               net_wm_moveresize;
101                 xcb_atom_t               net_supporting_wm_check;
102                 xcb_atom_t               net_supported;
103                 xcb_atom_t               clipboard;
104                 xcb_atom_t               targets;
105                 xcb_atom_t               utf8_string;
106                 xcb_atom_t               wl_selection;
107                 xcb_atom_t               incr;
108                 xcb_atom_t               timestamp;
109                 xcb_atom_t               multiple;
110                 xcb_atom_t               compound_text;
111                 xcb_atom_t               text;
112                 xcb_atom_t               string;
113                 xcb_atom_t               text_plain_utf8;
114                 xcb_atom_t               text_plain;
115         } atom;
116 };
117
118 struct weston_wm_window {
119         xcb_window_t id;
120         struct weston_surface *surface;
121         struct shell_surface *shsurf;
122         struct wl_listener surface_destroy_listener;
123         char *class;
124         char *name;
125         struct weston_wm_window *transient_for;
126         uint32_t protocols;
127         xcb_atom_t type;
128 };
129
130 static struct weston_wm_window *
131 get_wm_window(struct weston_surface *surface);
132
133 static const char *
134 get_atom_name(xcb_connection_t *c, xcb_atom_t atom)
135 {
136         xcb_get_atom_name_cookie_t cookie;
137         xcb_get_atom_name_reply_t *reply;
138         xcb_generic_error_t *e;
139         static char buffer[64];
140
141         if (atom == XCB_ATOM_NONE)
142                 return "None";
143
144         cookie = xcb_get_atom_name (c, atom);
145         reply = xcb_get_atom_name_reply (c, cookie, &e);
146         snprintf(buffer, sizeof buffer, "%.*s",
147                  xcb_get_atom_name_name_length (reply),
148                  xcb_get_atom_name_name (reply));
149         free(reply);
150
151         return buffer;
152 }
153
154 static void
155 dump_property(struct weston_wm *wm, xcb_atom_t property,
156               xcb_get_property_reply_t *reply)
157 {
158         int32_t *incr_value;
159         const char *text_value, *name;
160         xcb_atom_t *atom_value;
161         int width, len;
162         uint32_t i;
163
164         width = fprintf(stderr, "  %s: ", get_atom_name(wm->conn, property));
165         if (reply == NULL) {
166                 fprintf(stderr, "(no reply)\n");
167                 return;
168         }
169
170         width += fprintf(stderr,
171                          "type %s, format %d, length %d (value_len %d): ",
172                          get_atom_name(wm->conn, reply->type),
173                          reply->format,
174                          xcb_get_property_value_length(reply),
175                          reply->value_len);
176
177         if (reply->type == wm->atom.incr) {
178                 incr_value = xcb_get_property_value(reply);
179                 fprintf(stderr, "%d\n", *incr_value);
180         } else if (reply->type == wm->atom.utf8_string ||
181               reply->type == wm->atom.string) {
182                 text_value = xcb_get_property_value(reply);
183                 if (reply->value_len > 40)
184                         len = 40;
185                 else
186                         len = reply->value_len;
187                 fprintf(stderr, "\"%.*s\"\n", len, text_value);
188         } else if (reply->type == XCB_ATOM_ATOM) {
189                 atom_value = xcb_get_property_value(reply);
190                 for (i = 0; i < reply->value_len; i++) {
191                         name = get_atom_name(wm->conn, atom_value[i]);
192                         if (width + strlen(name) + 2 > 78) {
193                                 fprintf(stderr, "\n    ");
194                                 width = 4;
195                         } else if (i > 0) {
196                                 width += fprintf(stderr, ", ");
197                         }
198
199                         width += fprintf(stderr, "%s", name);
200                 }
201                 fprintf(stderr, "\n");
202         } else {
203                 fprintf(stderr, "huh?\n");
204         }
205 }
206
207 static void
208 dump_window_properties(struct weston_wm *wm, xcb_window_t window)
209 {
210         xcb_list_properties_cookie_t list_cookie;
211         xcb_list_properties_reply_t *list_reply;
212         xcb_get_property_cookie_t property_cookie;
213         xcb_get_property_reply_t *property_reply;
214         xcb_atom_t *atoms;
215         int i, length;
216
217         list_cookie = xcb_list_properties(wm->conn, window);
218         list_reply = xcb_list_properties_reply(wm->conn, list_cookie, NULL);
219         if (!list_reply)
220                 /* Bad window, typically */
221                 return;
222
223         length = xcb_list_properties_atoms_length(list_reply);
224         atoms = xcb_list_properties_atoms(list_reply);
225
226         for (i = 0; i < length; i++) {
227                 property_cookie =
228                         xcb_get_property(wm->conn,
229                                          0, /* delete */
230                                          window,
231                                          atoms[i],
232                                          XCB_ATOM_ANY,
233                                          0, 2048);
234
235                 property_reply = xcb_get_property_reply(wm->conn,
236                                                         property_cookie, NULL);
237                 dump_property(wm, atoms[i], property_reply);
238
239                 free(property_reply);
240         }
241
242         free(list_reply);
243 }
244
245 static void
246 data_offer_accept(struct wl_client *client, struct wl_resource *resource,
247                   uint32_t time, const char *mime_type)
248 {
249 }
250
251 static void
252 data_offer_receive(struct wl_client *client, struct wl_resource *resource,
253                    const char *mime_type, int32_t fd)
254 {
255         struct wl_data_offer *offer = resource->data;
256         struct weston_wm *wm = offer->source->resource.data;
257
258         if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
259                 /* Get data for the utf8_string target */
260                 xcb_convert_selection(wm->conn,
261                                       wm->selection_window,
262                                       wm->atom.clipboard,
263                                       wm->atom.utf8_string,
264                                       wm->atom.wl_selection,
265                                       XCB_TIME_CURRENT_TIME);
266
267                 xcb_flush(wm->conn);
268
269                 fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
270                 wm->data_source_fd = fd;
271         } else {
272                 close(fd);
273         }
274 }
275
276 static void
277 data_offer_destroy(struct wl_client *client, struct wl_resource *resource)
278 {
279         wl_resource_destroy(resource);
280 }
281
282 static const struct wl_data_offer_interface data_offer_interface = {
283         data_offer_accept,
284         data_offer_receive,
285         data_offer_destroy,
286 };
287
288 static void
289 data_source_cancel(struct wl_data_source *source)
290 {
291 }
292
293 static void
294 weston_wm_get_selection_targets(struct weston_wm *wm)
295 {
296         struct wl_data_source *source;
297         struct weston_compositor *compositor;
298         xcb_get_property_cookie_t cookie;
299         xcb_get_property_reply_t *reply;
300         xcb_atom_t *value;
301         char **p;
302         uint32_t i;
303
304         cookie = xcb_get_property(wm->conn,
305                                   1, /* delete */
306                                   wm->selection_window,
307                                   wm->atom.wl_selection,
308                                   XCB_GET_PROPERTY_TYPE_ANY,
309                                   0, /* offset */
310                                   4096 /* length */);
311
312         reply = xcb_get_property_reply(wm->conn, cookie, NULL);
313
314         dump_property(wm, wm->atom.wl_selection, reply);
315
316         if (reply->type != XCB_ATOM_ATOM) {
317                 free(reply);
318                 return;
319         }
320
321         source = malloc(sizeof *source);
322         if (source == NULL)
323                 return;
324
325         wl_signal_init(&source->resource.destroy_signal);
326         source->offer_interface = &data_offer_interface;
327         source->cancel = data_source_cancel;
328         source->resource.data = wm;
329
330         wl_array_init(&source->mime_types);
331         value = xcb_get_property_value(reply);
332         for (i = 0; i < reply->value_len; i++) {
333                 if (value[i] == wm->atom.utf8_string) {
334                         p = wl_array_add(&source->mime_types, sizeof *p);
335                         if (p)
336                                 *p = strdup("text/plain;charset=utf-8");
337                 }
338         }
339
340         compositor = wm->server->compositor;
341         wl_input_device_set_selection(compositor->input_device, source,
342                                       wl_display_next_serial(compositor->wl_display));
343
344         free(reply);
345 }
346
347 static int
348 weston_wm_write_property(int fd, uint32_t mask, void *data)
349 {
350         struct weston_wm *wm = data;
351         unsigned char *property;
352         int len, remainder;
353
354         property = xcb_get_property_value(wm->property_reply);
355         remainder = xcb_get_property_value_length(wm->property_reply) -
356                 wm->property_start;
357
358         len = write(fd, property + wm->property_start, remainder);
359         if (len == -1) {
360                 free(wm->property_reply);
361                 wl_event_source_remove(wm->property_source);
362                 close(fd);
363                 fprintf(stderr, "write error to target fd: %m\n");
364                 return 1;
365         }
366
367         fprintf(stderr, "wrote %d (chunk size %d) of %d bytes\n",
368                 wm->property_start + len,
369                 len, xcb_get_property_value_length(wm->property_reply));
370
371         wm->property_start += len;
372         if (len == remainder) {
373                 free(wm->property_reply);
374                 wl_event_source_remove(wm->property_source);
375
376                 if (wm->incr) {
377                         xcb_delete_property(wm->conn,
378                                             wm->selection_window,
379                                             wm->atom.wl_selection);
380                 } else {
381                         fprintf(stderr, "transfer complete\n");
382                         close(fd);
383                 }
384         }
385
386         return 1;
387 }
388
389 static void
390 weston_wm_get_selection_data(struct weston_wm *wm)
391 {
392         xcb_get_property_cookie_t cookie;
393         xcb_get_property_reply_t *reply;
394
395         cookie = xcb_get_property(wm->conn,
396                                   1, /* delete */
397                                   wm->selection_window,
398                                   wm->atom.wl_selection,
399                                   XCB_GET_PROPERTY_TYPE_ANY,
400                                   0, /* offset */
401                                   0x1fffffff /* length */);
402
403         reply = xcb_get_property_reply(wm->conn, cookie, NULL);
404
405         if (reply->type == wm->atom.incr) {
406                 dump_property(wm, wm->atom.wl_selection, reply);
407                 wm->incr = 1;
408                 free(reply);
409         } else {
410                 dump_property(wm, wm->atom.wl_selection, reply);
411                 wm->incr = 0;
412                 wm->property_start = 0;
413                 wm->property_source =
414                         wl_event_loop_add_fd(wm->server->loop,
415                                              wm->data_source_fd,
416                                              WL_EVENT_WRITABLE,
417                                              weston_wm_write_property,
418                                              wm);
419                 wm->property_reply = reply;
420         }
421 }
422
423 static void
424 weston_wm_get_incr_chunk(struct weston_wm *wm)
425 {
426         xcb_get_property_cookie_t cookie;
427         xcb_get_property_reply_t *reply;
428
429         cookie = xcb_get_property(wm->conn,
430                                   0, /* delete */
431                                   wm->selection_window,
432                                   wm->atom.wl_selection,
433                                   XCB_GET_PROPERTY_TYPE_ANY,
434                                   0, /* offset */
435                                   0x1fffffff /* length */);
436
437         reply = xcb_get_property_reply(wm->conn, cookie, NULL);
438
439         dump_property(wm, wm->atom.wl_selection, reply);
440
441         if (xcb_get_property_value_length(reply) > 0) {
442                 wm->property_start = 0;
443                 wm->property_source =
444                         wl_event_loop_add_fd(wm->server->loop,
445                                              wm->data_source_fd,
446                                              WL_EVENT_WRITABLE,
447                                              weston_wm_write_property,
448                                              wm);
449                 wm->property_reply = reply;
450         } else {
451                 fprintf(stderr, "transfer complete\n");
452                 close(wm->data_source_fd);
453                 free(reply);
454         }
455 }
456
457 static void
458 weston_wm_set_selection(struct wl_listener *listener, void *data)
459 {
460         struct wl_input_device *device = data;
461         struct weston_wm *wm =
462                 container_of(listener, struct weston_wm, selection_listener);
463         struct wl_data_source *source = device->selection_data_source;
464         const char **p, **end;
465         int has_text_plain = 0;
466
467         if (source->offer_interface == &data_offer_interface)
468                 return;
469
470         fprintf(stderr, "set selection\n");
471
472         p = source->mime_types.data;
473         end = (const char **)
474                 ((char *) source->mime_types.data + source->mime_types.size);
475         while (p < end) {
476                 fprintf(stderr, "  %s\n", *p);
477                 if (strcmp(*p, "text/plain") == 0 ||
478                     strcmp(*p, "text/plain;charset=utf-8") == 0)
479                         has_text_plain = 1;
480                 p++;
481         }
482
483         if (has_text_plain) {
484                 xcb_set_selection_owner(wm->conn,
485                                         wm->selection_window,
486                                         wm->atom.clipboard,
487                                         XCB_TIME_CURRENT_TIME);
488         } else {
489                 xcb_set_selection_owner(wm->conn,
490                                         XCB_ATOM_NONE,
491                                         wm->atom.clipboard,
492                                         XCB_TIME_CURRENT_TIME);
493         }
494 }
495
496 static void
497 weston_wm_handle_configure_request(struct weston_wm *wm, xcb_generic_event_t *event)
498 {
499         xcb_configure_request_event_t *configure_request = 
500                 (xcb_configure_request_event_t *) event;
501         uint32_t values[16];
502         int i = 0;
503
504         fprintf(stderr, "XCB_CONFIGURE_REQUEST (window %d) %d,%d @ %dx%d\n",
505                 configure_request->window,
506                 configure_request->x, configure_request->y,
507                 configure_request->width, configure_request->height);
508
509         if (configure_request->value_mask & XCB_CONFIG_WINDOW_X)
510                 values[i++] = configure_request->x;
511         if (configure_request->value_mask & XCB_CONFIG_WINDOW_Y)
512                 values[i++] = configure_request->y;
513         if (configure_request->value_mask & XCB_CONFIG_WINDOW_WIDTH)
514                 values[i++] = configure_request->width;
515         if (configure_request->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
516                 values[i++] = configure_request->height;
517         if (configure_request->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH)
518                 values[i++] = configure_request->border_width;
519         if (configure_request->value_mask & XCB_CONFIG_WINDOW_SIBLING)
520                 values[i++] = configure_request->sibling;
521         if (configure_request->value_mask & XCB_CONFIG_WINDOW_STACK_MODE)
522                 values[i++] = configure_request->stack_mode;
523
524         xcb_configure_window(wm->conn,
525                              configure_request->window,
526                              configure_request->value_mask, values);
527 }
528
529 static void
530 weston_wm_handle_configure_notify(struct weston_wm *wm, xcb_generic_event_t *event)
531 {
532         xcb_configure_notify_event_t *configure_notify = 
533                 (xcb_configure_notify_event_t *) event;
534
535         fprintf(stderr, "XCB_CONFIGURE_NOTIFY (window %d) %d,%d @ %dx%d\n",
536                 configure_notify->window,
537                 configure_notify->x, configure_notify->y,
538                 configure_notify->width, configure_notify->height);
539 }
540
541 static void
542 weston_wm_activate(struct weston_wm *wm,
543                  struct weston_wm_window *window, xcb_timestamp_t time)
544 {
545         xcb_client_message_event_t client_message;
546
547         client_message.response_type = XCB_CLIENT_MESSAGE;
548         client_message.format = 32;
549         client_message.window = window->id;
550         client_message.type = wm->atom.wm_protocols;
551         client_message.data.data32[0] = wm->atom.wm_take_focus;
552         client_message.data.data32[1] = XCB_TIME_CURRENT_TIME;
553
554         xcb_send_event(wm->conn, 0, window->id, 
555                        XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
556                        (char *) &client_message);
557
558         xcb_set_input_focus (wm->conn,
559                              XCB_INPUT_FOCUS_POINTER_ROOT, window->id, time);
560 }
561
562 static void
563 weston_xserver_surface_activate(struct wl_listener *listener, void *data)
564 {
565         struct weston_surface *surface = data;
566         struct weston_wm_window *window = get_wm_window(surface);
567         struct weston_xserver *wxs =
568                 container_of(listener,
569                              struct weston_xserver, activate_listener);
570
571         if (window)
572                 weston_wm_activate(wxs->wm, window, XCB_TIME_CURRENT_TIME);
573         else if (wxs && wxs->wm)
574                 xcb_set_input_focus (wxs->wm->conn,
575                                      XCB_INPUT_FOCUS_POINTER_ROOT,
576                                      XCB_NONE,
577                                      XCB_TIME_CURRENT_TIME);
578 }
579
580 static void
581 weston_wm_handle_map_request(struct weston_wm *wm, xcb_generic_event_t *event)
582 {
583         xcb_map_request_event_t *map_request =
584                 (xcb_map_request_event_t *) event;
585
586         fprintf(stderr, "XCB_MAP_REQUEST (window %d)\n", map_request->window);
587
588         xcb_map_window(wm->conn, map_request->window);
589 }
590
591 static void
592 weston_wm_handle_map_notify(struct weston_wm *wm, xcb_generic_event_t *event)
593 {
594         xcb_map_notify_event_t *map_notify = (xcb_map_notify_event_t *) event;
595         struct weston_wm_window *window;
596
597         fprintf(stderr, "XCB_MAP_NOTIFY (window %d)\n", map_notify->window);
598
599         window = hash_table_lookup(wm->window_hash, map_notify->window);
600         weston_wm_activate(wm, window, XCB_TIME_CURRENT_TIME);
601 }
602
603 static const size_t incr_chunk_size = 64 * 1024;
604
605 static void
606 weston_wm_send_selection_notify(struct weston_wm *wm, xcb_atom_t property)
607 {
608         xcb_selection_notify_event_t selection_notify;
609
610         memset(&selection_notify, 0, sizeof selection_notify);
611         selection_notify.response_type = XCB_SELECTION_NOTIFY;
612         selection_notify.sequence = 0;
613         selection_notify.time = wm->selection_request.time;
614         selection_notify.requestor = wm->selection_request.requestor;
615         selection_notify.selection = wm->selection_request.selection;
616         selection_notify.target = wm->selection_request.target;
617         selection_notify.property = property;
618
619         xcb_send_event(wm->conn, 0, /* propagate */
620                        wm->selection_request.requestor,
621                        XCB_EVENT_MASK_NO_EVENT, (char *) &selection_notify);
622 }
623
624 static void
625 weston_wm_send_targets(struct weston_wm *wm)
626 {
627         xcb_atom_t targets[] = {
628                 wm->atom.timestamp,
629                 wm->atom.targets,
630                 wm->atom.utf8_string,
631                 /* wm->atom.compound_text, */
632                 wm->atom.text,
633                 /* wm->atom.string */
634         };
635
636         xcb_change_property(wm->conn,
637                             XCB_PROP_MODE_REPLACE,
638                             wm->selection_request.requestor,
639                             wm->selection_request.property,
640                             XCB_ATOM_ATOM,
641                             32, /* format */
642                             ARRAY_LENGTH(targets), targets);
643
644         weston_wm_send_selection_notify(wm, wm->selection_request.property);
645 }
646
647 static void
648 weston_wm_send_timestamp(struct weston_wm *wm)
649 {
650         xcb_change_property(wm->conn,
651                             XCB_PROP_MODE_REPLACE,
652                             wm->selection_request.requestor,
653                             wm->selection_request.property,
654                             XCB_ATOM_INTEGER,
655                             32, /* format */
656                             1, &wm->selection_timestamp);
657
658         weston_wm_send_selection_notify(wm, wm->selection_request.property);
659 }
660
661 static int
662 weston_wm_flush_source_data(struct weston_wm *wm)
663 {
664         int length;
665
666         xcb_change_property(wm->conn,
667                             XCB_PROP_MODE_REPLACE,
668                             wm->selection_request.requestor,
669                             wm->selection_request.property,
670                             wm->selection_target,
671                             8, /* format */
672                             wm->source_data.size,
673                             wm->source_data.data);
674         wm->selection_property_set = 1;
675         length = wm->source_data.size;
676         wm->source_data.size = 0;
677
678         return length;
679 }
680
681 static int
682 weston_wm_read_data_source(int fd, uint32_t mask, void *data)
683 {
684         struct weston_wm *wm = data;
685         int len, current, available;
686         void *p;
687
688         current = wm->source_data.size;
689         if (wm->source_data.size < incr_chunk_size)
690                 p = wl_array_add(&wm->source_data, incr_chunk_size);
691         else
692                 p = (char *) wm->source_data.data + wm->source_data.size;
693         available = wm->source_data.alloc - current;
694
695         len = read(fd, p, available);
696         if (len == -1) {
697                 fprintf(stderr, "read error from data source: %m\n");
698                 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
699                 wl_event_source_remove(wm->property_source);
700                 close(fd);
701                 wl_array_release(&wm->source_data);
702         }
703
704         fprintf(stderr, "read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n",
705                 len, available, mask, len, (char *) p);
706
707         wm->source_data.size = current + len;
708         if (wm->source_data.size >= incr_chunk_size) {
709                 if (!wm->incr) {
710                         fprintf(stderr, "got %zu bytes, starting incr\n",
711                                 wm->source_data.size);
712                         wm->incr = 1;
713                         xcb_change_property(wm->conn,
714                                             XCB_PROP_MODE_REPLACE,
715                                             wm->selection_request.requestor,
716                                             wm->selection_request.property,
717                                             wm->atom.incr,
718                                             32, /* format */
719                                             1, &incr_chunk_size);
720                         wm->selection_property_set = 1;
721                         wm->flush_property_on_delete = 1;
722                         wl_event_source_remove(wm->property_source);
723                         weston_wm_send_selection_notify(wm, wm->selection_request.property);
724                 } else if (wm->selection_property_set) {
725                         fprintf(stderr, "got %zu bytes, waiting for "
726                                 "property delete\n", wm->source_data.size);
727
728                         wm->flush_property_on_delete = 1;
729                         wl_event_source_remove(wm->property_source);
730                 } else {
731                         fprintf(stderr, "got %zu bytes, "
732                                 "property deleted, seting new property\n",
733                                 wm->source_data.size);
734                         weston_wm_flush_source_data(wm);
735                 }
736         } else if (len == 0 && !wm->incr) {
737                 fprintf(stderr, "non-incr transfer complete\n");
738                 /* Non-incr transfer all done. */
739                 weston_wm_flush_source_data(wm);
740                 weston_wm_send_selection_notify(wm, wm->selection_request.property);
741                 xcb_flush(wm->conn);
742                 wl_event_source_remove(wm->property_source);
743                 close(fd);
744                 wl_array_release(&wm->source_data);
745                 wm->selection_request.requestor = XCB_NONE;
746         } else if (len == 0 && wm->incr) {
747                 fprintf(stderr, "incr transfer complete\n");
748
749                 wm->flush_property_on_delete = 1;
750                 if (wm->selection_property_set) {
751                         fprintf(stderr, "got %zu bytes, waiting for "
752                                 "property delete\n", wm->source_data.size);
753                 } else {
754                         fprintf(stderr, "got %zu bytes, "
755                                 "property deleted, seting new property\n",
756                                 wm->source_data.size);
757                         weston_wm_flush_source_data(wm);
758                 }
759                 xcb_flush(wm->conn);
760                 wl_event_source_remove(wm->property_source);
761                 wm->data_source_fd = -1;
762                 close(fd);
763         } else {
764                 fprintf(stderr, "nothing happened, buffered the bytes\n");
765         }
766
767         return 1;
768 }
769
770 static void
771 weston_wm_send_data(struct weston_wm *wm, xcb_atom_t target, const char *mime_type)
772 {
773         struct wl_input_device *device = wm->server->compositor->input_device;
774         int p[2];
775
776         if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) {
777                 fprintf(stderr, "pipe2 failed: %m\n");
778                 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
779                 return;
780         }
781
782         wl_array_init(&wm->source_data);
783         wm->selection_target = target;
784         wm->data_source_fd = p[0];
785         wm->property_source = wl_event_loop_add_fd(wm->server->loop,
786                                                    wm->data_source_fd,
787                                                    WL_EVENT_READABLE,
788                                                    weston_wm_read_data_source,
789                                                    wm);
790
791         wl_data_source_send_send(&device->selection_data_source->resource,
792                                  mime_type, p[1]);
793         close(p[1]);
794 }
795
796 static void
797 weston_wm_send_incr_chunk(struct weston_wm *wm)
798 {
799         fprintf(stderr, "property deleted\n");
800         int length;
801
802         wm->selection_property_set = 0;
803         if (wm->flush_property_on_delete) {
804                 fprintf(stderr, "setting new property, %zu bytes\n",
805                         wm->source_data.size);
806                 wm->flush_property_on_delete = 0;
807                 length = weston_wm_flush_source_data(wm);
808
809                 if (wm->data_source_fd >= 0) {
810                         wm->property_source =
811                                 wl_event_loop_add_fd(wm->server->loop,
812                                                      wm->data_source_fd,
813                                                      WL_EVENT_READABLE,
814                                                      weston_wm_read_data_source,
815                                                      wm);
816                 } else if (length > 0) {
817                         /* Transfer is all done, but queue a flush for
818                          * the delete of the last chunk so we can set
819                          * the 0 sized propert to signal the end of
820                          * the transfer. */
821                         wm->flush_property_on_delete = 1;
822                         wl_array_release(&wm->source_data);
823                 } else {
824                         wm->selection_request.requestor = XCB_NONE;
825                 }
826         }
827 }
828
829 static void
830 weston_wm_handle_selection_request(struct weston_wm *wm,
831                                  xcb_generic_event_t *event)
832 {
833         xcb_selection_request_event_t *selection_request =
834                 (xcb_selection_request_event_t *) event;
835
836         fprintf(stderr, "selection request, %s, ",
837                 get_atom_name(wm->conn, selection_request->selection));
838         fprintf(stderr, "target %s, ",
839                 get_atom_name(wm->conn, selection_request->target));
840         fprintf(stderr, "property %s\n",
841                 get_atom_name(wm->conn, selection_request->property));
842
843         wm->selection_request = *selection_request;
844         wm->incr = 0;
845         wm->flush_property_on_delete = 0;
846
847         if (selection_request->target == wm->atom.targets) {
848                 weston_wm_send_targets(wm);
849         } else if (selection_request->target == wm->atom.timestamp) {
850                 weston_wm_send_timestamp(wm);
851         } else if (selection_request->target == wm->atom.utf8_string ||
852                    selection_request->target == wm->atom.text) {
853                 weston_wm_send_data(wm, wm->atom.utf8_string,
854                                   "text/plain;charset=utf-8");
855         } else {
856                 fprintf(stderr, "can only handle UTF8_STRING targets...\n");
857                 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
858         }
859 }
860
861 static void
862 weston_wm_handle_property_notify(struct weston_wm *wm, xcb_generic_event_t *event)
863 {
864         xcb_property_notify_event_t *property_notify =
865                 (xcb_property_notify_event_t *) event;
866
867         if (property_notify->window == wm->selection_window) {
868                 if (property_notify->state == XCB_PROPERTY_NEW_VALUE &&
869                     property_notify->atom == wm->atom.wl_selection &&
870                     wm->incr)
871                         weston_wm_get_incr_chunk(wm);
872         } else if (property_notify->window == wm->selection_request.requestor) {
873                 if (property_notify->state == XCB_PROPERTY_DELETE &&
874                     property_notify->atom == wm->selection_request.property &&
875                     wm->incr)
876                         weston_wm_send_incr_chunk(wm);
877         } else if (property_notify->atom == XCB_ATOM_WM_CLASS) {
878                 fprintf(stderr, "wm_class changed\n");
879         } else if (property_notify->atom == XCB_ATOM_WM_TRANSIENT_FOR) {
880                 fprintf(stderr, "wm_transient_for changed\n");
881         } else if (property_notify->atom == wm->atom.wm_protocols) {
882                 fprintf(stderr, "wm_protocols changed\n");
883         } else if (property_notify->atom == wm->atom.net_wm_name) {
884                 fprintf(stderr, "_net_wm_name changed\n");
885         } else if (property_notify->atom == wm->atom.net_wm_user_time) {
886                 fprintf(stderr, "_net_wm_user_time changed\n");
887         } else if (property_notify->atom == wm->atom.net_wm_icon_name) {
888                 fprintf(stderr, "_net_wm_icon_name changed\n");
889         } else if (property_notify->atom == XCB_ATOM_WM_NAME) {
890                 fprintf(stderr, "wm_name changed\n");
891         } else if (property_notify->atom == XCB_ATOM_WM_ICON_NAME) {
892                 fprintf(stderr, "wm_icon_name changed\n");
893         } else {
894                 fprintf(stderr, "XCB_PROPERTY_NOTIFY: "
895                         "unhandled property change: %s\n",
896                         get_atom_name(wm->conn, property_notify->atom));
897         }
898 }
899
900 static void
901 weston_wm_handle_create_notify(struct weston_wm *wm, xcb_generic_event_t *event)
902 {
903         xcb_create_notify_event_t *create_notify =
904                 (xcb_create_notify_event_t *) event;
905         struct weston_wm_window *window;
906         uint32_t values[1];
907
908         fprintf(stderr, "XCB_CREATE_NOTIFY (window %d)\n",
909                 create_notify->window);
910
911         window = malloc(sizeof *window);
912         if (window == NULL) {
913                 fprintf(stderr, "failed to allocate window\n");
914                 return;
915         }
916
917         values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
918         xcb_change_window_attributes(wm->conn, create_notify->window,
919                                      XCB_CW_EVENT_MASK, values);
920
921         memset(window, 0, sizeof *window);
922         window->id = create_notify->window;
923         hash_table_insert(wm->window_hash, window->id, window);
924 }
925
926 static void
927 weston_wm_handle_destroy_notify(struct weston_wm *wm, xcb_generic_event_t *event)
928 {
929         xcb_destroy_notify_event_t *destroy_notify =
930                 (xcb_destroy_notify_event_t *) event;
931         struct weston_wm_window *window;
932
933         fprintf(stderr, "XCB_DESTROY_NOTIFY, win %d\n",
934                 destroy_notify->window);
935
936         window = hash_table_lookup(wm->window_hash, destroy_notify->window);
937         if (window == NULL) {
938                 fprintf(stderr, "destroy notify for unknow window %d\n",
939                         destroy_notify->window);
940                 return;
941         }
942
943         fprintf(stderr, "destroy window %p\n", window);
944         hash_table_remove(wm->window_hash, window->id);
945         if (window->surface)
946                 wl_list_remove(&window->surface_destroy_listener.link);
947         free(window);
948 }
949
950 static void
951 weston_wm_handle_selection_notify(struct weston_wm *wm,
952                                 xcb_generic_event_t *event)
953 {
954         xcb_selection_notify_event_t *selection_notify =
955                 (xcb_selection_notify_event_t *) event;
956
957         if (selection_notify->property == XCB_ATOM_NONE) {
958                 /* convert selection failed */
959         } else if (selection_notify->target == wm->atom.targets) {
960                 weston_wm_get_selection_targets(wm);
961         } else {
962                 weston_wm_get_selection_data(wm);
963         }
964 }
965
966 static void
967 weston_wm_handle_xfixes_selection_notify(struct weston_wm *wm,
968                                        xcb_generic_event_t *event)
969 {
970         xcb_xfixes_selection_notify_event_t *xfixes_selection_notify =
971                 (xcb_xfixes_selection_notify_event_t *) event;
972
973         printf("xfixes selection notify event: owner %d\n",
974                xfixes_selection_notify->owner);
975
976         /* We have to use XCB_TIME_CURRENT_TIME when we claim the
977          * selection, so grab the actual timestamp here so we can
978          * answer TIMESTAMP conversion requests correctly. */
979         if (xfixes_selection_notify->owner == wm->selection_window) {
980                 wm->selection_timestamp = xfixes_selection_notify->timestamp;
981                 fprintf(stderr, "our window, skipping\n");
982                 return;
983         }
984
985         wm->incr = 0;
986         xcb_convert_selection(wm->conn, wm->selection_window,
987                               wm->atom.clipboard,
988                               wm->atom.targets,
989                               wm->atom.wl_selection,
990                               xfixes_selection_notify->timestamp);
991
992         xcb_flush(wm->conn);
993 }
994
995 static void
996 weston_wm_handle_client_message(struct weston_wm *wm,
997                                 xcb_generic_event_t *event)
998 {
999         xcb_client_message_event_t *client_message =
1000                 (xcb_client_message_event_t *) event;
1001
1002         fprintf(stderr, "got client message, type: %s\n",
1003                 get_atom_name(wm->conn, client_message->type));
1004 }
1005
1006 static int
1007 weston_wm_handle_event(int fd, uint32_t mask, void *data)
1008 {
1009         struct weston_wm *wm = data;
1010         xcb_generic_event_t *event;
1011         int count = 0;
1012
1013         while (event = xcb_poll_for_event(wm->conn), event != NULL) {
1014                 switch (event->response_type & ~0x80) {
1015                 case XCB_CREATE_NOTIFY:
1016                         weston_wm_handle_create_notify(wm, event);
1017                         break;
1018                 case XCB_MAP_REQUEST:
1019                         weston_wm_handle_map_request(wm, event);
1020                         break;
1021                 case XCB_MAP_NOTIFY:
1022                         weston_wm_handle_map_notify(wm, event);
1023                         break;
1024                 case XCB_UNMAP_NOTIFY:
1025                         fprintf(stderr, "XCB_UNMAP_NOTIFY\n");
1026                         break;
1027                 case XCB_CONFIGURE_REQUEST:
1028                         weston_wm_handle_configure_request(wm, event);
1029                         break;
1030                 case XCB_CONFIGURE_NOTIFY:
1031                         weston_wm_handle_configure_notify(wm, event);
1032                         break;
1033                 case XCB_DESTROY_NOTIFY:
1034                         weston_wm_handle_destroy_notify(wm, event);
1035                         break;
1036                 case XCB_MAPPING_NOTIFY:
1037                         fprintf(stderr, "XCB_MAPPING_NOTIFY\n");
1038                         break;
1039                 case XCB_PROPERTY_NOTIFY:
1040                         weston_wm_handle_property_notify(wm, event);
1041                         break;
1042                 case XCB_SELECTION_NOTIFY:
1043                         weston_wm_handle_selection_notify(wm, event);
1044                         break;
1045                 case XCB_SELECTION_REQUEST:
1046                         weston_wm_handle_selection_request(wm, event);
1047                         break;
1048                 case XCB_CLIENT_MESSAGE:
1049                         weston_wm_handle_client_message(wm, event);
1050                         break;
1051                 }
1052
1053                 switch (event->response_type - wm->xfixes->first_event) {
1054                 case XCB_XFIXES_SELECTION_NOTIFY:
1055                         weston_wm_handle_xfixes_selection_notify(wm, event);
1056                         break;
1057                 }
1058
1059
1060                 free(event);
1061                 count++;
1062         }
1063
1064         xcb_flush(wm->conn);
1065
1066         return count;
1067 }
1068
1069 static void
1070 wxs_wm_get_resources(struct weston_wm *wm)
1071 {
1072
1073 #define F(field) offsetof(struct weston_wm, field)
1074
1075         static const struct { const char *name; int offset; } atoms[] = {
1076                 { "WM_PROTOCOLS",       F(atom.wm_protocols) },
1077                 { "WM_TAKE_FOCUS",      F(atom.wm_take_focus) },
1078                 { "WM_DELETE_WINDOW",   F(atom.wm_delete_window) },
1079                 { "_NET_WM_NAME",       F(atom.net_wm_name) },
1080                 { "_NET_WM_ICON",       F(atom.net_wm_icon) },
1081                 { "_NET_WM_STATE",      F(atom.net_wm_state) },
1082                 { "_NET_WM_STATE_FULLSCREEN", F(atom.net_wm_state_fullscreen) },
1083                 { "_NET_WM_USER_TIME", F(atom.net_wm_user_time) },
1084                 { "_NET_WM_ICON_NAME", F(atom.net_wm_icon_name) },
1085                 { "_NET_WM_WINDOW_TYPE", F(atom.net_wm_window_type) },
1086                 { "_NET_WM_MOVERESIZE", F(atom.net_wm_moveresize) },
1087                 { "_NET_SUPPORTING_WM_CHECK",
1088                                         F(atom.net_supporting_wm_check) },
1089                 { "_NET_SUPPORTED",     F(atom.net_supported) },
1090                 { "CLIPBOARD",          F(atom.clipboard) },
1091                 { "TARGETS",            F(atom.targets) },
1092                 { "UTF8_STRING",        F(atom.utf8_string) },
1093                 { "_WL_SELECTION",      F(atom.wl_selection) },
1094                 { "INCR",               F(atom.incr) },
1095                 { "TIMESTAMP",          F(atom.timestamp) },
1096                 { "MULTIPLE",           F(atom.multiple) },
1097                 { "UTF8_STRING" ,       F(atom.utf8_string) },
1098                 { "COMPOUND_TEXT",      F(atom.compound_text) },
1099                 { "TEXT",               F(atom.text) },
1100                 { "STRING",             F(atom.string) },
1101                 { "text/plain;charset=utf-8",   F(atom.text_plain_utf8) },
1102                 { "text/plain",         F(atom.text_plain) },
1103         };
1104 #undef F
1105
1106         xcb_xfixes_query_version_cookie_t xfixes_cookie;
1107         xcb_xfixes_query_version_reply_t *xfixes_reply;
1108         xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
1109         xcb_intern_atom_reply_t *reply;
1110         uint32_t i;
1111
1112         xcb_prefetch_extension_data (wm->conn, &xcb_xfixes_id);
1113
1114         for (i = 0; i < ARRAY_LENGTH(atoms); i++)
1115                 cookies[i] = xcb_intern_atom (wm->conn, 0,
1116                                               strlen(atoms[i].name),
1117                                               atoms[i].name);
1118
1119         for (i = 0; i < ARRAY_LENGTH(atoms); i++) {
1120                 reply = xcb_intern_atom_reply (wm->conn, cookies[i], NULL);
1121                 *(xcb_atom_t *) ((char *) wm + atoms[i].offset) = reply->atom;
1122                 free(reply);
1123         }
1124
1125         wm->xfixes = xcb_get_extension_data(wm->conn, &xcb_xfixes_id);
1126         if (!wm->xfixes || !wm->xfixes->present)
1127                 fprintf(stderr, "xfixes not available\n");
1128
1129         xfixes_cookie = xcb_xfixes_query_version(wm->conn,
1130                                                  XCB_XFIXES_MAJOR_VERSION,
1131                                                  XCB_XFIXES_MINOR_VERSION);
1132         xfixes_reply = xcb_xfixes_query_version_reply(wm->conn,
1133                                                       xfixes_cookie, NULL);
1134
1135         printf("xfixes version: %d.%d\n",
1136                xfixes_reply->major_version, xfixes_reply->minor_version);
1137
1138         free(xfixes_reply);
1139 }
1140
1141 static void
1142 weston_wm_create_wm_window(struct weston_wm *wm)
1143 {
1144         static const char name[] = "Weston WM";
1145
1146         wm->wm_window = xcb_generate_id(wm->conn);
1147         xcb_create_window(wm->conn,
1148                           XCB_COPY_FROM_PARENT,
1149                           wm->wm_window,
1150                           wm->screen->root,
1151                           0, 0,
1152                           10, 10,
1153                           0,
1154                           XCB_WINDOW_CLASS_INPUT_OUTPUT,
1155                           wm->screen->root_visual,
1156                           0, NULL);
1157
1158         xcb_change_property(wm->conn,
1159                             XCB_PROP_MODE_REPLACE,
1160                             wm->wm_window,
1161                             wm->atom.net_supporting_wm_check,
1162                             XCB_ATOM_WINDOW,
1163                             32, /* format */
1164                             1, &wm->wm_window);
1165
1166         xcb_change_property(wm->conn,
1167                             XCB_PROP_MODE_REPLACE,
1168                             wm->wm_window,
1169                             wm->atom.net_wm_name,
1170                             wm->atom.utf8_string,
1171                             8, /* format */
1172                             strlen(name), name);
1173
1174         xcb_change_property(wm->conn,
1175                             XCB_PROP_MODE_REPLACE,
1176                             wm->screen->root,
1177                             wm->atom.net_supporting_wm_check,
1178                             XCB_ATOM_WINDOW,
1179                             32, /* format */
1180                             1, &wm->wm_window);
1181
1182 }
1183
1184 static struct weston_wm *
1185 weston_wm_create(struct weston_xserver *wxs)
1186 {
1187         struct wl_input_device *device;
1188         struct weston_wm *wm;
1189         struct wl_event_loop *loop;
1190         xcb_screen_iterator_t s;
1191         uint32_t values[1], mask;
1192         int sv[2];
1193         xcb_atom_t supported[1];
1194
1195         wm = malloc(sizeof *wm);
1196         if (wm == NULL)
1197                 return NULL;
1198
1199         wm->server = wxs;
1200         wm->window_hash = hash_table_create();
1201         if (wm->window_hash == NULL) {
1202                 free(wm);
1203                 return NULL;
1204         }
1205
1206         if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
1207                 fprintf(stderr, "socketpair failed\n");
1208                 hash_table_destroy(wm->window_hash);
1209                 free(wm);
1210                 return NULL;
1211         }
1212
1213         xserver_send_client(wxs->resource, sv[1]);
1214         wl_client_flush(wxs->resource->client);
1215         close(sv[1]);
1216         
1217         /* xcb_connect_to_fd takes ownership of the fd. */
1218         wm->conn = xcb_connect_to_fd(sv[0], NULL);
1219         if (xcb_connection_has_error(wm->conn)) {
1220                 fprintf(stderr, "xcb_connect_to_fd failed\n");
1221                 close(sv[0]);
1222                 hash_table_destroy(wm->window_hash);
1223                 free(wm);
1224                 return NULL;
1225         }
1226
1227         s = xcb_setup_roots_iterator(xcb_get_setup(wm->conn));
1228         wm->screen = s.data;
1229
1230         loop = wl_display_get_event_loop(wxs->wl_display);
1231         wm->source =
1232                 wl_event_loop_add_fd(loop, sv[0],
1233                                      WL_EVENT_READABLE,
1234                                      weston_wm_handle_event, wm);
1235         wl_event_source_check(wm->source);
1236
1237         wxs_wm_get_resources(wm);
1238
1239         values[0] =
1240                 XCB_EVENT_MASK_STRUCTURE_NOTIFY |
1241                 XCB_EVENT_MASK_RESIZE_REDIRECT |
1242                 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
1243                 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
1244                 XCB_EVENT_MASK_PROPERTY_CHANGE;
1245         xcb_change_window_attributes(wm->conn, wm->screen->root,
1246                                      XCB_CW_EVENT_MASK, values);
1247
1248         weston_wm_create_wm_window(wm);
1249
1250         supported[0] = wm->atom.net_wm_moveresize;
1251         xcb_change_property(wm->conn,
1252                             XCB_PROP_MODE_REPLACE,
1253                             wm->screen->root,
1254                             wm->atom.net_supported,
1255                             XCB_ATOM_ATOM,
1256                             32, /* format */
1257                             ARRAY_LENGTH(supported), supported);
1258
1259         wm->selection_request.requestor = XCB_NONE;
1260
1261         wm->selection_window = xcb_generate_id(wm->conn);
1262         xcb_create_window(wm->conn,
1263                           XCB_COPY_FROM_PARENT,
1264                           wm->selection_window,
1265                           wm->screen->root,
1266                           0, 0,
1267                           10, 10,
1268                           0,
1269                           XCB_WINDOW_CLASS_INPUT_OUTPUT,
1270                           wm->screen->root_visual,
1271                           XCB_CW_EVENT_MASK, values);
1272
1273         mask =
1274                 XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
1275                 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
1276                 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
1277
1278         xcb_xfixes_select_selection_input(wm->conn, wm->selection_window,
1279                                           wm->atom.clipboard, mask);
1280
1281         xcb_flush(wm->conn);
1282
1283         device = wxs->compositor->input_device;
1284         wm->selection_listener.notify = weston_wm_set_selection;
1285         wl_signal_add(&device->selection_signal, &wm->selection_listener);
1286
1287         fprintf(stderr, "created wm\n");
1288
1289         return wm;
1290 }
1291
1292 static void
1293 weston_wm_destroy(struct weston_wm *wm)
1294 {
1295         /* FIXME: Free windows in hash. */
1296         hash_table_destroy(wm->window_hash);
1297         xcb_disconnect(wm->conn);
1298         wl_event_source_remove(wm->source);
1299         wl_list_remove(&wm->selection_listener.link);
1300         free(wm);
1301 }
1302
1303 static int
1304 weston_xserver_handle_event(int listen_fd, uint32_t mask, void *data)
1305 {
1306         struct weston_xserver *mxs = data;
1307         char display[8], s[8];
1308         int sv[2], client_fd;
1309
1310         if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
1311                 fprintf(stderr, "socketpair failed\n");
1312                 return 1;
1313         }
1314
1315         mxs->process.pid = fork();
1316         switch (mxs->process.pid) {
1317         case 0:
1318                 /* SOCK_CLOEXEC closes both ends, so we need to unset
1319                  * the flag on the client fd. */
1320                 client_fd = dup(sv[1]);
1321                 if (client_fd < 0)
1322                         return 1;
1323
1324                 snprintf(s, sizeof s, "%d", client_fd);
1325                 setenv("WAYLAND_SOCKET", s, 1);
1326
1327                 snprintf(display, sizeof display, ":%d", mxs->display);
1328
1329                 if (execl(XSERVER_PATH,
1330                           XSERVER_PATH,
1331                           display,
1332                           "-wayland",
1333                           "-rootless",
1334                           "-retro",
1335                           "-nolisten", "all",
1336                           "-terminate",
1337                           NULL) < 0)
1338                         fprintf(stderr, "exec failed: %m\n");
1339                 exit(-1);
1340
1341         default:
1342                 fprintf(stderr, "forked X server, pid %d\n", mxs->process.pid);
1343
1344                 close(sv[1]);
1345                 mxs->client = wl_client_create(mxs->wl_display, sv[0]);
1346
1347                 weston_watch_process(&mxs->process);
1348
1349                 wl_event_source_remove(mxs->abstract_source);
1350                 wl_event_source_remove(mxs->unix_source);
1351                 break;
1352
1353         case -1:
1354                 fprintf(stderr, "failed to fork\n");
1355                 break;
1356         }
1357
1358         return 1;
1359 }
1360
1361 static void
1362 weston_xserver_shutdown(struct weston_xserver *wxs)
1363 {
1364         char path[256];
1365
1366         snprintf(path, sizeof path, "/tmp/.X%d-lock", wxs->display);
1367         unlink(path);
1368         snprintf(path, sizeof path, "/tmp/.X11-unix/X%d", wxs->display);
1369         unlink(path);
1370         if (wxs->process.pid == 0) {
1371                 wl_event_source_remove(wxs->abstract_source);
1372                 wl_event_source_remove(wxs->unix_source);
1373         }
1374         close(wxs->abstract_fd);
1375         close(wxs->unix_fd);
1376         if (wxs->wm)
1377                 weston_wm_destroy(wxs->wm);
1378         wxs->loop = NULL;
1379 }
1380
1381 static void
1382 weston_xserver_cleanup(struct weston_process *process, int status)
1383 {
1384         struct weston_xserver *mxs =
1385                 container_of(process, struct weston_xserver, process);
1386
1387         mxs->process.pid = 0;
1388         mxs->client = NULL;
1389         mxs->resource = NULL;
1390
1391         mxs->abstract_source =
1392                 wl_event_loop_add_fd(mxs->loop, mxs->abstract_fd,
1393                                      WL_EVENT_READABLE,
1394                                      weston_xserver_handle_event, mxs);
1395
1396         mxs->unix_source =
1397                 wl_event_loop_add_fd(mxs->loop, mxs->unix_fd,
1398                                      WL_EVENT_READABLE,
1399                                      weston_xserver_handle_event, mxs);
1400
1401         if (mxs->wm) {
1402                 fprintf(stderr, "xserver exited, code %d\n", status);
1403                 weston_wm_destroy(mxs->wm);
1404                 mxs->wm = NULL;
1405         } else {
1406                 /* If the X server crashes before it binds to the
1407                  * xserver interface, shut down and don't try
1408                  * again. */
1409                 fprintf(stderr, "xserver crashing too fast: %d\n", status);
1410                 weston_xserver_shutdown(mxs);
1411         }
1412 }
1413
1414 static void
1415 surface_destroy(struct wl_listener *listener, void *data)
1416 {
1417         struct weston_wm_window *window =
1418                 container_of(listener,
1419                              struct weston_wm_window, surface_destroy_listener);
1420
1421         fprintf(stderr, "surface for xid %d destroyed\n", window->id);
1422 }
1423
1424 static struct weston_wm_window *
1425 get_wm_window(struct weston_surface *surface)
1426 {
1427         struct wl_resource *resource = &surface->surface.resource;
1428         struct wl_listener *listener;
1429
1430         listener = wl_signal_get(&resource->destroy_signal, surface_destroy);
1431         if (listener)
1432                 return container_of(listener, struct weston_wm_window,
1433                                     surface_destroy_listener);
1434
1435         return NULL;
1436 }
1437
1438 /* We reuse some predefined, but otherwise useles atoms */
1439 #define TYPE_WM_PROTOCOLS XCB_ATOM_CUT_BUFFER0
1440
1441 static void
1442 read_window_properties(struct weston_wm *wm, struct weston_wm_window *window)
1443 {
1444 #define F(field) offsetof(struct weston_wm_window, field)
1445         const struct {
1446                 xcb_atom_t atom;
1447                 xcb_atom_t type;
1448                 int offset;
1449         } props[] = {
1450                 { XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, F(class) },
1451                 { XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, F(transient_for) },
1452                 { wm->atom.wm_protocols, TYPE_WM_PROTOCOLS, F(protocols) },
1453                 { wm->atom.net_wm_window_type, XCB_ATOM_ATOM, F(type) },
1454                 { wm->atom.net_wm_name, XCB_ATOM_STRING, F(name) },
1455         };
1456 #undef F
1457
1458         xcb_get_property_cookie_t cookie[ARRAY_LENGTH(props)];
1459         xcb_get_property_reply_t *reply;
1460         void *p;
1461         uint32_t *xid;
1462         xcb_atom_t *atom;
1463         uint32_t i;
1464
1465         dump_window_properties(wm, window->id);
1466
1467         for (i = 0; i < ARRAY_LENGTH(props); i++)
1468                 cookie[i] = xcb_get_property(wm->conn,
1469                                              0, /* delete */
1470                                              window->id,
1471                                              props[i].atom,
1472                                              XCB_ATOM_ANY, 0, 2048);
1473
1474         for (i = 0; i < ARRAY_LENGTH(props); i++)  {
1475                 reply = xcb_get_property_reply(wm->conn, cookie[i], NULL);
1476                 if (!reply)
1477                         /* Bad window, typically */
1478                         continue;
1479                 if (reply->type == XCB_ATOM_NONE) {
1480                         /* No such property */
1481                         free(reply);
1482                         continue;
1483                 }
1484
1485                 p = ((char *) window + props[i].offset);
1486
1487                 switch (props[i].type) {
1488                 case XCB_ATOM_STRING:
1489                         /* FIXME: We're using this for both string and
1490                            utf8_string */
1491                         *(char **) p =
1492                                 strndup(xcb_get_property_value(reply),
1493                                         xcb_get_property_value_length(reply));
1494                         break;
1495                 case XCB_ATOM_WINDOW:
1496                         xid = xcb_get_property_value(reply);
1497                         *(struct weston_wm_window **) p =
1498                                 hash_table_lookup(wm->window_hash, *xid);
1499                         break;
1500                 case XCB_ATOM_ATOM:
1501                         atom = xcb_get_property_value(reply);
1502                         *(xcb_atom_t *) p = *atom;
1503                         break;
1504                 case TYPE_WM_PROTOCOLS:
1505                         break;
1506                 default:
1507                         break;
1508                 }
1509                 free(reply);
1510         }
1511
1512         fprintf(stderr, "window %d: name %s, class %s, transient_for %d\n",
1513                 window->id, window->name, window->class,
1514                 window->transient_for ? window->transient_for->id : 0);
1515 }
1516
1517 static void
1518 xserver_set_window_id(struct wl_client *client, struct wl_resource *resource,
1519                       struct wl_resource *surface_resource, uint32_t id)
1520 {
1521         struct weston_xserver *wxs = resource->data;
1522         struct weston_wm *wm = wxs->wm;
1523         struct wl_surface *surface = surface_resource->data;
1524         struct weston_wm_window *window;
1525         struct weston_shell_interface *shell_interface =
1526                 &wm->server->compositor->shell_interface;
1527
1528         if (client != wxs->client)
1529                 return;
1530
1531         window = hash_table_lookup(wm->window_hash, id);
1532         if (window == NULL) {
1533                 fprintf(stderr, "set_window_id for unknown window %d\n", id);
1534                 return;
1535         }
1536
1537         fprintf(stderr, "set_window_id %d for surface %p\n", id, surface);
1538
1539         read_window_properties(wm, window);
1540
1541         window->surface = (struct weston_surface *) surface;
1542         window->surface_destroy_listener.notify = surface_destroy;
1543         wl_signal_add(&surface->resource.destroy_signal,
1544                       &window->surface_destroy_listener);
1545
1546         if (shell_interface->create_shell_surface) {
1547                 shell_interface->create_shell_surface(shell_interface->shell,
1548                                                       window->surface,
1549                                                       &window->shsurf);
1550
1551                 shell_interface->set_toplevel(window->shsurf);
1552         }
1553 }
1554
1555 static const struct xserver_interface xserver_implementation = {
1556         xserver_set_window_id
1557 };
1558
1559 static void
1560 bind_xserver(struct wl_client *client,
1561              void *data, uint32_t version, uint32_t id)
1562 {
1563         struct weston_xserver *wxs = data;
1564
1565         /* If it's a different client than the xserver we launched,
1566          * don't start the wm. */
1567         if (client != wxs->client)
1568                 return;
1569
1570         wxs->resource = 
1571                 wl_client_add_object(client, &xserver_interface,
1572                                      &xserver_implementation, id, wxs);
1573
1574         wxs->wm = weston_wm_create(wxs);
1575         if (wxs->wm == NULL) {
1576                 fprintf(stderr, "failed to create wm\n");
1577         }
1578
1579         xserver_send_listen_socket(wxs->resource, wxs->abstract_fd);
1580         xserver_send_listen_socket(wxs->resource, wxs->unix_fd);
1581 }
1582
1583 static int
1584 bind_to_abstract_socket(int display)
1585 {
1586         struct sockaddr_un addr;
1587         socklen_t size, name_size;
1588         int fd;
1589
1590         fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
1591         if (fd < 0)
1592                 return -1;
1593
1594         addr.sun_family = AF_LOCAL;
1595         name_size = snprintf(addr.sun_path, sizeof addr.sun_path,
1596                              "%c/tmp/.X11-unix/X%d", 0, display);
1597         size = offsetof(struct sockaddr_un, sun_path) + name_size;
1598         if (bind(fd, (struct sockaddr *) &addr, size) < 0) {
1599                 fprintf(stderr, "failed to bind to @%s: %s\n",
1600                         addr.sun_path + 1, strerror(errno));
1601                 close(fd);
1602                 return -1;
1603         }
1604
1605         if (listen(fd, 1) < 0) {
1606                 close(fd);
1607                 return -1;
1608         }
1609
1610         return fd;
1611 }
1612
1613 static int
1614 bind_to_unix_socket(int display)
1615 {
1616         struct sockaddr_un addr;
1617         socklen_t size, name_size;
1618         int fd;
1619
1620         fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
1621         if (fd < 0)
1622                 return -1;
1623
1624         addr.sun_family = AF_LOCAL;
1625         name_size = snprintf(addr.sun_path, sizeof addr.sun_path,
1626                              "/tmp/.X11-unix/X%d", display) + 1;
1627         size = offsetof(struct sockaddr_un, sun_path) + name_size;
1628         unlink(addr.sun_path);
1629         if (bind(fd, (struct sockaddr *) &addr, size) < 0) {
1630                 fprintf(stderr, "failed to bind to %s (%s)\n",
1631                         addr.sun_path, strerror(errno));
1632                 close(fd);
1633                 return -1;
1634         }
1635
1636         if (listen(fd, 1) < 0) {
1637                 unlink(addr.sun_path);
1638                 close(fd);
1639                 return -1;
1640         }
1641
1642         return fd;
1643 }
1644
1645 static int
1646 create_lockfile(int display, char *lockfile, size_t lsize)
1647 {
1648         char pid[16], *end;
1649         int fd, size;
1650         pid_t other;
1651
1652         snprintf(lockfile, lsize, "/tmp/.X%d-lock", display);
1653         fd = open(lockfile, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0444);
1654         if (fd < 0 && errno == EEXIST) {
1655                 fd = open(lockfile, O_CLOEXEC, O_RDONLY);
1656                 if (fd < 0 || read(fd, pid, 11) != 11) {
1657                         fprintf(stderr, "can't read lock file %s: %s\n",
1658                                 lockfile, strerror(errno));
1659                         errno = EEXIST;
1660                         return -1;
1661                 }
1662
1663                 other = strtol(pid, &end, 0);
1664                 if (end != pid + 10) {
1665                         fprintf(stderr, "can't parse lock file %s\n",
1666                                 lockfile);
1667                         close(fd);
1668                         errno = EEXIST;
1669                         return -1;
1670                 }
1671
1672                 if (kill(other, 0) < 0 && errno == ESRCH) {
1673                         /* stale lock file; unlink and try again */
1674                         fprintf(stderr,
1675                                 "unlinking stale lock file %s\n", lockfile);
1676                         close(fd);
1677                         if (unlink(lockfile))
1678                                 /* If we fail to unlink, return EEXIST
1679                                    so we try the next display number.*/
1680                                 errno = EEXIST;
1681                         else
1682                                 errno = EAGAIN;
1683                         return -1;
1684                 }
1685
1686                 errno = EEXIST;
1687                 return -1;
1688         } else if (fd < 0) {
1689                 fprintf(stderr, "failed to create lock file %s: %s\n",
1690                         lockfile, strerror(errno));
1691                 return -1;
1692         }
1693
1694         /* Subtle detail: we use the pid of the wayland
1695          * compositor, not the xserver in the lock file. */
1696         size = snprintf(pid, sizeof pid, "%10d\n", getpid());
1697         if (write(fd, pid, size) != size) {
1698                 unlink(lockfile);
1699                 close(fd);
1700                 return -1;
1701         }
1702
1703         close(fd);
1704
1705         return 0;
1706 }
1707
1708 static void
1709 weston_xserver_destroy(struct wl_listener *l, void *data)
1710 {
1711         struct weston_xserver *wxs =
1712                 container_of(l, struct weston_xserver, destroy_listener);
1713
1714         if (!wxs)
1715                 return;
1716
1717         if (wxs->loop)
1718                 weston_xserver_shutdown(wxs);
1719
1720         free(wxs);
1721 }
1722
1723 WL_EXPORT int
1724 weston_xserver_init(struct weston_compositor *compositor)
1725 {
1726         struct wl_display *display = compositor->wl_display;
1727         struct weston_xserver *mxs;
1728         char lockfile[256], display_name[8];
1729
1730         mxs = malloc(sizeof *mxs);
1731         memset(mxs, 0, sizeof *mxs);
1732
1733         mxs->process.cleanup = weston_xserver_cleanup;
1734         mxs->wl_display = display;
1735         mxs->compositor = compositor;
1736
1737         mxs->display = 0;
1738
1739  retry:
1740         if (create_lockfile(mxs->display, lockfile, sizeof lockfile) < 0) {
1741                 if (errno == EAGAIN) {
1742                         goto retry;
1743                 } else if (errno == EEXIST) {
1744                         mxs->display++;
1745                         goto retry;
1746                 } else {
1747                         free(mxs);
1748                         return -1;
1749                 }
1750         }                               
1751
1752         mxs->abstract_fd = bind_to_abstract_socket(mxs->display);
1753         if (mxs->abstract_fd < 0 && errno == EADDRINUSE) {
1754                 mxs->display++;
1755                 unlink(lockfile);
1756                 goto retry;
1757         }
1758
1759         mxs->unix_fd = bind_to_unix_socket(mxs->display);
1760         if (mxs->unix_fd < 0) {
1761                 unlink(lockfile);
1762                 close(mxs->abstract_fd);
1763                 free(mxs);
1764                 return -1;
1765         }
1766
1767         snprintf(display_name, sizeof display_name, ":%d", mxs->display);
1768         fprintf(stderr, "xserver listening on display %s\n", display_name);
1769         setenv("DISPLAY", display_name, 1);
1770
1771         mxs->loop = wl_display_get_event_loop(display);
1772         mxs->abstract_source =
1773                 wl_event_loop_add_fd(mxs->loop, mxs->abstract_fd,
1774                                      WL_EVENT_READABLE,
1775                                      weston_xserver_handle_event, mxs);
1776         mxs->unix_source =
1777                 wl_event_loop_add_fd(mxs->loop, mxs->unix_fd,
1778                                      WL_EVENT_READABLE,
1779                                      weston_xserver_handle_event, mxs);
1780
1781         wl_display_add_global(display, &xserver_interface, mxs, bind_xserver);
1782
1783         mxs->destroy_listener.notify = weston_xserver_destroy;
1784         wl_signal_add(&compositor->destroy_signal, &mxs->destroy_listener);
1785
1786
1787         mxs->activate_listener.notify = weston_xserver_surface_activate;
1788         wl_signal_add(&compositor->activate_signal, &mxs->activate_listener);
1789
1790         return 0;
1791 }