uploaded spice-vdagent
[platform/adaptation/emulator/spice-vdagent.git] / src / vdagent-x11.c
1 /*  vdagent-x11.c vdagent x11 code
2
3     Copyright 2010-2011 Red Hat, Inc.
4
5     Red Hat Authors:
6     Hans de Goede <hdegoede@redhat.com>
7
8     This program is free software: you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation, either version 3 of the License, or
11     (at your option) any later version.
12
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17
18     You should have received a copy of the GNU General Public License
19     along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /* Note: Our event loop is only called when there is data to be read from the
23    X11 socket. If events have arrived and have already been read by libX11 from
24    the socket triggered by other libX11 calls from this file, the select for
25    read in the main loop, won't see these and our event loop won't get called!
26    
27    Thus we must make sure that all queued events have been consumed, whenever
28    we return to the main loop. IOW all (externally callable) functions in this
29    file must end with calling XPending and consuming all queued events.
30    
31    Calling XPending when-ever we return to the mainloop also ensures any
32    pending writes are flushed. */
33
34 #include <glib.h>
35 #include <stdlib.h>
36 #include <limits.h>
37 #include <string.h>
38 #include <syslog.h>
39 #include <assert.h>
40 #include <unistd.h>
41 #include <X11/Xatom.h>
42 #include <X11/Xlib.h>
43 #include <X11/extensions/Xfixes.h>
44 #include "vdagentd-proto.h"
45 #include "vdagent-x11.h"
46 #include "vdagent-x11-priv.h"
47
48 /* Stupid X11 API, there goes our encapsulate all data in a struct design */
49 int (*vdagent_x11_prev_error_handler)(Display *, XErrorEvent *);
50 int vdagent_x11_caught_error;
51
52 static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
53                                                 XEvent *event, int incr);
54 static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11);
55 static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
56                                               XEvent *event);
57 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11,
58                                                       XEvent *del_event);
59 static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
60                 Atom prop, struct vdagent_x11_selection_request *request);
61 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11,
62                                             uint8_t selection, int new_owner);
63
64 static const char *vdagent_x11_sel_to_str(uint8_t selection) {
65     switch (selection) {
66     case VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD:
67         return "clipboard";
68     case VD_AGENT_CLIPBOARD_SELECTION_PRIMARY:
69         return "primary";
70     case VD_AGENT_CLIPBOARD_SELECTION_SECONDARY:
71         return "secondary";
72     default:
73         return "unknown";
74     }
75 }
76
77 static int vdagent_x11_debug_error_handler(
78     Display *display, XErrorEvent *error)
79 {
80     abort();
81 }
82
83 /* With the clipboard we're sometimes dealing with Properties on another apps
84    Window. which can go away at any time. */
85 static int vdagent_x11_ignore_bad_window_handler(
86     Display *display, XErrorEvent *error)
87 {
88     if (error->error_code == BadWindow)
89         return 0;
90
91     return vdagent_x11_prev_error_handler(display, error);
92 }
93
94 void vdagent_x11_set_error_handler(struct vdagent_x11 *x11,
95     int (*handler)(Display *, XErrorEvent *))
96 {
97     XSync(x11->display, False);
98     vdagent_x11_caught_error = 0;
99     vdagent_x11_prev_error_handler = XSetErrorHandler(handler);
100 }
101
102 int vdagent_x11_restore_error_handler(struct vdagent_x11 *x11)
103 {
104     int error;
105
106     XSync(x11->display, False);
107     XSetErrorHandler(vdagent_x11_prev_error_handler);
108     error = vdagent_x11_caught_error;
109     vdagent_x11_caught_error = 0;
110
111     return error;
112 }
113
114 static void vdagent_x11_get_wm_name(struct vdagent_x11 *x11)
115 {
116     Atom type_ret;
117     int format_ret;
118     unsigned long len, remain;
119     unsigned char *data = NULL;
120     Window sup_window = None;
121
122     /* XGetWindowProperty can throw a BadWindow error. One way we can trigger
123        this is when the display-manager (ie gdm) has set, and not cleared the
124        _NET_SUPPORTING_WM_CHECK property, and the window manager running in
125        the user session has not yet updated it to point to its window, so its
126        pointing to a non existing window. */
127     vdagent_x11_set_error_handler(x11, vdagent_x11_ignore_bad_window_handler);
128
129     /* Get the window manager SUPPORTING_WM_CHECK window */
130     if (XGetWindowProperty(x11->display, x11->root_window[0],
131             XInternAtom(x11->display, "_NET_SUPPORTING_WM_CHECK", False), 0,
132             LONG_MAX, False, XA_WINDOW, &type_ret, &format_ret, &len,
133             &remain, &data) == Success) {
134         if (type_ret == XA_WINDOW)
135             sup_window = *((Window *)data);
136         XFree(data);
137     }
138     if (sup_window == None &&
139         XGetWindowProperty(x11->display, x11->root_window[0],
140             XInternAtom(x11->display, "_WIN_SUPPORTING_WM_CHECK", False), 0,
141             LONG_MAX, False, XA_CARDINAL, &type_ret, &format_ret, &len,
142             &remain, &data) == Success) {
143         if (type_ret == XA_CARDINAL)
144             sup_window = *((Window *)data);
145         XFree(data);
146     }
147     /* So that we can get the net_wm_name */
148     if (sup_window != None) {
149         Atom utf8 = XInternAtom(x11->display, "UTF8_STRING", False);
150         if (XGetWindowProperty(x11->display, sup_window,
151                 XInternAtom(x11->display, "_NET_WM_NAME", False), 0,
152                 LONG_MAX, False, utf8, &type_ret, &format_ret, &len,
153                 &remain, &data) == Success) {
154             if (type_ret == utf8) {
155                 x11->net_wm_name =
156                     g_strndup((char *)data, (format_ret / 8) * len);
157             }
158             XFree(data);
159         }
160         if (x11->net_wm_name == NULL &&
161             XGetWindowProperty(x11->display, sup_window,
162                 XInternAtom(x11->display, "_NET_WM_NAME", False), 0,
163                 LONG_MAX, False, XA_STRING, &type_ret, &format_ret, &len,
164                 &remain, &data) == Success) {
165             if (type_ret == XA_STRING) {
166                 x11->net_wm_name =
167                     g_strndup((char *)data, (format_ret / 8) * len);
168             }
169             XFree(data);
170         }
171     }
172
173     vdagent_x11_restore_error_handler(x11);
174 }
175
176 struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd,
177     int debug, int sync)
178 {
179     struct vdagent_x11 *x11;
180     XWindowAttributes attrib;
181     int i, j, major, minor;
182
183     x11 = calloc(1, sizeof(*x11));
184     if (!x11) {
185         syslog(LOG_ERR, "out of memory allocating vdagent_x11 struct");
186         return NULL;
187     }
188
189     x11->vdagentd = vdagentd;
190     x11->debug = debug;
191
192     x11->display = XOpenDisplay(NULL);
193     if (!x11->display) {
194         syslog(LOG_ERR, "could not connect to X-server");
195         free(x11);
196         return NULL;
197     }
198
199     x11->screen_count = ScreenCount(x11->display);
200     if (x11->screen_count > MAX_SCREENS) {
201         syslog(LOG_ERR, "Error too much screens: %d > %d",
202                x11->screen_count, MAX_SCREENS);
203         XCloseDisplay(x11->display);
204         free(x11);
205         return NULL;
206     }
207
208     if (sync) {
209         XSetErrorHandler(vdagent_x11_debug_error_handler);
210         XSynchronize(x11->display, True);
211     }
212
213     for (i = 0; i < x11->screen_count; i++)
214         x11->root_window[i] = RootWindow(x11->display, i);
215     x11->fd = ConnectionNumber(x11->display);
216     x11->clipboard_atom = XInternAtom(x11->display, "CLIPBOARD", False);
217     x11->clipboard_primary_atom = XInternAtom(x11->display, "PRIMARY", False);
218     x11->targets_atom = XInternAtom(x11->display, "TARGETS", False);
219     x11->incr_atom = XInternAtom(x11->display, "INCR", False);
220     x11->multiple_atom = XInternAtom(x11->display, "MULTIPLE", False);
221     for(i = 0; i < clipboard_format_count; i++) {
222         x11->clipboard_formats[i].type = clipboard_format_templates[i].type;
223         for(j = 0; clipboard_format_templates[i].atom_names[j]; j++) {
224             x11->clipboard_formats[i].atoms[j] =
225                 XInternAtom(x11->display,
226                             clipboard_format_templates[i].atom_names[j],
227                             False);
228         }
229         x11->clipboard_formats[i].atom_count = j;
230     }
231
232     /* We should not store properties (for selections) on the root window */
233     x11->selection_window = XCreateSimpleWindow(x11->display, x11->root_window[0],
234                                                 0, 0, 1, 1, 0, 0, 0);
235     if (x11->debug)
236         syslog(LOG_DEBUG, "Selection window: %u", (int)x11->selection_window);
237
238     vdagent_x11_randr_init(x11);
239
240     if (XFixesQueryExtension(x11->display, &x11->xfixes_event_base, &i) &&
241         XFixesQueryVersion(x11->display, &major, &minor) && major >= 1) {
242         x11->has_xfixes = 1;
243         XFixesSelectSelectionInput(x11->display, x11->root_window[0],
244                                    x11->clipboard_atom,
245                                    XFixesSetSelectionOwnerNotifyMask|
246                                    XFixesSelectionWindowDestroyNotifyMask|
247                                    XFixesSelectionClientCloseNotifyMask);
248         XFixesSelectSelectionInput(x11->display, x11->root_window[0],
249                                    x11->clipboard_primary_atom,
250                                    XFixesSetSelectionOwnerNotifyMask|
251                                    XFixesSelectionWindowDestroyNotifyMask|
252                                    XFixesSelectionClientCloseNotifyMask);
253     } else
254         syslog(LOG_ERR, "no xfixes, no guest -> client copy paste support");
255
256     x11->max_prop_size = XExtendedMaxRequestSize(x11->display);
257     if (x11->max_prop_size) {
258         x11->max_prop_size -= 100;
259     } else {
260         x11->max_prop_size = XMaxRequestSize(x11->display) - 100;
261     }
262     /* Be a good X11 citizen and maximize the amount of data we send at once */
263     if (x11->max_prop_size > 262144)
264         x11->max_prop_size = 262144;
265
266     for (i = 0; i < x11->screen_count; i++) {
267         /* Catch resolution changes */
268         XSelectInput(x11->display, x11->root_window[i], StructureNotifyMask);
269
270         /* Get the current resolution */
271         XGetWindowAttributes(x11->display, x11->root_window[i], &attrib);
272         x11->width[i]  = attrib.width;
273         x11->height[i] = attrib.height;
274     }
275     vdagent_x11_send_daemon_guest_xorg_res(x11, 1);
276
277     /* Get net_wm_name, since we are started at the same time as the wm,
278        sometimes we need to wait a bit for it to show up. */
279     i = 10;
280     vdagent_x11_get_wm_name(x11);
281     while (x11->net_wm_name == NULL && --i > 0) {
282         usleep(100000);
283         vdagent_x11_get_wm_name(x11);
284     }
285     if (x11->debug && x11->net_wm_name)
286         syslog(LOG_DEBUG, "net_wm_name: \"%s\", has icons: %d",
287                x11->net_wm_name, vdagent_x11_has_icons_on_desktop(x11));
288
289     /* Flush output buffers and consume any pending events */
290     vdagent_x11_do_read(x11);
291
292     return x11;
293 }
294
295 void vdagent_x11_destroy(struct vdagent_x11 *x11, int vdagentd_disconnected)
296 {
297     uint8_t sel;
298
299     if (!x11)
300         return;
301
302     if (vdagentd_disconnected)
303         x11->vdagentd = NULL;
304
305     for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) {
306         vdagent_x11_set_clipboard_owner(x11, sel, owner_none);
307     }
308
309     XCloseDisplay(x11->display);
310     g_free(x11->net_wm_name);
311     free(x11->randr.failed_conf);
312     free(x11);
313 }
314
315 int vdagent_x11_get_fd(struct vdagent_x11 *x11)
316 {
317     return x11->fd;
318 }
319
320 static void vdagent_x11_next_selection_request(struct vdagent_x11 *x11)
321 {
322     struct vdagent_x11_selection_request *selection_request;
323     selection_request = x11->selection_req;
324     x11->selection_req = selection_request->next;
325     free(selection_request);
326 }
327
328 static void vdagent_x11_next_conversion_request(struct vdagent_x11 *x11)
329 {
330     struct vdagent_x11_conversion_request *conversion_req;
331     conversion_req = x11->conversion_req;
332     x11->conversion_req = conversion_req->next;
333     free(conversion_req);
334 }
335
336 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11,
337     uint8_t selection, int new_owner)
338 {
339     struct vdagent_x11_selection_request *prev_sel, *curr_sel, *next_sel;
340     struct vdagent_x11_conversion_request *prev_conv, *curr_conv, *next_conv;
341     int once;
342
343     /* Clear pending requests and clipboard data */
344     once = 1;
345     prev_sel = NULL;
346     next_sel = x11->selection_req;
347     while (next_sel) {
348         curr_sel = next_sel;
349         next_sel = curr_sel->next;
350         if (curr_sel->selection == selection) {
351             if (once) {
352                 SELPRINTF("selection requests pending on clipboard ownership "
353                           "change, clearing");
354                 once = 0;
355             }
356             vdagent_x11_send_selection_notify(x11, None, curr_sel);
357             if (curr_sel == x11->selection_req) {
358                 x11->selection_req = next_sel;
359                 free(x11->selection_req_data);
360                 x11->selection_req_data = NULL;
361                 x11->selection_req_data_pos = 0;
362                 x11->selection_req_data_size = 0;
363                 x11->selection_req_atom = None;
364             } else {
365                 prev_sel->next = next_sel;
366             }
367             free(curr_sel);
368         } else {
369             prev_sel = curr_sel;
370         }
371     }
372
373     once = 1;
374     prev_conv = NULL;
375     next_conv = x11->conversion_req;
376     while (next_conv) {
377         curr_conv = next_conv;
378         next_conv = curr_conv->next;
379         if (curr_conv->selection == selection) {
380             if (once) {
381                 SELPRINTF("client clipboard request pending on clipboard "
382                           "ownership change, clearing");
383                 once = 0;
384             }
385             if (x11->vdagentd)
386                 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, selection,
387                             VD_AGENT_CLIPBOARD_NONE, NULL, 0);
388             if (curr_conv == x11->conversion_req) {
389                 x11->conversion_req = next_conv;
390                 x11->clipboard_data_size = 0;
391                 x11->expect_property_notify = 0;
392             } else {
393                 prev_conv->next = next_conv;
394             }
395             free(curr_conv);
396         } else {
397             prev_conv = curr_conv;
398         }
399     }
400
401     if (new_owner == owner_none) {
402         /* When going from owner_guest to owner_none we need to send a
403            clipboard release message to the client */
404         if (x11->clipboard_owner[selection] == owner_guest && x11->vdagentd) {
405             udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_RELEASE, selection,
406                         0, NULL, 0);
407         }
408         x11->clipboard_type_count[selection] = 0;
409     }
410     x11->clipboard_owner[selection] = new_owner;
411 }
412
413 static int vdagent_x11_get_clipboard_atom(struct vdagent_x11 *x11, uint8_t selection, Atom* clipboard)
414 {
415     if (selection == VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) {
416         *clipboard = x11->clipboard_atom;
417     } else if (selection == VD_AGENT_CLIPBOARD_SELECTION_PRIMARY) {
418         *clipboard = x11->clipboard_primary_atom;
419     } else {
420         syslog(LOG_ERR, "get_clipboard_atom: unknown selection");
421         return -1;
422     }
423
424     return 0;
425 }
426
427 static int vdagent_x11_get_clipboard_selection(struct vdagent_x11 *x11,
428     XEvent *event, uint8_t *selection)
429 {
430     Atom atom;
431
432     if (event->type == x11->xfixes_event_base) {
433         XFixesSelectionNotifyEvent *xfev = (XFixesSelectionNotifyEvent *)event;
434         atom = xfev->selection;
435     } else if (event->type == SelectionNotify) {
436         atom = event->xselection.selection;
437     } else if (event->type == SelectionRequest) {
438         atom = event->xselectionrequest.selection;
439     } else {
440         syslog(LOG_ERR, "get_clipboard_selection: unknown event type");
441         return -1;
442     }
443
444     if (atom == x11->clipboard_atom) {
445         *selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
446     } else if (atom == x11->clipboard_primary_atom) {
447         *selection = VD_AGENT_CLIPBOARD_SELECTION_PRIMARY;
448     } else {
449         syslog(LOG_ERR, "get_clipboard_selection: unknown selection");
450         return -1;
451     }
452
453     return 0;
454 }
455
456 static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
457 {
458     int i, handled = 0;
459     uint8_t selection;
460
461     if (event.type == x11->xfixes_event_base) {
462         union {
463             XEvent ev;
464             XFixesSelectionNotifyEvent xfev;
465         } ev;
466
467         if (vdagent_x11_get_clipboard_selection(x11, &event, &selection)) {
468             return;
469         }
470
471         ev.ev = event;
472         switch (ev.xfev.subtype) {
473         case XFixesSetSelectionOwnerNotify:
474             break;
475         /* Treat ... as a SelectionOwnerNotify None */
476         case XFixesSelectionWindowDestroyNotify:
477         case XFixesSelectionClientCloseNotify:
478             ev.xfev.owner = None;
479             break;
480         default:
481             VSELPRINTF("unexpected xfix event subtype %d window %d",
482                        (int)ev.xfev.subtype, (int)event.xany.window);
483             return;
484         }
485         VSELPRINTF("New selection owner: %u", (unsigned int)ev.xfev.owner);
486
487         /* Ignore becoming the owner ourselves */
488         if (ev.xfev.owner == x11->selection_window)
489             return;
490
491         /* If the clipboard owner is changed we no longer own it */
492         vdagent_x11_set_clipboard_owner(x11, selection, owner_none);
493
494         if (ev.xfev.owner == None)
495             return;
496
497         /* Request the supported targets from the new owner */
498         XConvertSelection(x11->display, ev.xfev.selection, x11->targets_atom,
499                           x11->targets_atom, x11->selection_window,
500                           CurrentTime);
501         x11->expected_targets_notifies[selection]++;
502         return;
503     }
504
505     switch (event.type) {
506     case ConfigureNotify:
507         // TODO: handle CrtcConfigureNotify, OutputConfigureNotify can be ignored.
508         for (i = 0; i < x11->screen_count; i++)
509             if (event.xconfigure.window == x11->root_window[i])
510                 break;
511         if (i == x11->screen_count)
512             break;
513
514         handled = 1;
515         vdagent_x11_randr_handle_root_size_change(x11, i,
516                 event.xconfigure.width, event.xconfigure.height);
517         break;
518     case MappingNotify:
519         /* These are uninteresting */
520         handled = 1;
521         break;
522     case SelectionNotify:
523         if (event.xselection.target == x11->targets_atom)
524             vdagent_x11_handle_targets_notify(x11, &event);
525         else
526             vdagent_x11_handle_selection_notify(x11, &event, 0);
527
528         handled = 1;
529         break;
530     case PropertyNotify:
531         if (x11->expect_property_notify &&
532                                 event.xproperty.state == PropertyNewValue) {
533             vdagent_x11_handle_selection_notify(x11, &event, 1);
534         }
535         if (x11->selection_req_data && 
536                                  event.xproperty.state == PropertyDelete) {
537             vdagent_x11_handle_property_delete_notify(x11, &event);
538         }
539         /* Always mark as handled, since we cannot unselect input for property
540            notifications once we are done with handling the incr transfer. */
541         handled = 1;
542         break;
543     case SelectionClear:
544         /* Do nothing the clipboard ownership will get updated through
545            the XFixesSetSelectionOwnerNotify event */
546         handled = 1;
547         break;
548     case SelectionRequest: {
549         struct vdagent_x11_selection_request *req, *new_req;
550
551         if (vdagent_x11_get_clipboard_selection(x11, &event, &selection)) {
552             return;
553         }
554
555         new_req = malloc(sizeof(*new_req));
556         if (!new_req) {
557             SELPRINTF("out of memory on SelectionRequest, ignoring.");
558             break;
559         }
560
561         handled = 1;
562
563         new_req->event = event;
564         new_req->selection = selection;
565         new_req->next = NULL;
566
567         if (!x11->selection_req) {
568             x11->selection_req = new_req;
569             vdagent_x11_handle_selection_request(x11);
570             break;
571         }
572
573         /* maybe we should limit the selection_request stack depth ? */
574         req = x11->selection_req;
575         while (req->next)
576             req = req->next;
577
578         req->next = new_req;
579         break;
580     }
581     }
582     if (!handled && x11->debug)
583         syslog(LOG_DEBUG, "unhandled x11 event, type %d, window %d",
584                (int)event.type, (int)event.xany.window);
585 }
586
587 void vdagent_x11_do_read(struct vdagent_x11 *x11)
588 {
589     XEvent event;
590
591     while (XPending(x11->display)) {
592         XNextEvent(x11->display, &event);
593         vdagent_x11_handle_event(x11, event);
594     }
595 }
596
597 static const char *vdagent_x11_get_atom_name(struct vdagent_x11 *x11, Atom a)
598 {
599     if (a == None)
600         return "None";
601
602     return XGetAtomName(x11->display, a);
603 }
604
605 static int vdagent_x11_get_selection(struct vdagent_x11 *x11, XEvent *event,
606     uint8_t selection, Atom type, Atom prop, int format,
607     unsigned char **data_ret, int incr)
608 {
609     Bool del = incr ? True: False;
610     Atom type_ret;
611     int format_ret, ret_val = -1;
612     unsigned long len, remain;
613     unsigned char *data = NULL;
614
615     *data_ret = NULL;
616
617     if (!incr) {
618         if (event->xselection.property == None) {
619             VSELPRINTF("XConvertSelection refused by clipboard owner");
620             goto exit;
621         }
622
623         if (event->xselection.requestor != x11->selection_window ||
624             event->xselection.property != prop) {
625             SELPRINTF("SelectionNotify parameters mismatch");
626             goto exit;
627         }
628     }
629
630     if (XGetWindowProperty(x11->display, x11->selection_window, prop, 0,
631                            LONG_MAX, del, type, &type_ret, &format_ret, &len,
632                            &remain, &data) != Success) {
633         SELPRINTF("XGetWindowProperty failed");
634         goto exit;
635     }
636
637     if (!incr && prop != x11->targets_atom) {
638         if (type_ret == x11->incr_atom) {
639             int prop_min_size = *(uint32_t*)data;
640
641             if (x11->expect_property_notify) {
642                 SELPRINTF("received an incr SelectionNotify while "
643                           "still reading another incr property");
644                 goto exit;
645             }
646
647             if (x11->clipboard_data_space < prop_min_size) {
648                 free(x11->clipboard_data);
649                 x11->clipboard_data = malloc(prop_min_size);
650                 if (!x11->clipboard_data) {
651                     SELPRINTF("out of memory allocating clipboard buffer");
652                     x11->clipboard_data_space = 0;
653                     goto exit;
654                 }
655                 x11->clipboard_data_space = prop_min_size;
656             }
657             x11->expect_property_notify = 1;
658             XSelectInput(x11->display, x11->selection_window,
659                          PropertyChangeMask);
660             XDeleteProperty(x11->display, x11->selection_window, prop);
661             XFree(data);
662             return 0; /* Wait for more data */
663         }
664         XDeleteProperty(x11->display, x11->selection_window, prop);
665     }
666
667     if (type_ret != type) {
668         SELPRINTF("expected property type: %s, got: %s",
669                   vdagent_x11_get_atom_name(x11, type),
670                   vdagent_x11_get_atom_name(x11, type_ret));
671         goto exit;
672     }
673
674     if (format_ret != format) {
675         SELPRINTF("expected %d bit format, got %d bits", format, format_ret);
676         goto exit;
677     }
678
679     /* Convert len to bytes */
680     switch(format) {
681     case 8:
682         break;
683     case 16:
684         len *= sizeof(short);
685         break;
686     case 32:
687         len *= sizeof(long);
688         break;
689     }
690
691     if (incr) {
692         if (len) {
693             if (x11->clipboard_data_size + len > x11->clipboard_data_space) {
694                 void *old_clipboard_data = x11->clipboard_data;
695
696                 x11->clipboard_data_space = x11->clipboard_data_size + len;
697                 x11->clipboard_data = realloc(x11->clipboard_data,
698                                               x11->clipboard_data_space);
699                 if (!x11->clipboard_data) {
700                     SELPRINTF("out of memory allocating clipboard buffer");
701                     x11->clipboard_data_space = 0;
702                     free(old_clipboard_data);
703                     goto exit;
704                 }
705             }
706             memcpy(x11->clipboard_data + x11->clipboard_data_size, data, len);
707             x11->clipboard_data_size += len;
708             VSELPRINTF("Appended %ld bytes to buffer", len);
709             XFree(data);
710             return 0; /* Wait for more data */
711         }
712         len = x11->clipboard_data_size;
713         *data_ret = x11->clipboard_data;
714     } else
715         *data_ret = data;
716
717     if (len > 0) {
718         ret_val = len;
719     } else {
720         SELPRINTF("property contains no data (zero length)");
721         *data_ret = NULL;
722     }
723
724 exit:
725     if ((incr || ret_val == -1) && data)
726         XFree(data);
727
728     if (incr) {
729         x11->clipboard_data_size = 0;
730         x11->expect_property_notify = 0;
731     }
732
733     return ret_val;
734 }
735
736 static void vdagent_x11_get_selection_free(struct vdagent_x11 *x11,
737     unsigned char *data, int incr)
738 {
739     if (incr) {
740         /* If the clipboard has grown large return the memory to the system */
741         if (x11->clipboard_data_space > 512 * 1024) {
742             free(x11->clipboard_data);
743             x11->clipboard_data = NULL;
744             x11->clipboard_data_space = 0;
745         }
746     } else if (data)
747         XFree(data);
748 }
749
750 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11 *x11,
751     uint8_t selection, Atom target)
752 {
753     int i, j;
754
755     for (i = 0; i < clipboard_format_count; i++) {
756         for (j = 0; j < x11->clipboard_formats[i].atom_count; i++) {
757             if (x11->clipboard_formats[i].atoms[j] == target) {
758                 return x11->clipboard_formats[i].type;
759             }
760         }
761     }
762
763     VSELPRINTF("unexpected selection type %s",
764                vdagent_x11_get_atom_name(x11, target));
765     return VD_AGENT_CLIPBOARD_NONE;
766 }
767
768 static Atom vdagent_x11_type_to_target(struct vdagent_x11 *x11,
769                                        uint8_t selection, uint32_t type)
770 {
771     int i;
772
773     for (i = 0; i < x11->clipboard_type_count[selection]; i++) {
774         if (x11->clipboard_agent_types[selection][i] == type) {
775             return x11->clipboard_x11_targets[selection][i];
776         }
777     }
778     SELPRINTF("client requested unavailable type %u", type);
779     return None;
780 }
781
782 static void vdagent_x11_handle_conversion_request(struct vdagent_x11 *x11)
783 {
784     Atom clip = None;
785
786     if (!x11->conversion_req) {
787         return;
788     }
789
790     vdagent_x11_get_clipboard_atom(x11, x11->conversion_req->selection, &clip);
791     XConvertSelection(x11->display, clip, x11->conversion_req->target,
792                       clip, x11->selection_window, CurrentTime);
793 }
794
795 static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
796                                                 XEvent *event, int incr)
797 {
798     int len = 0;
799     unsigned char *data = NULL;
800     uint32_t type;
801     uint8_t selection = -1;
802     Atom clip = None;
803
804     if (!x11->conversion_req) {
805         syslog(LOG_ERR, "SelectionNotify received without a target");
806         return;
807     }
808     vdagent_x11_get_clipboard_atom(x11, x11->conversion_req->selection, &clip);
809
810     if (incr) {
811         if (event->xproperty.atom != clip ||
812                 event->xproperty.window != x11->selection_window) {
813             return;
814         }
815     } else {
816         if (vdagent_x11_get_clipboard_selection(x11, event, &selection)) {
817             len = -1;
818         } else if (selection != x11->conversion_req->selection) {
819             SELPRINTF("Requested data for selection %d got %d",
820                       (int)x11->conversion_req->selection, (int)selection);
821             len = -1;
822         }
823         if (event->xselection.target != x11->conversion_req->target &&
824                 event->xselection.target != x11->incr_atom) {
825             SELPRINTF("Requested %s target got %s",
826                 vdagent_x11_get_atom_name(x11, x11->conversion_req->target),
827                 vdagent_x11_get_atom_name(x11, event->xselection.target));
828             len = -1;
829         }
830     }
831
832     selection = x11->conversion_req->selection;
833     type = vdagent_x11_target_to_type(x11, selection,
834                                       x11->conversion_req->target);
835     if (type == VD_AGENT_CLIPBOARD_NONE)
836         SELPRINTF("internal error conversion_req has bad target %s",
837                   vdagent_x11_get_atom_name(x11, x11->conversion_req->target));
838     if (len == 0) { /* No errors so far */
839         len = vdagent_x11_get_selection(x11, event, selection,
840                                         x11->conversion_req->target,
841                                         clip, 8, &data, incr);
842         if (len == 0) { /* waiting for more data? */
843             return;
844         }
845     }
846     if (len == -1) {
847         type = VD_AGENT_CLIPBOARD_NONE;
848         len = 0;
849     }
850
851     udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, selection, type,
852                 data, len);
853     vdagent_x11_get_selection_free(x11, data, incr);
854
855     vdagent_x11_next_conversion_request(x11);
856     vdagent_x11_handle_conversion_request(x11);
857 }
858
859 static Atom atom_lists_overlap(Atom *atoms1, Atom *atoms2, int l1, int l2)
860 {
861     int i, j;
862
863     for (i = 0; i < l1; i++)
864         for (j = 0; j < l2; j++)
865             if (atoms1[i] == atoms2[j])
866                 return atoms1[i];
867
868     return 0;
869 }
870
871 static void vdagent_x11_print_targets(struct vdagent_x11 *x11,
872     uint8_t selection, const char *action, Atom *atoms, int c)
873 {
874     int i;
875     VSELPRINTF("%s %d targets:", action, c);
876     for (i = 0; i < c; i++)
877         VSELPRINTF("%s", vdagent_x11_get_atom_name(x11, atoms[i]));
878 }
879
880 static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
881                                               XEvent *event)
882 {
883     int i, len;
884     Atom atom, *atoms = NULL;
885     uint8_t selection;
886     int *type_count;
887
888     if (vdagent_x11_get_clipboard_selection(x11, event, &selection)) {
889         return;
890     }
891
892     if (!x11->expected_targets_notifies[selection]) {
893         SELPRINTF("unexpected selection notify TARGETS");
894         return;
895     }
896
897     x11->expected_targets_notifies[selection]--;
898
899     /* If we have more targets_notifies pending, ignore this one, we
900        are only interested in the targets list of the current owner
901        (which is the last one we've requested a targets list from) */
902     if (x11->expected_targets_notifies[selection]) {
903         return;
904     }
905
906     len = vdagent_x11_get_selection(x11, event, selection,
907                                     XA_ATOM, x11->targets_atom, 32,
908                                     (unsigned char **)&atoms, 0);
909     if (len == 0 || len == -1) /* waiting for more data or error? */
910         return;
911
912     /* bytes -> atoms */
913     len /= sizeof(Atom);
914     vdagent_x11_print_targets(x11, selection, "received", atoms, len);
915
916     type_count = &x11->clipboard_type_count[selection];
917     *type_count = 0;
918     for (i = 0; i < clipboard_format_count; i++) {
919         atom = atom_lists_overlap(x11->clipboard_formats[i].atoms, atoms,
920                                   x11->clipboard_formats[i].atom_count, len);
921         if (atom) {
922             x11->clipboard_agent_types[selection][*type_count] =
923                 x11->clipboard_formats[i].type;
924             x11->clipboard_x11_targets[selection][*type_count] = atom;
925             (*type_count)++;
926             if (*type_count ==
927                     sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t)) {
928                 SELPRINTF("handle_targets_notify: too many types");
929                 break;
930             }
931         }
932     }
933
934     if (*type_count) {
935         udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_GRAB, selection, 0,
936                     (uint8_t *)x11->clipboard_agent_types[selection],
937                     *type_count * sizeof(uint32_t));
938         vdagent_x11_set_clipboard_owner(x11, selection, owner_guest);
939     }
940
941     vdagent_x11_get_selection_free(x11, (unsigned char *)atoms, 0);
942 }
943
944 static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
945     Atom prop, struct vdagent_x11_selection_request *request)
946 {
947     XEvent res, *event;
948
949     if (request) {
950         event = &request->event;
951     } else {
952         event = &x11->selection_req->event;
953     }
954
955     res.xselection.property = prop;
956     res.xselection.type = SelectionNotify;
957     res.xselection.display = event->xselectionrequest.display;
958     res.xselection.requestor = event->xselectionrequest.requestor;
959     res.xselection.selection = event->xselectionrequest.selection;
960     res.xselection.target = event->xselectionrequest.target;
961     res.xselection.time = event->xselectionrequest.time;
962
963     vdagent_x11_set_error_handler(x11, vdagent_x11_ignore_bad_window_handler);
964     XSendEvent(x11->display, event->xselectionrequest.requestor, 0, 0, &res);
965     vdagent_x11_restore_error_handler(x11);
966
967     if (!request) {
968         vdagent_x11_next_selection_request(x11);
969         vdagent_x11_handle_selection_request(x11);
970     }
971 }
972
973 static void vdagent_x11_send_targets(struct vdagent_x11 *x11,
974     uint8_t selection, XEvent *event)
975 {
976     Atom prop, targets[256] = { x11->targets_atom, };
977     int i, j, k, target_count = 1;
978
979     for (i = 0; i < x11->clipboard_type_count[selection]; i++) {
980         for (j = 0; j < clipboard_format_count; j++) {
981             if (x11->clipboard_formats[j].type !=
982                     x11->clipboard_agent_types[selection][i])
983                 continue;
984
985             for (k = 0; k < x11->clipboard_formats[j].atom_count; k++) {
986                 targets[target_count] = x11->clipboard_formats[j].atoms[k];
987                 target_count++;
988                 if (target_count == sizeof(targets)/sizeof(Atom)) {
989                     SELPRINTF("send_targets: too many targets");
990                     goto exit_loop;
991                 }
992             }
993         }
994     }
995 exit_loop:
996
997     prop = event->xselectionrequest.property;
998     if (prop == None)
999         prop = event->xselectionrequest.target;
1000
1001     vdagent_x11_set_error_handler(x11, vdagent_x11_ignore_bad_window_handler);
1002     XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
1003                     XA_ATOM, 32, PropModeReplace, (unsigned char *)&targets,
1004                     target_count);
1005     if (vdagent_x11_restore_error_handler(x11) == 0) {
1006         vdagent_x11_print_targets(x11, selection, "sent",
1007                                   targets, target_count);
1008         vdagent_x11_send_selection_notify(x11, prop, NULL);
1009     } else
1010         SELPRINTF("send_targets: Failed to sent, requestor window gone");
1011 }
1012
1013 static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11)
1014 {
1015     XEvent *event;
1016     uint32_t type = VD_AGENT_CLIPBOARD_NONE;
1017     uint8_t selection;
1018
1019     if (!x11->selection_req)
1020         return;
1021
1022     event = &x11->selection_req->event;
1023     selection = x11->selection_req->selection;
1024
1025     if (x11->clipboard_owner[selection] != owner_client) {
1026         SELPRINTF("received selection request event for target %s, "
1027                   "while not owning client clipboard",
1028             vdagent_x11_get_atom_name(x11, event->xselectionrequest.target));
1029         vdagent_x11_send_selection_notify(x11, None, NULL);
1030         return;
1031     }
1032
1033     if (event->xselectionrequest.target == x11->multiple_atom) {
1034         SELPRINTF("multiple target not supported");
1035         vdagent_x11_send_selection_notify(x11, None, NULL);
1036         return;
1037     }
1038
1039     if (event->xselectionrequest.target == x11->targets_atom) {
1040         vdagent_x11_send_targets(x11, selection, event);
1041         return;
1042     }
1043
1044     type = vdagent_x11_target_to_type(x11, selection,
1045                                       event->xselectionrequest.target);
1046     if (type == VD_AGENT_CLIPBOARD_NONE) {
1047         VSELPRINTF("guest app requested a non-advertised target");
1048         vdagent_x11_send_selection_notify(x11, None, NULL);
1049         return;
1050     }
1051
1052     udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_REQUEST, selection, type,
1053                 NULL, 0);
1054 }
1055
1056 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11,
1057                                                       XEvent *del_event)
1058 {
1059     XEvent *sel_event;
1060     int len;
1061     uint8_t selection;
1062
1063     assert(x11->selection_req);
1064     sel_event = &x11->selection_req->event;
1065     selection = x11->selection_req->selection;
1066     if (del_event->xproperty.window != sel_event->xselectionrequest.requestor
1067             || del_event->xproperty.atom != x11->selection_req_atom) {
1068         return;
1069     }
1070
1071     len = x11->selection_req_data_size - x11->selection_req_data_pos;
1072     if (len > x11->max_prop_size) {
1073         len = x11->max_prop_size;
1074     }
1075
1076     if (len) {
1077         VSELPRINTF("Sending %d-%d/%d bytes of clipboard data",
1078                 x11->selection_req_data_pos,
1079                 x11->selection_req_data_pos + len - 1,
1080                 x11->selection_req_data_size);
1081     } else {
1082         VSELPRINTF("Ending incr send of clipboard data");
1083     }
1084     vdagent_x11_set_error_handler(x11, vdagent_x11_ignore_bad_window_handler);
1085     XChangeProperty(x11->display, sel_event->xselectionrequest.requestor,
1086                     x11->selection_req_atom,
1087                     sel_event->xselectionrequest.target, 8, PropModeReplace,
1088                     x11->selection_req_data + x11->selection_req_data_pos,
1089                     len);
1090     if (vdagent_x11_restore_error_handler(x11)) {
1091         SELPRINTF("incr sent failed, requestor window gone");
1092         len = 0;
1093     }
1094
1095     x11->selection_req_data_pos += len;
1096
1097     /* Note we must explictly send a 0 sized XChangeProperty to signal the
1098        incr transfer is done. Hence we do not check if we've send all data
1099        but instead check we've send the final 0 sized XChangeProperty. */
1100     if (len == 0) {
1101         free(x11->selection_req_data);
1102         x11->selection_req_data = NULL;
1103         x11->selection_req_data_pos = 0;
1104         x11->selection_req_data_size = 0;
1105         x11->selection_req_atom = None;
1106         vdagent_x11_next_selection_request(x11);
1107         vdagent_x11_handle_selection_request(x11);
1108     }
1109 }
1110
1111 void vdagent_x11_clipboard_request(struct vdagent_x11 *x11,
1112         uint8_t selection, uint32_t type)
1113 {
1114     Atom target, clip;
1115     struct vdagent_x11_conversion_request *req, *new_req;
1116
1117     /* We don't use clip here, but we call get_clipboard_atom to verify
1118        selection is valid */
1119     if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) {
1120         goto none;
1121     }
1122
1123     if (x11->clipboard_owner[selection] != owner_guest) {
1124         SELPRINTF("received clipboard req while not owning guest clipboard");
1125         goto none;
1126     }
1127
1128     target = vdagent_x11_type_to_target(x11, selection, type);
1129     if (target == None) {
1130         goto none;
1131     }
1132
1133     new_req = malloc(sizeof(*new_req));
1134     if (!new_req) {
1135         SELPRINTF("out of memory on client clipboard request, ignoring.");
1136         return;
1137     }
1138
1139     new_req->target = target;
1140     new_req->selection = selection;
1141     new_req->next = NULL;
1142
1143     if (!x11->conversion_req) {
1144         x11->conversion_req = new_req;
1145         vdagent_x11_handle_conversion_request(x11);
1146         /* Flush output buffers and consume any pending events */
1147         vdagent_x11_do_read(x11);
1148         return;
1149     }
1150
1151     /* maybe we should limit the conversion_request stack depth ? */
1152     req = x11->conversion_req;
1153     while (req->next)
1154         req = req->next;
1155
1156     req->next = new_req;
1157     return;
1158
1159 none:
1160     udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
1161                 selection, VD_AGENT_CLIPBOARD_NONE, NULL, 0);
1162 }
1163
1164 void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint8_t selection,
1165     uint32_t *types, uint32_t type_count)
1166 {
1167     Atom clip = None;
1168
1169     if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) {
1170         return;
1171     }
1172
1173     if (type_count > sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t)) {
1174         SELPRINTF("x11_clipboard_grab: too many types");
1175         type_count = sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t);
1176     }
1177
1178     memcpy(x11->clipboard_agent_types[selection], types,
1179            type_count * sizeof(uint32_t));
1180     x11->clipboard_type_count[selection] = type_count;
1181
1182     XSetSelectionOwner(x11->display, clip,
1183                        x11->selection_window, CurrentTime);
1184     vdagent_x11_set_clipboard_owner(x11, selection, owner_client);
1185
1186     /* Flush output buffers and consume any pending events */
1187     vdagent_x11_do_read(x11);
1188 }
1189
1190 void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint8_t selection,
1191     uint32_t type, uint8_t *data, uint32_t size)
1192 {
1193     Atom prop;
1194     XEvent *event;
1195     uint32_t type_from_event;
1196
1197     if (x11->selection_req_data) {
1198         if (type || size) {
1199             SELPRINTF("received clipboard data while still sending"
1200                       " data from previous request, ignoring");
1201         }
1202         free(data);
1203         return;
1204     }
1205
1206     if (!x11->selection_req) {
1207         if (type || size) {
1208             SELPRINTF("received clipboard data without an "
1209                       "outstanding selection request, ignoring");
1210         }
1211         free(data);
1212         return;
1213     }
1214
1215     event = &x11->selection_req->event;
1216     type_from_event = vdagent_x11_target_to_type(x11, 
1217                                              x11->selection_req->selection,
1218                                              event->xselectionrequest.target);
1219     if (type_from_event != type ||
1220             selection != x11->selection_req->selection) {
1221         if (selection != x11->selection_req->selection) {
1222             SELPRINTF("expecting data for selection %d got %d",
1223                       (int)x11->selection_req->selection, (int)selection);
1224         }
1225         if (type_from_event != type) {
1226             SELPRINTF("expecting type %u clipboard data got %u",
1227                       type_from_event, type);
1228         }
1229         vdagent_x11_send_selection_notify(x11, None, NULL);
1230         free(data);
1231
1232         /* Flush output buffers and consume any pending events */
1233         vdagent_x11_do_read(x11);
1234         return;
1235     }
1236
1237     prop = event->xselectionrequest.property;
1238     if (prop == None)
1239         prop = event->xselectionrequest.target;
1240
1241     if (size > x11->max_prop_size) {
1242         unsigned long len = size;
1243         VSELPRINTF("Starting incr send of clipboard data");
1244
1245         vdagent_x11_set_error_handler(x11, vdagent_x11_ignore_bad_window_handler);
1246         XSelectInput(x11->display, event->xselectionrequest.requestor,
1247                      PropertyChangeMask);
1248         XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
1249                         x11->incr_atom, 32, PropModeReplace,
1250                         (unsigned char*)&len, 1);
1251         if (vdagent_x11_restore_error_handler(x11) == 0) {
1252             x11->selection_req_data = data;
1253             x11->selection_req_data_pos = 0;
1254             x11->selection_req_data_size = size;
1255             x11->selection_req_atom = prop;
1256             vdagent_x11_send_selection_notify(x11, prop, x11->selection_req);
1257         } else {
1258             SELPRINTF("clipboard data sent failed, requestor window gone");
1259             free(data);
1260         }
1261     } else {
1262         vdagent_x11_set_error_handler(x11, vdagent_x11_ignore_bad_window_handler);
1263         XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
1264                         event->xselectionrequest.target, 8, PropModeReplace,
1265                         data, size);
1266         if (vdagent_x11_restore_error_handler(x11) == 0)
1267             vdagent_x11_send_selection_notify(x11, prop, NULL);
1268         else
1269             SELPRINTF("clipboard data sent failed, requestor window gone");
1270
1271         free(data);
1272     }
1273
1274     /* Flush output buffers and consume any pending events */
1275     vdagent_x11_do_read(x11);
1276 }
1277
1278 void vdagent_x11_clipboard_release(struct vdagent_x11 *x11, uint8_t selection)
1279 {
1280     XEvent event;
1281     Atom clip = None;
1282
1283     if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) {
1284         return;
1285     }
1286
1287     if (x11->clipboard_owner[selection] != owner_client) {
1288         VSELPRINTF("received release while not owning client clipboard");
1289         return;
1290     }
1291
1292     XSetSelectionOwner(x11->display, clip, None, CurrentTime);
1293     /* Make sure we process the XFixesSetSelectionOwnerNotify event caused
1294        by this, so we don't end up changing the clipboard owner to none, after
1295        it has already been re-owned because this event is still pending. */
1296     XSync(x11->display, False);
1297     while (XCheckTypedEvent(x11->display, x11->xfixes_event_base,
1298                             &event))
1299         vdagent_x11_handle_event(x11, event);
1300
1301     /* Note no need to do a set_clipboard_owner(owner_none) here, as that is
1302        already done by processing the XFixesSetSelectionOwnerNotify event. */
1303
1304     /* Flush output buffers and consume any pending events */
1305     vdagent_x11_do_read(x11);
1306 }
1307
1308 void vdagent_x11_client_disconnected(struct vdagent_x11 *x11)
1309 {
1310     int sel;
1311
1312     for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; sel++) {
1313         if (x11->clipboard_owner[sel] == owner_client)
1314             vdagent_x11_clipboard_release(x11, sel);
1315     }
1316 }
1317
1318 /* Function used to determine the default location to save file-xfers,
1319    xdg desktop dir or xdg download dir. We error on the save side and use a
1320    whitelist approach, so any unknown desktops will end up with saving
1321    file-xfers to the xdg download dir, and opening the xdg download dir with
1322    xdg-open when the file-xfer completes. */
1323 int vdagent_x11_has_icons_on_desktop(struct vdagent_x11 *x11)
1324 {
1325     const char * const wms_with_icons_on_desktop[] = {
1326         "Metacity", /* GNOME-2 or GNOME-3 fallback */
1327         "Xfwm4",    /* XFCE */
1328         "Marco",    /* Mate */
1329         NULL
1330     };
1331     int i;
1332
1333     if (x11->net_wm_name)
1334         for (i = 0; wms_with_icons_on_desktop[i]; i++)
1335             if (!strcmp(x11->net_wm_name, wms_with_icons_on_desktop[i]))
1336                 return 1;
1337
1338     return 0;
1339 }