1 /* vdagent-x11.c vdagent x11 code
3 Copyright 2010-2011 Red Hat, Inc.
6 Hans de Goede <hdegoede@redhat.com>
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.
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.
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/>.
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!
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.
31 Calling XPending when-ever we return to the mainloop also ensures any
32 pending writes are flushed. */
41 #include <X11/Xatom.h>
43 #include <X11/extensions/Xfixes.h>
44 #include "vdagentd-proto.h"
45 #include "vdagent-x11.h"
46 #include "vdagent-x11-priv.h"
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;
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,
57 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11,
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);
64 static const char *vdagent_x11_sel_to_str(uint8_t selection) {
66 case VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD:
68 case VD_AGENT_CLIPBOARD_SELECTION_PRIMARY:
70 case VD_AGENT_CLIPBOARD_SELECTION_SECONDARY:
77 static int vdagent_x11_debug_error_handler(
78 Display *display, XErrorEvent *error)
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)
88 if (error->error_code == BadWindow)
91 return vdagent_x11_prev_error_handler(display, error);
94 void vdagent_x11_set_error_handler(struct vdagent_x11 *x11,
95 int (*handler)(Display *, XErrorEvent *))
97 XSync(x11->display, False);
98 vdagent_x11_caught_error = 0;
99 vdagent_x11_prev_error_handler = XSetErrorHandler(handler);
102 int vdagent_x11_restore_error_handler(struct vdagent_x11 *x11)
106 XSync(x11->display, False);
107 XSetErrorHandler(vdagent_x11_prev_error_handler);
108 error = vdagent_x11_caught_error;
109 vdagent_x11_caught_error = 0;
114 static void vdagent_x11_get_wm_name(struct vdagent_x11 *x11)
118 unsigned long len, remain;
119 unsigned char *data = NULL;
120 Window sup_window = None;
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);
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);
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);
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) {
156 g_strndup((char *)data, (format_ret / 8) * len);
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) {
167 g_strndup((char *)data, (format_ret / 8) * len);
173 vdagent_x11_restore_error_handler(x11);
176 struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd,
179 struct vdagent_x11 *x11;
180 XWindowAttributes attrib;
181 int i, j, major, minor;
183 x11 = calloc(1, sizeof(*x11));
185 syslog(LOG_ERR, "out of memory allocating vdagent_x11 struct");
189 x11->vdagentd = vdagentd;
192 x11->display = XOpenDisplay(NULL);
194 syslog(LOG_ERR, "could not connect to X-server");
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);
209 XSetErrorHandler(vdagent_x11_debug_error_handler);
210 XSynchronize(x11->display, True);
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],
229 x11->clipboard_formats[i].atom_count = j;
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);
236 syslog(LOG_DEBUG, "Selection window: %u", (int)x11->selection_window);
238 vdagent_x11_randr_init(x11);
240 if (XFixesQueryExtension(x11->display, &x11->xfixes_event_base, &i) &&
241 XFixesQueryVersion(x11->display, &major, &minor) && major >= 1) {
243 XFixesSelectSelectionInput(x11->display, x11->root_window[0],
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);
254 syslog(LOG_ERR, "no xfixes, no guest -> client copy paste support");
256 x11->max_prop_size = XExtendedMaxRequestSize(x11->display);
257 if (x11->max_prop_size) {
258 x11->max_prop_size -= 100;
260 x11->max_prop_size = XMaxRequestSize(x11->display) - 100;
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;
266 for (i = 0; i < x11->screen_count; i++) {
267 /* Catch resolution changes */
268 XSelectInput(x11->display, x11->root_window[i], StructureNotifyMask);
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;
275 vdagent_x11_send_daemon_guest_xorg_res(x11, 1);
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. */
280 vdagent_x11_get_wm_name(x11);
281 while (x11->net_wm_name == NULL && --i > 0) {
283 vdagent_x11_get_wm_name(x11);
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));
289 /* Flush output buffers and consume any pending events */
290 vdagent_x11_do_read(x11);
295 void vdagent_x11_destroy(struct vdagent_x11 *x11, int vdagentd_disconnected)
302 if (vdagentd_disconnected)
303 x11->vdagentd = NULL;
305 for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) {
306 vdagent_x11_set_clipboard_owner(x11, sel, owner_none);
309 XCloseDisplay(x11->display);
310 g_free(x11->net_wm_name);
311 free(x11->randr.failed_conf);
315 int vdagent_x11_get_fd(struct vdagent_x11 *x11)
320 static void vdagent_x11_next_selection_request(struct vdagent_x11 *x11)
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);
328 static void vdagent_x11_next_conversion_request(struct vdagent_x11 *x11)
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);
336 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11,
337 uint8_t selection, int new_owner)
339 struct vdagent_x11_selection_request *prev_sel, *curr_sel, *next_sel;
340 struct vdagent_x11_conversion_request *prev_conv, *curr_conv, *next_conv;
343 /* Clear pending requests and clipboard data */
346 next_sel = x11->selection_req;
349 next_sel = curr_sel->next;
350 if (curr_sel->selection == selection) {
352 SELPRINTF("selection requests pending on clipboard ownership "
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;
365 prev_sel->next = next_sel;
375 next_conv = x11->conversion_req;
377 curr_conv = next_conv;
378 next_conv = curr_conv->next;
379 if (curr_conv->selection == selection) {
381 SELPRINTF("client clipboard request pending on clipboard "
382 "ownership change, clearing");
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;
393 prev_conv->next = next_conv;
397 prev_conv = curr_conv;
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,
408 x11->clipboard_type_count[selection] = 0;
410 x11->clipboard_owner[selection] = new_owner;
413 static int vdagent_x11_get_clipboard_atom(struct vdagent_x11 *x11, uint8_t selection, Atom* clipboard)
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;
420 syslog(LOG_ERR, "get_clipboard_atom: unknown selection");
427 static int vdagent_x11_get_clipboard_selection(struct vdagent_x11 *x11,
428 XEvent *event, uint8_t *selection)
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;
440 syslog(LOG_ERR, "get_clipboard_selection: unknown event type");
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;
449 syslog(LOG_ERR, "get_clipboard_selection: unknown selection");
456 static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
461 if (event.type == x11->xfixes_event_base) {
464 XFixesSelectionNotifyEvent xfev;
467 if (vdagent_x11_get_clipboard_selection(x11, &event, &selection)) {
472 switch (ev.xfev.subtype) {
473 case XFixesSetSelectionOwnerNotify:
475 /* Treat ... as a SelectionOwnerNotify None */
476 case XFixesSelectionWindowDestroyNotify:
477 case XFixesSelectionClientCloseNotify:
478 ev.xfev.owner = None;
481 VSELPRINTF("unexpected xfix event subtype %d window %d",
482 (int)ev.xfev.subtype, (int)event.xany.window);
485 VSELPRINTF("New selection owner: %u", (unsigned int)ev.xfev.owner);
487 /* Ignore becoming the owner ourselves */
488 if (ev.xfev.owner == x11->selection_window)
491 /* If the clipboard owner is changed we no longer own it */
492 vdagent_x11_set_clipboard_owner(x11, selection, owner_none);
494 if (ev.xfev.owner == None)
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,
501 x11->expected_targets_notifies[selection]++;
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])
511 if (i == x11->screen_count)
515 vdagent_x11_randr_handle_root_size_change(x11, i,
516 event.xconfigure.width, event.xconfigure.height);
519 /* These are uninteresting */
522 case SelectionNotify:
523 if (event.xselection.target == x11->targets_atom)
524 vdagent_x11_handle_targets_notify(x11, &event);
526 vdagent_x11_handle_selection_notify(x11, &event, 0);
531 if (x11->expect_property_notify &&
532 event.xproperty.state == PropertyNewValue) {
533 vdagent_x11_handle_selection_notify(x11, &event, 1);
535 if (x11->selection_req_data &&
536 event.xproperty.state == PropertyDelete) {
537 vdagent_x11_handle_property_delete_notify(x11, &event);
539 /* Always mark as handled, since we cannot unselect input for property
540 notifications once we are done with handling the incr transfer. */
544 /* Do nothing the clipboard ownership will get updated through
545 the XFixesSetSelectionOwnerNotify event */
548 case SelectionRequest: {
549 struct vdagent_x11_selection_request *req, *new_req;
551 if (vdagent_x11_get_clipboard_selection(x11, &event, &selection)) {
555 new_req = malloc(sizeof(*new_req));
557 SELPRINTF("out of memory on SelectionRequest, ignoring.");
563 new_req->event = event;
564 new_req->selection = selection;
565 new_req->next = NULL;
567 if (!x11->selection_req) {
568 x11->selection_req = new_req;
569 vdagent_x11_handle_selection_request(x11);
573 /* maybe we should limit the selection_request stack depth ? */
574 req = x11->selection_req;
582 if (!handled && x11->debug)
583 syslog(LOG_DEBUG, "unhandled x11 event, type %d, window %d",
584 (int)event.type, (int)event.xany.window);
587 void vdagent_x11_do_read(struct vdagent_x11 *x11)
591 while (XPending(x11->display)) {
592 XNextEvent(x11->display, &event);
593 vdagent_x11_handle_event(x11, event);
597 static const char *vdagent_x11_get_atom_name(struct vdagent_x11 *x11, Atom a)
602 return XGetAtomName(x11->display, a);
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)
609 Bool del = incr ? True: False;
611 int format_ret, ret_val = -1;
612 unsigned long len, remain;
613 unsigned char *data = NULL;
618 if (event->xselection.property == None) {
619 VSELPRINTF("XConvertSelection refused by clipboard owner");
623 if (event->xselection.requestor != x11->selection_window ||
624 event->xselection.property != prop) {
625 SELPRINTF("SelectionNotify parameters mismatch");
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");
637 if (!incr && prop != x11->targets_atom) {
638 if (type_ret == x11->incr_atom) {
639 int prop_min_size = *(uint32_t*)data;
641 if (x11->expect_property_notify) {
642 SELPRINTF("received an incr SelectionNotify while "
643 "still reading another incr property");
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;
655 x11->clipboard_data_space = prop_min_size;
657 x11->expect_property_notify = 1;
658 XSelectInput(x11->display, x11->selection_window,
660 XDeleteProperty(x11->display, x11->selection_window, prop);
662 return 0; /* Wait for more data */
664 XDeleteProperty(x11->display, x11->selection_window, prop);
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));
674 if (format_ret != format) {
675 SELPRINTF("expected %d bit format, got %d bits", format, format_ret);
679 /* Convert len to bytes */
684 len *= sizeof(short);
693 if (x11->clipboard_data_size + len > x11->clipboard_data_space) {
694 void *old_clipboard_data = x11->clipboard_data;
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);
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);
710 return 0; /* Wait for more data */
712 len = x11->clipboard_data_size;
713 *data_ret = x11->clipboard_data;
720 SELPRINTF("property contains no data (zero length)");
725 if ((incr || ret_val == -1) && data)
729 x11->clipboard_data_size = 0;
730 x11->expect_property_notify = 0;
736 static void vdagent_x11_get_selection_free(struct vdagent_x11 *x11,
737 unsigned char *data, int 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;
750 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11 *x11,
751 uint8_t selection, Atom target)
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;
763 VSELPRINTF("unexpected selection type %s",
764 vdagent_x11_get_atom_name(x11, target));
765 return VD_AGENT_CLIPBOARD_NONE;
768 static Atom vdagent_x11_type_to_target(struct vdagent_x11 *x11,
769 uint8_t selection, uint32_t type)
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];
778 SELPRINTF("client requested unavailable type %u", type);
782 static void vdagent_x11_handle_conversion_request(struct vdagent_x11 *x11)
786 if (!x11->conversion_req) {
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);
795 static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
796 XEvent *event, int incr)
799 unsigned char *data = NULL;
801 uint8_t selection = -1;
804 if (!x11->conversion_req) {
805 syslog(LOG_ERR, "SelectionNotify received without a target");
808 vdagent_x11_get_clipboard_atom(x11, x11->conversion_req->selection, &clip);
811 if (event->xproperty.atom != clip ||
812 event->xproperty.window != x11->selection_window) {
816 if (vdagent_x11_get_clipboard_selection(x11, event, &selection)) {
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);
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));
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? */
847 type = VD_AGENT_CLIPBOARD_NONE;
851 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, selection, type,
853 vdagent_x11_get_selection_free(x11, data, incr);
855 vdagent_x11_next_conversion_request(x11);
856 vdagent_x11_handle_conversion_request(x11);
859 static Atom atom_lists_overlap(Atom *atoms1, Atom *atoms2, int l1, int l2)
863 for (i = 0; i < l1; i++)
864 for (j = 0; j < l2; j++)
865 if (atoms1[i] == atoms2[j])
871 static void vdagent_x11_print_targets(struct vdagent_x11 *x11,
872 uint8_t selection, const char *action, Atom *atoms, int c)
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]));
880 static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
884 Atom atom, *atoms = NULL;
888 if (vdagent_x11_get_clipboard_selection(x11, event, &selection)) {
892 if (!x11->expected_targets_notifies[selection]) {
893 SELPRINTF("unexpected selection notify TARGETS");
897 x11->expected_targets_notifies[selection]--;
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]) {
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? */
914 vdagent_x11_print_targets(x11, selection, "received", atoms, len);
916 type_count = &x11->clipboard_type_count[selection];
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);
922 x11->clipboard_agent_types[selection][*type_count] =
923 x11->clipboard_formats[i].type;
924 x11->clipboard_x11_targets[selection][*type_count] = atom;
927 sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t)) {
928 SELPRINTF("handle_targets_notify: too many types");
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);
941 vdagent_x11_get_selection_free(x11, (unsigned char *)atoms, 0);
944 static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
945 Atom prop, struct vdagent_x11_selection_request *request)
950 event = &request->event;
952 event = &x11->selection_req->event;
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;
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);
968 vdagent_x11_next_selection_request(x11);
969 vdagent_x11_handle_selection_request(x11);
973 static void vdagent_x11_send_targets(struct vdagent_x11 *x11,
974 uint8_t selection, XEvent *event)
976 Atom prop, targets[256] = { x11->targets_atom, };
977 int i, j, k, target_count = 1;
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])
985 for (k = 0; k < x11->clipboard_formats[j].atom_count; k++) {
986 targets[target_count] = x11->clipboard_formats[j].atoms[k];
988 if (target_count == sizeof(targets)/sizeof(Atom)) {
989 SELPRINTF("send_targets: too many targets");
997 prop = event->xselectionrequest.property;
999 prop = event->xselectionrequest.target;
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,
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);
1010 SELPRINTF("send_targets: Failed to sent, requestor window gone");
1013 static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11)
1016 uint32_t type = VD_AGENT_CLIPBOARD_NONE;
1019 if (!x11->selection_req)
1022 event = &x11->selection_req->event;
1023 selection = x11->selection_req->selection;
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);
1033 if (event->xselectionrequest.target == x11->multiple_atom) {
1034 SELPRINTF("multiple target not supported");
1035 vdagent_x11_send_selection_notify(x11, None, NULL);
1039 if (event->xselectionrequest.target == x11->targets_atom) {
1040 vdagent_x11_send_targets(x11, selection, event);
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);
1052 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_REQUEST, selection, type,
1056 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11,
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) {
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;
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);
1082 VSELPRINTF("Ending incr send of clipboard data");
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,
1090 if (vdagent_x11_restore_error_handler(x11)) {
1091 SELPRINTF("incr sent failed, requestor window gone");
1095 x11->selection_req_data_pos += len;
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. */
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);
1111 void vdagent_x11_clipboard_request(struct vdagent_x11 *x11,
1112 uint8_t selection, uint32_t type)
1115 struct vdagent_x11_conversion_request *req, *new_req;
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)) {
1123 if (x11->clipboard_owner[selection] != owner_guest) {
1124 SELPRINTF("received clipboard req while not owning guest clipboard");
1128 target = vdagent_x11_type_to_target(x11, selection, type);
1129 if (target == None) {
1133 new_req = malloc(sizeof(*new_req));
1135 SELPRINTF("out of memory on client clipboard request, ignoring.");
1139 new_req->target = target;
1140 new_req->selection = selection;
1141 new_req->next = NULL;
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);
1151 /* maybe we should limit the conversion_request stack depth ? */
1152 req = x11->conversion_req;
1156 req->next = new_req;
1160 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
1161 selection, VD_AGENT_CLIPBOARD_NONE, NULL, 0);
1164 void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint8_t selection,
1165 uint32_t *types, uint32_t type_count)
1169 if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) {
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);
1178 memcpy(x11->clipboard_agent_types[selection], types,
1179 type_count * sizeof(uint32_t));
1180 x11->clipboard_type_count[selection] = type_count;
1182 XSetSelectionOwner(x11->display, clip,
1183 x11->selection_window, CurrentTime);
1184 vdagent_x11_set_clipboard_owner(x11, selection, owner_client);
1186 /* Flush output buffers and consume any pending events */
1187 vdagent_x11_do_read(x11);
1190 void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint8_t selection,
1191 uint32_t type, uint8_t *data, uint32_t size)
1195 uint32_t type_from_event;
1197 if (x11->selection_req_data) {
1199 SELPRINTF("received clipboard data while still sending"
1200 " data from previous request, ignoring");
1206 if (!x11->selection_req) {
1208 SELPRINTF("received clipboard data without an "
1209 "outstanding selection request, ignoring");
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);
1225 if (type_from_event != type) {
1226 SELPRINTF("expecting type %u clipboard data got %u",
1227 type_from_event, type);
1229 vdagent_x11_send_selection_notify(x11, None, NULL);
1232 /* Flush output buffers and consume any pending events */
1233 vdagent_x11_do_read(x11);
1237 prop = event->xselectionrequest.property;
1239 prop = event->xselectionrequest.target;
1241 if (size > x11->max_prop_size) {
1242 unsigned long len = size;
1243 VSELPRINTF("Starting incr send of clipboard data");
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);
1258 SELPRINTF("clipboard data sent failed, requestor window gone");
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,
1266 if (vdagent_x11_restore_error_handler(x11) == 0)
1267 vdagent_x11_send_selection_notify(x11, prop, NULL);
1269 SELPRINTF("clipboard data sent failed, requestor window gone");
1274 /* Flush output buffers and consume any pending events */
1275 vdagent_x11_do_read(x11);
1278 void vdagent_x11_clipboard_release(struct vdagent_x11 *x11, uint8_t selection)
1283 if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) {
1287 if (x11->clipboard_owner[selection] != owner_client) {
1288 VSELPRINTF("received release while not owning client clipboard");
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,
1299 vdagent_x11_handle_event(x11, event);
1301 /* Note no need to do a set_clipboard_owner(owner_none) here, as that is
1302 already done by processing the XFixesSetSelectionOwnerNotify event. */
1304 /* Flush output buffers and consume any pending events */
1305 vdagent_x11_do_read(x11);
1308 void vdagent_x11_client_disconnected(struct vdagent_x11 *x11)
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);
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)
1325 const char * const wms_with_icons_on_desktop[] = {
1326 "Metacity", /* GNOME-2 or GNOME-3 fallback */
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]))