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"
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 x11
->timestamp_atom
= XInternAtom(x11
->display
, "TIMESTAMP", False
);
222 for(i
= 0; i
< clipboard_format_count
; i
++) {
223 x11
->clipboard_formats
[i
].type
= clipboard_format_templates
[i
].type
;
224 for(j
= 0; clipboard_format_templates
[i
].atom_names
[j
]; j
++) {
225 x11
->clipboard_formats
[i
].atoms
[j
] =
226 XInternAtom(x11
->display
,
227 clipboard_format_templates
[i
].atom_names
[j
],
230 x11
->clipboard_formats
[i
].atom_count
= j
;
233 /* We should not store properties (for selections) on the root window */
234 x11
->selection_window
= XCreateSimpleWindow(x11
->display
, x11
->root_window
[0],
235 0, 0, 1, 1, 0, 0, 0);
237 syslog(LOG_DEBUG
, "Selection window: %u", (int)x11
->selection_window
);
239 vdagent_x11_randr_init(x11
);
241 if (XFixesQueryExtension(x11
->display
, &x11
->xfixes_event_base
, &i
) &&
242 XFixesQueryVersion(x11
->display
, &major
, &minor
) && major
>= 1) {
244 XFixesSelectSelectionInput(x11
->display
, x11
->root_window
[0],
246 XFixesSetSelectionOwnerNotifyMask
|
247 XFixesSelectionWindowDestroyNotifyMask
|
248 XFixesSelectionClientCloseNotifyMask
);
249 XFixesSelectSelectionInput(x11
->display
, x11
->root_window
[0],
250 x11
->clipboard_primary_atom
,
251 XFixesSetSelectionOwnerNotifyMask
|
252 XFixesSelectionWindowDestroyNotifyMask
|
253 XFixesSelectionClientCloseNotifyMask
);
255 syslog(LOG_ERR
, "no xfixes, no guest -> client copy paste support");
257 x11
->max_prop_size
= XExtendedMaxRequestSize(x11
->display
);
258 if (x11
->max_prop_size
) {
259 x11
->max_prop_size
-= 100;
261 x11
->max_prop_size
= XMaxRequestSize(x11
->display
) - 100;
263 /* Be a good X11 citizen and maximize the amount of data we send at once */
264 if (x11
->max_prop_size
> 262144)
265 x11
->max_prop_size
= 262144;
267 for (i
= 0; i
< x11
->screen_count
; i
++) {
268 /* Catch resolution changes */
269 XSelectInput(x11
->display
, x11
->root_window
[i
], StructureNotifyMask
);
271 /* Get the current resolution */
272 XGetWindowAttributes(x11
->display
, x11
->root_window
[i
], &attrib
);
273 x11
->width
[i
] = attrib
.width
;
274 x11
->height
[i
] = attrib
.height
;
276 vdagent_x11_send_daemon_guest_xorg_res(x11
, 1);
278 /* Get net_wm_name, since we are started at the same time as the wm,
279 sometimes we need to wait a bit for it to show up. */
281 vdagent_x11_get_wm_name(x11
);
282 while (x11
->net_wm_name
== NULL
&& --i
> 0) {
284 vdagent_x11_get_wm_name(x11
);
286 if (x11
->debug
&& x11
->net_wm_name
)
287 syslog(LOG_DEBUG
, "net_wm_name: \"%s\", has icons: %d",
288 x11
->net_wm_name
, vdagent_x11_has_icons_on_desktop(x11
));
290 /* Flush output buffers and consume any pending events */
291 vdagent_x11_do_read(x11
);
296 void vdagent_x11_destroy(struct vdagent_x11
*x11
, int vdagentd_disconnected
)
303 if (vdagentd_disconnected
)
304 x11
->vdagentd
= NULL
;
306 for (sel
= 0; sel
< VD_AGENT_CLIPBOARD_SELECTION_SECONDARY
; ++sel
) {
307 vdagent_x11_set_clipboard_owner(x11
, sel
, owner_none
);
310 XCloseDisplay(x11
->display
);
311 g_free(x11
->net_wm_name
);
312 free(x11
->randr
.failed_conf
);
316 int vdagent_x11_get_fd(struct vdagent_x11
*x11
)
321 static void vdagent_x11_next_selection_request(struct vdagent_x11
*x11
)
323 struct vdagent_x11_selection_request
*selection_request
;
324 selection_request
= x11
->selection_req
;
325 x11
->selection_req
= selection_request
->next
;
326 free(selection_request
);
329 static void vdagent_x11_next_conversion_request(struct vdagent_x11
*x11
)
331 struct vdagent_x11_conversion_request
*conversion_req
;
332 conversion_req
= x11
->conversion_req
;
333 x11
->conversion_req
= conversion_req
->next
;
334 free(conversion_req
);
337 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11
*x11
,
338 uint8_t selection
, int new_owner
)
340 struct vdagent_x11_selection_request
*prev_sel
, *curr_sel
, *next_sel
;
341 struct vdagent_x11_conversion_request
*prev_conv
, *curr_conv
, *next_conv
;
344 /* Clear pending requests and clipboard data */
347 next_sel
= x11
->selection_req
;
350 next_sel
= curr_sel
->next
;
351 if (curr_sel
->selection
== selection
) {
353 SELPRINTF("selection requests pending on clipboard ownership "
357 vdagent_x11_send_selection_notify(x11
, None
, curr_sel
);
358 if (curr_sel
== x11
->selection_req
) {
359 x11
->selection_req
= next_sel
;
360 free(x11
->selection_req_data
);
361 x11
->selection_req_data
= NULL
;
362 x11
->selection_req_data_pos
= 0;
363 x11
->selection_req_data_size
= 0;
364 x11
->selection_req_atom
= None
;
366 prev_sel
->next
= next_sel
;
376 next_conv
= x11
->conversion_req
;
378 curr_conv
= next_conv
;
379 next_conv
= curr_conv
->next
;
380 if (curr_conv
->selection
== selection
) {
382 SELPRINTF("client clipboard request pending on clipboard "
383 "ownership change, clearing");
387 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_DATA
, selection
,
388 VD_AGENT_CLIPBOARD_NONE
, NULL
, 0);
389 if (curr_conv
== x11
->conversion_req
) {
390 x11
->conversion_req
= next_conv
;
391 x11
->clipboard_data_size
= 0;
392 x11
->expect_property_notify
= 0;
394 prev_conv
->next
= next_conv
;
398 prev_conv
= curr_conv
;
402 if (new_owner
== owner_none
) {
403 /* When going from owner_guest to owner_none we need to send a
404 clipboard release message to the client */
405 if (x11
->clipboard_owner
[selection
] == owner_guest
&& x11
->vdagentd
) {
406 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_RELEASE
, selection
,
409 x11
->clipboard_type_count
[selection
] = 0;
411 x11
->clipboard_owner
[selection
] = new_owner
;
414 static int vdagent_x11_get_clipboard_atom(struct vdagent_x11
*x11
, uint8_t selection
, Atom
* clipboard
)
416 if (selection
== VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD
) {
417 *clipboard
= x11
->clipboard_atom
;
418 } else if (selection
== VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
) {
419 *clipboard
= x11
->clipboard_primary_atom
;
421 syslog(LOG_ERR
, "get_clipboard_atom: unknown selection");
428 static int vdagent_x11_get_clipboard_selection(struct vdagent_x11
*x11
,
429 XEvent
*event
, uint8_t *selection
)
433 if (event
->type
== x11
->xfixes_event_base
) {
434 XFixesSelectionNotifyEvent
*xfev
= (XFixesSelectionNotifyEvent
*)event
;
435 atom
= xfev
->selection
;
436 } else if (event
->type
== SelectionNotify
) {
437 atom
= event
->xselection
.selection
;
438 } else if (event
->type
== SelectionRequest
) {
439 atom
= event
->xselectionrequest
.selection
;
441 syslog(LOG_ERR
, "get_clipboard_selection: unknown event type");
445 if (atom
== x11
->clipboard_atom
) {
446 *selection
= VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD
;
447 } else if (atom
== x11
->clipboard_primary_atom
) {
448 *selection
= VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
;
450 syslog(LOG_ERR
, "get_clipboard_selection: unknown selection");
457 static void vdagent_x11_handle_event(struct vdagent_x11
*x11
, XEvent event
)
462 if (event
.type
== x11
->xfixes_event_base
) {
465 XFixesSelectionNotifyEvent xfev
;
468 if (vdagent_x11_get_clipboard_selection(x11
, &event
, &selection
)) {
473 switch (ev
.xfev
.subtype
) {
474 case XFixesSetSelectionOwnerNotify
:
476 /* Treat ... as a SelectionOwnerNotify None */
477 case XFixesSelectionWindowDestroyNotify
:
478 case XFixesSelectionClientCloseNotify
:
479 ev
.xfev
.owner
= None
;
482 VSELPRINTF("unexpected xfix event subtype %d window %d",
483 (int)ev
.xfev
.subtype
, (int)event
.xany
.window
);
486 VSELPRINTF("New selection owner: %u", (unsigned int)ev
.xfev
.owner
);
488 /* Ignore becoming the owner ourselves */
489 if (ev
.xfev
.owner
== x11
->selection_window
)
492 /* If the clipboard owner is changed we no longer own it */
493 vdagent_x11_set_clipboard_owner(x11
, selection
, owner_none
);
495 if (ev
.xfev
.owner
== None
)
498 /* Request the supported targets from the new owner */
499 XConvertSelection(x11
->display
, ev
.xfev
.selection
, x11
->targets_atom
,
500 x11
->targets_atom
, x11
->selection_window
,
502 x11
->expected_targets_notifies
[selection
]++;
506 if (vdagent_x11_randr_handle_event(x11
, event
))
509 switch (event
.type
) {
510 case ConfigureNotify
:
511 for (i
= 0; i
< x11
->screen_count
; i
++)
512 if (event
.xconfigure
.window
== x11
->root_window
[i
])
514 if (i
== x11
->screen_count
)
518 vdagent_x11_randr_handle_root_size_change(x11
, i
,
519 event
.xconfigure
.width
, event
.xconfigure
.height
);
522 /* These are uninteresting */
525 case SelectionNotify
:
526 if (event
.xselection
.target
== x11
->targets_atom
)
527 vdagent_x11_handle_targets_notify(x11
, &event
);
529 vdagent_x11_handle_selection_notify(x11
, &event
, 0);
534 if (x11
->expect_property_notify
&&
535 event
.xproperty
.state
== PropertyNewValue
) {
536 vdagent_x11_handle_selection_notify(x11
, &event
, 1);
538 if (x11
->selection_req_data
&&
539 event
.xproperty
.state
== PropertyDelete
) {
540 vdagent_x11_handle_property_delete_notify(x11
, &event
);
542 /* Always mark as handled, since we cannot unselect input for property
543 notifications once we are done with handling the incr transfer. */
547 /* Do nothing the clipboard ownership will get updated through
548 the XFixesSetSelectionOwnerNotify event */
551 case SelectionRequest
: {
552 struct vdagent_x11_selection_request
*req
, *new_req
;
554 if (vdagent_x11_get_clipboard_selection(x11
, &event
, &selection
)) {
558 new_req
= malloc(sizeof(*new_req
));
560 SELPRINTF("out of memory on SelectionRequest, ignoring.");
566 new_req
->event
= event
;
567 new_req
->selection
= selection
;
568 new_req
->next
= NULL
;
570 if (!x11
->selection_req
) {
571 x11
->selection_req
= new_req
;
572 vdagent_x11_handle_selection_request(x11
);
576 /* maybe we should limit the selection_request stack depth ? */
577 req
= x11
->selection_req
;
585 if (!handled
&& x11
->debug
)
586 syslog(LOG_DEBUG
, "unhandled x11 event, type %d, window %d",
587 (int)event
.type
, (int)event
.xany
.window
);
590 void vdagent_x11_do_read(struct vdagent_x11
*x11
)
594 while (XPending(x11
->display
)) {
595 XNextEvent(x11
->display
, &event
);
596 vdagent_x11_handle_event(x11
, event
);
600 static const char *vdagent_x11_get_atom_name(struct vdagent_x11
*x11
, Atom a
)
605 return XGetAtomName(x11
->display
, a
);
608 static int vdagent_x11_get_selection(struct vdagent_x11
*x11
, XEvent
*event
,
609 uint8_t selection
, Atom type
, Atom prop
, int format
,
610 unsigned char **data_ret
, int incr
)
612 Bool del
= incr
? True
: False
;
614 int format_ret
, ret_val
= -1;
615 unsigned long len
, remain
;
616 unsigned char *data
= NULL
;
621 if (event
->xselection
.property
== None
) {
622 VSELPRINTF("XConvertSelection refused by clipboard owner");
626 if (event
->xselection
.requestor
!= x11
->selection_window
||
627 event
->xselection
.property
!= prop
) {
628 SELPRINTF("SelectionNotify parameters mismatch");
633 if (XGetWindowProperty(x11
->display
, x11
->selection_window
, prop
, 0,
634 LONG_MAX
, del
, type
, &type_ret
, &format_ret
, &len
,
635 &remain
, &data
) != Success
) {
636 SELPRINTF("XGetWindowProperty failed");
640 if (!incr
&& prop
!= x11
->targets_atom
) {
641 if (type_ret
== x11
->incr_atom
) {
642 int prop_min_size
= *(uint32_t*)data
;
644 if (x11
->expect_property_notify
) {
645 SELPRINTF("received an incr SelectionNotify while "
646 "still reading another incr property");
650 if (x11
->clipboard_data_space
< prop_min_size
) {
651 free(x11
->clipboard_data
);
652 x11
->clipboard_data
= malloc(prop_min_size
);
653 if (!x11
->clipboard_data
) {
654 SELPRINTF("out of memory allocating clipboard buffer");
655 x11
->clipboard_data_space
= 0;
658 x11
->clipboard_data_space
= prop_min_size
;
660 x11
->expect_property_notify
= 1;
661 XSelectInput(x11
->display
, x11
->selection_window
,
663 XDeleteProperty(x11
->display
, x11
->selection_window
, prop
);
665 return 0; /* Wait for more data */
667 XDeleteProperty(x11
->display
, x11
->selection_window
, prop
);
670 if (type_ret
!= type
) {
671 SELPRINTF("expected property type: %s, got: %s",
672 vdagent_x11_get_atom_name(x11
, type
),
673 vdagent_x11_get_atom_name(x11
, type_ret
));
677 if (format_ret
!= format
) {
678 SELPRINTF("expected %d bit format, got %d bits", format
, format_ret
);
682 /* Convert len to bytes */
687 len
*= sizeof(short);
696 if (x11
->clipboard_data_size
+ len
> x11
->clipboard_data_space
) {
697 void *old_clipboard_data
= x11
->clipboard_data
;
699 x11
->clipboard_data_space
= x11
->clipboard_data_size
+ len
;
700 x11
->clipboard_data
= realloc(x11
->clipboard_data
,
701 x11
->clipboard_data_space
);
702 if (!x11
->clipboard_data
) {
703 SELPRINTF("out of memory allocating clipboard buffer");
704 x11
->clipboard_data_space
= 0;
705 free(old_clipboard_data
);
709 memcpy(x11
->clipboard_data
+ x11
->clipboard_data_size
, data
, len
);
710 x11
->clipboard_data_size
+= len
;
711 VSELPRINTF("Appended %ld bytes to buffer", len
);
713 return 0; /* Wait for more data */
715 len
= x11
->clipboard_data_size
;
716 *data_ret
= x11
->clipboard_data
;
723 SELPRINTF("property contains no data (zero length)");
728 if ((incr
|| ret_val
== -1) && data
)
732 x11
->clipboard_data_size
= 0;
733 x11
->expect_property_notify
= 0;
739 static void vdagent_x11_get_selection_free(struct vdagent_x11
*x11
,
740 unsigned char *data
, int incr
)
743 /* If the clipboard has grown large return the memory to the system */
744 if (x11
->clipboard_data_space
> 512 * 1024) {
745 free(x11
->clipboard_data
);
746 x11
->clipboard_data
= NULL
;
747 x11
->clipboard_data_space
= 0;
753 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11
*x11
,
754 uint8_t selection
, Atom target
)
758 for (i
= 0; i
< clipboard_format_count
; i
++) {
759 for (j
= 0; j
< x11
->clipboard_formats
[i
].atom_count
; j
++) {
760 if (x11
->clipboard_formats
[i
].atoms
[j
] == target
) {
761 return x11
->clipboard_formats
[i
].type
;
766 VSELPRINTF("unexpected selection type %s",
767 vdagent_x11_get_atom_name(x11
, target
));
768 return VD_AGENT_CLIPBOARD_NONE
;
771 static Atom
vdagent_x11_type_to_target(struct vdagent_x11
*x11
,
772 uint8_t selection
, uint32_t type
)
776 for (i
= 0; i
< x11
->clipboard_type_count
[selection
]; i
++) {
777 if (x11
->clipboard_agent_types
[selection
][i
] == type
) {
778 return x11
->clipboard_x11_targets
[selection
][i
];
781 SELPRINTF("client requested unavailable type %u", type
);
785 static void vdagent_x11_handle_conversion_request(struct vdagent_x11
*x11
)
789 if (!x11
->conversion_req
) {
793 vdagent_x11_get_clipboard_atom(x11
, x11
->conversion_req
->selection
, &clip
);
794 XConvertSelection(x11
->display
, clip
, x11
->conversion_req
->target
,
795 clip
, x11
->selection_window
, CurrentTime
);
798 static void vdagent_x11_handle_selection_notify(struct vdagent_x11
*x11
,
799 XEvent
*event
, int incr
)
802 unsigned char *data
= NULL
;
804 uint8_t selection
= -1;
807 if (!x11
->conversion_req
) {
808 syslog(LOG_ERR
, "SelectionNotify received without a target");
811 vdagent_x11_get_clipboard_atom(x11
, x11
->conversion_req
->selection
, &clip
);
814 if (event
->xproperty
.atom
!= clip
||
815 event
->xproperty
.window
!= x11
->selection_window
) {
819 if (vdagent_x11_get_clipboard_selection(x11
, event
, &selection
)) {
821 } else if (selection
!= x11
->conversion_req
->selection
) {
822 SELPRINTF("Requested data for selection %d got %d",
823 (int)x11
->conversion_req
->selection
, (int)selection
);
826 if (event
->xselection
.target
!= x11
->conversion_req
->target
&&
827 event
->xselection
.target
!= x11
->incr_atom
) {
828 SELPRINTF("Requested %s target got %s",
829 vdagent_x11_get_atom_name(x11
, x11
->conversion_req
->target
),
830 vdagent_x11_get_atom_name(x11
, event
->xselection
.target
));
835 selection
= x11
->conversion_req
->selection
;
836 type
= vdagent_x11_target_to_type(x11
, selection
,
837 x11
->conversion_req
->target
);
838 if (type
== VD_AGENT_CLIPBOARD_NONE
)
839 SELPRINTF("internal error conversion_req has bad target %s",
840 vdagent_x11_get_atom_name(x11
, x11
->conversion_req
->target
));
841 if (len
== 0) { /* No errors so far */
842 len
= vdagent_x11_get_selection(x11
, event
, selection
,
843 x11
->conversion_req
->target
,
844 clip
, 8, &data
, incr
);
845 if (len
== 0) { /* waiting for more data? */
850 type
= VD_AGENT_CLIPBOARD_NONE
;
854 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_DATA
, selection
, type
,
856 vdagent_x11_get_selection_free(x11
, data
, incr
);
858 vdagent_x11_next_conversion_request(x11
);
859 vdagent_x11_handle_conversion_request(x11
);
862 static Atom
atom_lists_overlap(Atom
*atoms1
, Atom
*atoms2
, int l1
, int l2
)
866 for (i
= 0; i
< l1
; i
++)
867 for (j
= 0; j
< l2
; j
++)
868 if (atoms1
[i
] == atoms2
[j
])
874 static void vdagent_x11_print_targets(struct vdagent_x11
*x11
,
875 uint8_t selection
, const char *action
, Atom
*atoms
, int c
)
878 VSELPRINTF("%s %d targets:", action
, c
);
879 for (i
= 0; i
< c
; i
++)
880 VSELPRINTF("%s", vdagent_x11_get_atom_name(x11
, atoms
[i
]));
883 static void vdagent_x11_handle_targets_notify(struct vdagent_x11
*x11
,
887 Atom atom
, *atoms
= NULL
;
891 if (vdagent_x11_get_clipboard_selection(x11
, event
, &selection
)) {
895 if (!x11
->expected_targets_notifies
[selection
]) {
896 SELPRINTF("unexpected selection notify TARGETS");
900 x11
->expected_targets_notifies
[selection
]--;
902 /* If we have more targets_notifies pending, ignore this one, we
903 are only interested in the targets list of the current owner
904 (which is the last one we've requested a targets list from) */
905 if (x11
->expected_targets_notifies
[selection
]) {
909 len
= vdagent_x11_get_selection(x11
, event
, selection
,
910 XA_ATOM
, x11
->targets_atom
, 32,
911 (unsigned char **)&atoms
, 0);
912 if (len
== 0 || len
== -1) /* waiting for more data or error? */
917 vdagent_x11_print_targets(x11
, selection
, "received", atoms
, len
);
919 type_count
= &x11
->clipboard_type_count
[selection
];
921 for (i
= 0; i
< clipboard_format_count
; i
++) {
922 atom
= atom_lists_overlap(x11
->clipboard_formats
[i
].atoms
, atoms
,
923 x11
->clipboard_formats
[i
].atom_count
, len
);
925 x11
->clipboard_agent_types
[selection
][*type_count
] =
926 x11
->clipboard_formats
[i
].type
;
927 x11
->clipboard_x11_targets
[selection
][*type_count
] = atom
;
930 sizeof(x11
->clipboard_agent_types
[0])/sizeof(uint32_t)) {
931 SELPRINTF("handle_targets_notify: too many types");
938 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_GRAB
, selection
, 0,
939 (uint8_t *)x11
->clipboard_agent_types
[selection
],
940 *type_count
* sizeof(uint32_t));
941 vdagent_x11_set_clipboard_owner(x11
, selection
, owner_guest
);
944 vdagent_x11_get_selection_free(x11
, (unsigned char *)atoms
, 0);
947 static void vdagent_x11_send_selection_notify(struct vdagent_x11
*x11
,
948 Atom prop
, struct vdagent_x11_selection_request
*request
)
953 event
= &request
->event
;
955 event
= &x11
->selection_req
->event
;
958 res
.xselection
.property
= prop
;
959 res
.xselection
.type
= SelectionNotify
;
960 res
.xselection
.display
= event
->xselectionrequest
.display
;
961 res
.xselection
.requestor
= event
->xselectionrequest
.requestor
;
962 res
.xselection
.selection
= event
->xselectionrequest
.selection
;
963 res
.xselection
.target
= event
->xselectionrequest
.target
;
964 res
.xselection
.time
= event
->xselectionrequest
.time
;
966 vdagent_x11_set_error_handler(x11
, vdagent_x11_ignore_bad_window_handler
);
967 XSendEvent(x11
->display
, event
->xselectionrequest
.requestor
, 0, 0, &res
);
968 vdagent_x11_restore_error_handler(x11
);
971 vdagent_x11_next_selection_request(x11
);
972 vdagent_x11_handle_selection_request(x11
);
976 static void vdagent_x11_send_targets(struct vdagent_x11
*x11
,
977 uint8_t selection
, XEvent
*event
)
979 Atom prop
, targets
[256] = { x11
->targets_atom
, };
980 int i
, j
, k
, target_count
= 1;
982 for (i
= 0; i
< x11
->clipboard_type_count
[selection
]; i
++) {
983 for (j
= 0; j
< clipboard_format_count
; j
++) {
984 if (x11
->clipboard_formats
[j
].type
!=
985 x11
->clipboard_agent_types
[selection
][i
])
988 for (k
= 0; k
< x11
->clipboard_formats
[j
].atom_count
; k
++) {
989 targets
[target_count
] = x11
->clipboard_formats
[j
].atoms
[k
];
991 if (target_count
== sizeof(targets
)/sizeof(Atom
)) {
992 SELPRINTF("send_targets: too many targets");
1000 prop
= event
->xselectionrequest
.property
;
1002 prop
= event
->xselectionrequest
.target
;
1004 vdagent_x11_set_error_handler(x11
, vdagent_x11_ignore_bad_window_handler
);
1005 XChangeProperty(x11
->display
, event
->xselectionrequest
.requestor
, prop
,
1006 XA_ATOM
, 32, PropModeReplace
, (unsigned char *)&targets
,
1008 if (vdagent_x11_restore_error_handler(x11
) == 0) {
1009 vdagent_x11_print_targets(x11
, selection
, "sent",
1010 targets
, target_count
);
1011 vdagent_x11_send_selection_notify(x11
, prop
, NULL
);
1013 SELPRINTF("send_targets: Failed to sent, requestor window gone");
1016 static void vdagent_x11_handle_selection_request(struct vdagent_x11
*x11
)
1019 uint32_t type
= VD_AGENT_CLIPBOARD_NONE
;
1022 if (!x11
->selection_req
)
1025 event
= &x11
->selection_req
->event
;
1026 selection
= x11
->selection_req
->selection
;
1028 if (x11
->clipboard_owner
[selection
] != owner_client
) {
1029 SELPRINTF("received selection request event for target %s, "
1030 "while not owning client clipboard",
1031 vdagent_x11_get_atom_name(x11
, event
->xselectionrequest
.target
));
1032 vdagent_x11_send_selection_notify(x11
, None
, NULL
);
1036 if (event
->xselectionrequest
.target
== x11
->multiple_atom
) {
1037 SELPRINTF("multiple target not supported");
1038 vdagent_x11_send_selection_notify(x11
, None
, NULL
);
1042 if (event
->xselectionrequest
.target
== x11
->timestamp_atom
) {
1043 /* TODO: use more accurate selection time */
1044 guint32 timestamp
= event
->xselectionrequest
.time
;
1046 XChangeProperty(x11
->display
, event
->xselectionrequest
.requestor
,
1047 event
->xselectionrequest
.property
,
1048 event
->xselectionrequest
.target
, 32, PropModeReplace
,
1049 (guint8
*)×tamp
, 1);
1050 vdagent_x11_send_selection_notify(x11
,
1051 event
->xselectionrequest
.property
, NULL
);
1056 if (event
->xselectionrequest
.target
== x11
->targets_atom
) {
1057 vdagent_x11_send_targets(x11
, selection
, event
);
1061 type
= vdagent_x11_target_to_type(x11
, selection
,
1062 event
->xselectionrequest
.target
);
1063 if (type
== VD_AGENT_CLIPBOARD_NONE
) {
1064 VSELPRINTF("guest app requested a non-advertised target");
1065 vdagent_x11_send_selection_notify(x11
, None
, NULL
);
1069 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_REQUEST
, selection
, type
,
1073 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11
*x11
,
1080 assert(x11
->selection_req
);
1081 sel_event
= &x11
->selection_req
->event
;
1082 selection
= x11
->selection_req
->selection
;
1083 if (del_event
->xproperty
.window
!= sel_event
->xselectionrequest
.requestor
1084 || del_event
->xproperty
.atom
!= x11
->selection_req_atom
) {
1088 len
= x11
->selection_req_data_size
- x11
->selection_req_data_pos
;
1089 if (len
> x11
->max_prop_size
) {
1090 len
= x11
->max_prop_size
;
1094 VSELPRINTF("Sending %d-%d/%d bytes of clipboard data",
1095 x11
->selection_req_data_pos
,
1096 x11
->selection_req_data_pos
+ len
- 1,
1097 x11
->selection_req_data_size
);
1099 VSELPRINTF("Ending incr send of clipboard data");
1101 vdagent_x11_set_error_handler(x11
, vdagent_x11_ignore_bad_window_handler
);
1102 XChangeProperty(x11
->display
, sel_event
->xselectionrequest
.requestor
,
1103 x11
->selection_req_atom
,
1104 sel_event
->xselectionrequest
.target
, 8, PropModeReplace
,
1105 x11
->selection_req_data
+ x11
->selection_req_data_pos
,
1107 if (vdagent_x11_restore_error_handler(x11
)) {
1108 SELPRINTF("incr sent failed, requestor window gone");
1112 x11
->selection_req_data_pos
+= len
;
1114 /* Note we must explictly send a 0 sized XChangeProperty to signal the
1115 incr transfer is done. Hence we do not check if we've send all data
1116 but instead check we've send the final 0 sized XChangeProperty. */
1118 free(x11
->selection_req_data
);
1119 x11
->selection_req_data
= NULL
;
1120 x11
->selection_req_data_pos
= 0;
1121 x11
->selection_req_data_size
= 0;
1122 x11
->selection_req_atom
= None
;
1123 vdagent_x11_next_selection_request(x11
);
1124 vdagent_x11_handle_selection_request(x11
);
1128 void vdagent_x11_clipboard_request(struct vdagent_x11
*x11
,
1129 uint8_t selection
, uint32_t type
)
1132 struct vdagent_x11_conversion_request
*req
, *new_req
;
1134 /* We don't use clip here, but we call get_clipboard_atom to verify
1135 selection is valid */
1136 if (vdagent_x11_get_clipboard_atom(x11
, selection
, &clip
)) {
1140 if (x11
->clipboard_owner
[selection
] != owner_guest
) {
1141 SELPRINTF("received clipboard req while not owning guest clipboard");
1145 target
= vdagent_x11_type_to_target(x11
, selection
, type
);
1146 if (target
== None
) {
1150 new_req
= malloc(sizeof(*new_req
));
1152 SELPRINTF("out of memory on client clipboard request, ignoring.");
1156 new_req
->target
= target
;
1157 new_req
->selection
= selection
;
1158 new_req
->next
= NULL
;
1160 if (!x11
->conversion_req
) {
1161 x11
->conversion_req
= new_req
;
1162 vdagent_x11_handle_conversion_request(x11
);
1163 /* Flush output buffers and consume any pending events */
1164 vdagent_x11_do_read(x11
);
1168 /* maybe we should limit the conversion_request stack depth ? */
1169 req
= x11
->conversion_req
;
1173 req
->next
= new_req
;
1177 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_DATA
,
1178 selection
, VD_AGENT_CLIPBOARD_NONE
, NULL
, 0);
1181 void vdagent_x11_clipboard_grab(struct vdagent_x11
*x11
, uint8_t selection
,
1182 uint32_t *types
, uint32_t type_count
)
1186 if (vdagent_x11_get_clipboard_atom(x11
, selection
, &clip
)) {
1190 if (type_count
> sizeof(x11
->clipboard_agent_types
[0])/sizeof(uint32_t)) {
1191 SELPRINTF("x11_clipboard_grab: too many types");
1192 type_count
= sizeof(x11
->clipboard_agent_types
[0])/sizeof(uint32_t);
1195 memcpy(x11
->clipboard_agent_types
[selection
], types
,
1196 type_count
* sizeof(uint32_t));
1197 x11
->clipboard_type_count
[selection
] = type_count
;
1199 XSetSelectionOwner(x11
->display
, clip
,
1200 x11
->selection_window
, CurrentTime
);
1201 vdagent_x11_set_clipboard_owner(x11
, selection
, owner_client
);
1203 /* Flush output buffers and consume any pending events */
1204 vdagent_x11_do_read(x11
);
1207 void vdagent_x11_clipboard_data(struct vdagent_x11
*x11
, uint8_t selection
,
1208 uint32_t type
, uint8_t *data
, uint32_t size
)
1212 uint32_t type_from_event
;
1214 if (x11
->selection_req_data
) {
1216 SELPRINTF("received clipboard data while still sending"
1217 " data from previous request, ignoring");
1222 if (!x11
->selection_req
) {
1224 SELPRINTF("received clipboard data without an "
1225 "outstanding selection request, ignoring");
1230 event
= &x11
->selection_req
->event
;
1231 type_from_event
= vdagent_x11_target_to_type(x11
,
1232 x11
->selection_req
->selection
,
1233 event
->xselectionrequest
.target
);
1234 if (type_from_event
!= type
||
1235 selection
!= x11
->selection_req
->selection
) {
1236 if (selection
!= x11
->selection_req
->selection
) {
1237 SELPRINTF("expecting data for selection %d got %d",
1238 (int)x11
->selection_req
->selection
, (int)selection
);
1240 if (type_from_event
!= type
) {
1241 SELPRINTF("expecting type %u clipboard data got %u",
1242 type_from_event
, type
);
1244 vdagent_x11_send_selection_notify(x11
, None
, NULL
);
1246 /* Flush output buffers and consume any pending events */
1247 vdagent_x11_do_read(x11
);
1251 prop
= event
->xselectionrequest
.property
;
1253 prop
= event
->xselectionrequest
.target
;
1255 if (size
> x11
->max_prop_size
) {
1256 unsigned long len
= size
;
1257 VSELPRINTF("Starting incr send of clipboard data");
1259 vdagent_x11_set_error_handler(x11
, vdagent_x11_ignore_bad_window_handler
);
1260 XSelectInput(x11
->display
, event
->xselectionrequest
.requestor
,
1261 PropertyChangeMask
);
1262 XChangeProperty(x11
->display
, event
->xselectionrequest
.requestor
, prop
,
1263 x11
->incr_atom
, 32, PropModeReplace
,
1264 (unsigned char*)&len
, 1);
1265 if (vdagent_x11_restore_error_handler(x11
) == 0) {
1266 /* duplicate data */
1267 x11
->selection_req_data
= malloc(size
);
1268 if (x11
->selection_req_data
!= NULL
) {
1269 memcpy(x11
->selection_req_data
, data
, size
);
1270 x11
->selection_req_data_pos
= 0;
1271 x11
->selection_req_data_size
= size
;
1272 x11
->selection_req_atom
= prop
;
1273 vdagent_x11_send_selection_notify(x11
, prop
, x11
->selection_req
);
1275 SELPRINTF("out of memory allocating selection buffer");
1278 SELPRINTF("clipboard data sent failed, requestor window gone");
1281 vdagent_x11_set_error_handler(x11
, vdagent_x11_ignore_bad_window_handler
);
1282 XChangeProperty(x11
->display
, event
->xselectionrequest
.requestor
, prop
,
1283 event
->xselectionrequest
.target
, 8, PropModeReplace
,
1285 if (vdagent_x11_restore_error_handler(x11
) == 0)
1286 vdagent_x11_send_selection_notify(x11
, prop
, NULL
);
1288 SELPRINTF("clipboard data sent failed, requestor window gone");
1291 /* Flush output buffers and consume any pending events */
1292 vdagent_x11_do_read(x11
);
1295 void vdagent_x11_clipboard_release(struct vdagent_x11
*x11
, uint8_t selection
)
1300 if (vdagent_x11_get_clipboard_atom(x11
, selection
, &clip
)) {
1304 if (x11
->clipboard_owner
[selection
] != owner_client
) {
1305 VSELPRINTF("received release while not owning client clipboard");
1309 XSetSelectionOwner(x11
->display
, clip
, None
, CurrentTime
);
1310 /* Make sure we process the XFixesSetSelectionOwnerNotify event caused
1311 by this, so we don't end up changing the clipboard owner to none, after
1312 it has already been re-owned because this event is still pending. */
1313 XSync(x11
->display
, False
);
1314 while (XCheckTypedEvent(x11
->display
, x11
->xfixes_event_base
,
1316 vdagent_x11_handle_event(x11
, event
);
1318 /* Note no need to do a set_clipboard_owner(owner_none) here, as that is
1319 already done by processing the XFixesSetSelectionOwnerNotify event. */
1321 /* Flush output buffers and consume any pending events */
1322 vdagent_x11_do_read(x11
);
1325 void vdagent_x11_client_disconnected(struct vdagent_x11
*x11
)
1329 for (sel
= 0; sel
< VD_AGENT_CLIPBOARD_SELECTION_SECONDARY
; sel
++) {
1330 if (x11
->clipboard_owner
[sel
] == owner_client
)
1331 vdagent_x11_clipboard_release(x11
, sel
);
1335 /* Function used to determine the default location to save file-xfers,
1336 xdg desktop dir or xdg download dir. We error on the save side and use a
1337 whitelist approach, so any unknown desktops will end up with saving
1338 file-xfers to the xdg download dir, and opening the xdg download dir with
1339 xdg-open when the file-xfer completes. */
1340 int vdagent_x11_has_icons_on_desktop(struct vdagent_x11
*x11
)
1342 const char * const wms_with_icons_on_desktop
[] = {
1343 "Metacity", /* GNOME-2 or GNOME-3 fallback */
1350 if (x11
->net_wm_name
)
1351 for (i
= 0; wms_with_icons_on_desktop
[i
]; i
++)
1352 if (!strcmp(x11
->net_wm_name
, wms_with_icons_on_desktop
[i
]))