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. */
38 #include <X11/Xatom.h>
40 #include <X11/extensions/Xrandr.h>
41 #include <X11/extensions/Xfixes.h>
42 #include "vdagentd-proto.h"
43 #include "vdagent-x11.h"
45 /* Macros to print a message to the logfile prefixed by the selection */
46 #define SELPRINTF(format, ...) \
47 fprintf(x11->errfile, "%s: " format, \
48 vdagent_x11_sel_to_str(selection), ##__VA_ARGS__)
50 #define VSELPRINTF(format, ...) \
53 fprintf(x11->errfile, "%s: " format, \
54 vdagent_x11_sel_to_str(selection), ##__VA_ARGS__); \
58 enum { owner_none
, owner_guest
, owner_client
};
60 /* X11 terminology is confusing a selection request is a request from an
61 app to get clipboard data from us, so iow from the spice client through
62 the vdagent channel. We handle these one at a time and queue any which
63 come in while we are still handling the current one. */
64 struct vdagent_x11_selection_request
{
67 struct vdagent_x11_selection_request
*next
;
70 /* A conversion request is X11 speak for asking an other app to give its
71 clipboard data to us, we do these on behalf of the spice client to copy
72 data from the guest to the client. Like selection requests we process
73 these one at a time. */
74 struct vdagent_x11_conversion_request
{
77 struct vdagent_x11_conversion_request
*next
;
80 struct clipboard_format_tmpl
{
82 const char *atom_names
[16];
85 struct clipboard_format_info
{
91 static const struct clipboard_format_tmpl clipboard_format_templates
[] = {
92 { VD_AGENT_CLIPBOARD_UTF8_TEXT
, { "UTF8_STRING",
93 "text/plain;charset=UTF-8", "text/plain;charset=utf-8", NULL
}, },
94 { VD_AGENT_CLIPBOARD_IMAGE_PNG
, { "image/png", NULL
}, },
95 { VD_AGENT_CLIPBOARD_IMAGE_BMP
, { "image/bmp", "image/x-bmp",
96 "image/x-MS-bmp", "image/x-win-bitmap", NULL
}, },
97 { VD_AGENT_CLIPBOARD_IMAGE_TIFF
, { "image/tiff", NULL
}, },
98 { VD_AGENT_CLIPBOARD_IMAGE_JPG
, { "image/jpeg", NULL
}, },
101 #define clipboard_format_count (sizeof(clipboard_format_templates)/sizeof(clipboard_format_templates[0]))
104 struct clipboard_format_info clipboard_formats
[clipboard_format_count
];
107 Atom clipboard_primary_atom
;
112 Window selection_window
;
113 struct udscs_connection
*vdagentd
;
122 int xfixes_event_base
;
124 int expected_targets_notifies
[256];
125 int clipboard_owner
[256];
126 int clipboard_type_count
[256];
127 uint32_t clipboard_agent_types
[256][256];
128 Atom clipboard_x11_targets
[256][256];
129 /* Data for conversion_req which is currently being processed */
130 struct vdagent_x11_conversion_request
*conversion_req
;
131 int expect_property_notify
;
132 uint8_t *clipboard_data
;
133 uint32_t clipboard_data_size
;
134 uint32_t clipboard_data_space
;
135 /* Data for selection_req which is currently being processed */
136 struct vdagent_x11_selection_request
*selection_req
;
137 uint8_t *selection_req_data
;
138 uint32_t selection_req_data_pos
;
139 uint32_t selection_req_data_size
;
140 Atom selection_req_atom
;
143 static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11
*x11
);
144 static void vdagent_x11_handle_selection_notify(struct vdagent_x11
*x11
,
145 XEvent
*event
, int incr
);
146 static void vdagent_x11_handle_selection_request(struct vdagent_x11
*x11
);
147 static void vdagent_x11_handle_targets_notify(struct vdagent_x11
*x11
,
149 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11
*x11
,
151 static void vdagent_x11_send_selection_notify(struct vdagent_x11
*x11
,
152 Atom prop
, struct vdagent_x11_selection_request
*request
);
153 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11
*x11
,
154 uint8_t selection
, int new_owner
);
156 static const char *vdagent_x11_sel_to_str(uint8_t selection
) {
158 case VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD
:
160 case VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
:
162 case VD_AGENT_CLIPBOARD_SELECTION_SECONDARY
:
169 struct vdagent_x11
*vdagent_x11_create(struct udscs_connection
*vdagentd
,
170 FILE *errfile
, int verbose
)
172 struct vdagent_x11
*x11
;
173 XWindowAttributes attrib
;
174 int i
, j
, major
, minor
;
176 x11
= calloc(1, sizeof(*x11
));
178 fprintf(errfile
, "out of memory allocating vdagent_x11 struct\n");
182 x11
->vdagentd
= vdagentd
;
183 x11
->errfile
= errfile
;
184 x11
->verbose
= verbose
;
186 x11
->display
= XOpenDisplay(NULL
);
188 fprintf(x11
->errfile
, "could not connect to X-server\n");
193 x11
->screen
= DefaultScreen(x11
->display
);
194 x11
->root_window
= RootWindow(x11
->display
, x11
->screen
);
195 x11
->fd
= ConnectionNumber(x11
->display
);
196 x11
->clipboard_atom
= XInternAtom(x11
->display
, "CLIPBOARD", False
);
197 x11
->clipboard_primary_atom
= XInternAtom(x11
->display
, "PRIMARY", False
);
198 x11
->targets_atom
= XInternAtom(x11
->display
, "TARGETS", False
);
199 x11
->incr_atom
= XInternAtom(x11
->display
, "INCR", False
);
200 x11
->multiple_atom
= XInternAtom(x11
->display
, "MULTIPLE", False
);
201 for(i
= 0; i
< clipboard_format_count
; i
++) {
202 x11
->clipboard_formats
[i
].type
= clipboard_format_templates
[i
].type
;
203 for(j
= 0; clipboard_format_templates
[i
].atom_names
[j
]; j
++) {
204 x11
->clipboard_formats
[i
].atoms
[j
] =
205 XInternAtom(x11
->display
,
206 clipboard_format_templates
[i
].atom_names
[j
],
209 x11
->clipboard_formats
[i
].atom_count
= j
;
212 /* We should not store properties (for selections) on the root window */
213 x11
->selection_window
= XCreateSimpleWindow(x11
->display
, x11
->root_window
,
214 0, 0, 1, 1, 0, 0, 0);
216 fprintf(x11
->errfile
, "Selection window: %u\n",
217 (unsigned int)x11
->selection_window
);
219 if (XRRQueryExtension(x11
->display
, &i
, &i
))
222 fprintf(x11
->errfile
, "no xrandr\n");
224 if (XFixesQueryExtension(x11
->display
, &x11
->xfixes_event_base
, &i
) &&
225 XFixesQueryVersion(x11
->display
, &major
, &minor
) && major
>= 1) {
227 XFixesSelectSelectionInput(x11
->display
, x11
->root_window
,
229 XFixesSetSelectionOwnerNotifyMask
|
230 XFixesSelectionWindowDestroyNotifyMask
|
231 XFixesSelectionClientCloseNotifyMask
);
232 XFixesSelectSelectionInput(x11
->display
, x11
->root_window
,
233 x11
->clipboard_primary_atom
,
234 XFixesSetSelectionOwnerNotifyMask
|
235 XFixesSelectionWindowDestroyNotifyMask
|
236 XFixesSelectionClientCloseNotifyMask
);
238 fprintf(x11
->errfile
,
239 "no xfixes, no guest -> client copy paste support\n");
241 x11
->max_prop_size
= XExtendedMaxRequestSize(x11
->display
);
242 if (x11
->max_prop_size
) {
243 x11
->max_prop_size
-= 100;
245 x11
->max_prop_size
= XMaxRequestSize(x11
->display
) - 100;
247 /* Be a good X11 citizen and maximize the amount of data we send at once */
248 if (x11
->max_prop_size
> 262144)
249 x11
->max_prop_size
= 262144;
251 /* Catch resolution changes */
252 XSelectInput(x11
->display
, x11
->root_window
, StructureNotifyMask
);
254 /* Get the current resolution */
255 XGetWindowAttributes(x11
->display
, x11
->root_window
, &attrib
);
256 x11
->width
= attrib
.width
;
257 x11
->height
= attrib
.height
;
258 vdagent_x11_send_daemon_guest_xorg_res(x11
);
260 /* Flush output buffers and consume any pending events */
261 vdagent_x11_do_read(x11
);
266 void vdagent_x11_destroy(struct vdagent_x11
*x11
)
273 for (sel
= 0; sel
< VD_AGENT_CLIPBOARD_SELECTION_SECONDARY
; ++sel
) {
274 vdagent_x11_set_clipboard_owner(x11
, sel
, owner_none
);
277 XCloseDisplay(x11
->display
);
281 int vdagent_x11_get_fd(struct vdagent_x11
*x11
)
286 static void vdagent_x11_next_selection_request(struct vdagent_x11
*x11
)
288 struct vdagent_x11_selection_request
*selection_request
;
289 selection_request
= x11
->selection_req
;
290 x11
->selection_req
= selection_request
->next
;
291 free(selection_request
);
294 static void vdagent_x11_next_conversion_request(struct vdagent_x11
*x11
)
296 struct vdagent_x11_conversion_request
*conversion_req
;
297 conversion_req
= x11
->conversion_req
;
298 x11
->conversion_req
= conversion_req
->next
;
299 free(conversion_req
);
302 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11
*x11
,
303 uint8_t selection
, int new_owner
)
305 struct vdagent_x11_selection_request
*prev_sel
, *curr_sel
, *next_sel
;
306 struct vdagent_x11_conversion_request
*prev_conv
, *curr_conv
, *next_conv
;
309 /* Clear pending requests and clipboard data */
312 next_sel
= x11
->selection_req
;
315 next_sel
= curr_sel
->next
;
316 if (curr_sel
->selection
== selection
) {
318 SELPRINTF("selection requests pending on clipboard ownership "
319 "change, clearing\n");
322 vdagent_x11_send_selection_notify(x11
, None
, curr_sel
);
323 if (curr_sel
== x11
->selection_req
) {
324 x11
->selection_req
= next_sel
;
325 free(x11
->selection_req_data
);
326 x11
->selection_req_data
= NULL
;
327 x11
->selection_req_data_pos
= 0;
328 x11
->selection_req_data_size
= 0;
329 x11
->selection_req_atom
= None
;
331 prev_sel
->next
= next_sel
;
341 next_conv
= x11
->conversion_req
;
343 curr_conv
= next_conv
;
344 next_conv
= curr_conv
->next
;
345 if (curr_conv
->selection
== selection
) {
347 SELPRINTF("client clipboard request pending on clipboard "
348 "ownership change, clearing\n");
351 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_DATA
, selection
,
352 VD_AGENT_CLIPBOARD_NONE
, NULL
, 0);
353 if (curr_conv
== x11
->conversion_req
) {
354 x11
->conversion_req
= next_conv
;
355 x11
->clipboard_data_size
= 0;
356 x11
->expect_property_notify
= 0;
358 prev_conv
->next
= next_conv
;
362 prev_conv
= curr_conv
;
366 if (new_owner
== owner_none
) {
367 /* When going from owner_guest to owner_none we need to send a
368 clipboard release message to the client */
369 if (x11
->clipboard_owner
[selection
] == owner_guest
) {
370 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_RELEASE
, selection
,
373 x11
->clipboard_type_count
[selection
] = 0;
375 x11
->clipboard_owner
[selection
] = new_owner
;
378 static int vdagent_x11_get_clipboard_atom(struct vdagent_x11
*x11
, uint8_t selection
, Atom
* clipboard
)
380 if (selection
== VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD
) {
381 *clipboard
= x11
->clipboard_atom
;
382 } else if (selection
== VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
) {
383 *clipboard
= x11
->clipboard_primary_atom
;
385 fprintf(x11
->errfile
, "get_clipboard_atom: unknown selection\n");
392 static int vdagent_x11_get_clipboard_selection(struct vdagent_x11
*x11
,
393 XEvent
*event
, uint8_t *selection
)
397 if (event
->type
== x11
->xfixes_event_base
) {
398 XFixesSelectionNotifyEvent
*xfev
= (XFixesSelectionNotifyEvent
*)event
;
399 atom
= xfev
->selection
;
400 } else if (event
->type
== SelectionNotify
) {
401 atom
= event
->xselection
.selection
;
402 } else if (event
->type
== SelectionRequest
) {
403 atom
= event
->xselectionrequest
.selection
;
405 fprintf(x11
->errfile
, "get_clipboard_selection: unknown event type\n");
409 if (atom
== x11
->clipboard_atom
) {
410 *selection
= VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD
;
411 } else if (atom
== x11
->clipboard_primary_atom
) {
412 *selection
= VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
;
414 fprintf(x11
->errfile
, "get_clipboard_selection: unknown selection\n");
421 static void vdagent_x11_handle_event(struct vdagent_x11
*x11
, XEvent event
)
426 if (event
.type
== x11
->xfixes_event_base
) {
429 XFixesSelectionNotifyEvent xfev
;
432 if (vdagent_x11_get_clipboard_selection(x11
, &event
, &selection
)) {
437 switch (ev
.xfev
.subtype
) {
438 case XFixesSetSelectionOwnerNotify
:
440 /* Treat ... as a SelectionOwnerNotify None */
441 case XFixesSelectionWindowDestroyNotify
:
442 case XFixesSelectionClientCloseNotify
:
443 ev
.xfev
.owner
= None
;
446 VSELPRINTF("unexpected xfix event subtype %d window %d\n",
447 (int)ev
.xfev
.subtype
, (int)event
.xany
.window
);
450 VSELPRINTF("New selection owner: %u\n", (unsigned int)ev
.xfev
.owner
);
452 /* Ignore becoming the owner ourselves */
453 if (ev
.xfev
.owner
== x11
->selection_window
)
456 /* If the clipboard owner is changed we no longer own it */
457 vdagent_x11_set_clipboard_owner(x11
, selection
, owner_none
);
459 if (ev
.xfev
.owner
== None
)
462 /* Request the supported targets from the new owner */
463 XConvertSelection(x11
->display
, ev
.xfev
.selection
, x11
->targets_atom
,
464 x11
->targets_atom
, x11
->selection_window
,
466 x11
->expected_targets_notifies
[selection
]++;
470 switch (event
.type
) {
471 case ConfigureNotify
:
472 if (event
.xconfigure
.window
!= x11
->root_window
)
477 if (event
.xconfigure
.width
== x11
->width
&&
478 event
.xconfigure
.height
== x11
->height
)
481 x11
->width
= event
.xconfigure
.width
;
482 x11
->height
= event
.xconfigure
.height
;
484 vdagent_x11_send_daemon_guest_xorg_res(x11
);
486 case SelectionNotify
:
487 if (event
.xselection
.target
== x11
->targets_atom
)
488 vdagent_x11_handle_targets_notify(x11
, &event
);
490 vdagent_x11_handle_selection_notify(x11
, &event
, 0);
495 if (x11
->expect_property_notify
&&
496 event
.xproperty
.state
== PropertyNewValue
) {
497 vdagent_x11_handle_selection_notify(x11
, &event
, 1);
499 if (x11
->selection_req_data
&&
500 event
.xproperty
.state
== PropertyDelete
) {
501 vdagent_x11_handle_property_delete_notify(x11
, &event
);
503 /* Always mark as handled, since we cannot unselect input for property
504 notifications once we are done with handling the incr transfer. */
508 /* Do nothing the clipboard ownership will get updated through
509 the XFixesSetSelectionOwnerNotify event */
512 case SelectionRequest
: {
513 struct vdagent_x11_selection_request
*req
, *new_req
;
515 if (vdagent_x11_get_clipboard_selection(x11
, &event
, &selection
)) {
519 new_req
= malloc(sizeof(*new_req
));
521 SELPRINTF("out of memory on SelectionRequest, ignoring.\n");
527 new_req
->event
= event
;
528 new_req
->selection
= selection
;
529 new_req
->next
= NULL
;
531 if (!x11
->selection_req
) {
532 x11
->selection_req
= new_req
;
533 vdagent_x11_handle_selection_request(x11
);
537 /* maybe we should limit the selection_request stack depth ? */
538 req
= x11
->selection_req
;
546 if (!handled
&& x11
->verbose
)
547 fprintf(x11
->errfile
, "unhandled x11 event, type %d, window %d\n",
548 (int)event
.type
, (int)event
.xany
.window
);
551 void vdagent_x11_do_read(struct vdagent_x11
*x11
)
555 while (XPending(x11
->display
)) {
556 XNextEvent(x11
->display
, &event
);
557 vdagent_x11_handle_event(x11
, event
);
561 static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11
*x11
)
563 struct vdagentd_guest_xorg_resolution res
;
565 res
.width
= x11
->width
;
566 res
.height
= x11
->height
;
568 udscs_write(x11
->vdagentd
, VDAGENTD_GUEST_XORG_RESOLUTION
, 0, 0,
569 (uint8_t *)&res
, sizeof(res
));
572 static const char *vdagent_x11_get_atom_name(struct vdagent_x11
*x11
, Atom a
)
577 return XGetAtomName(x11
->display
, a
);
580 static int vdagent_x11_get_selection(struct vdagent_x11
*x11
, XEvent
*event
,
581 uint8_t selection
, Atom type
, Atom prop
, int format
,
582 unsigned char **data_ret
, int incr
)
584 Bool del
= incr
? True
: False
;
586 int format_ret
, ret_val
= -1;
587 unsigned long len
, remain
;
588 unsigned char *data
= NULL
;
593 if (event
->xselection
.property
== None
) {
594 VSELPRINTF("XConvertSelection refused by clipboard owner\n");
598 if (event
->xselection
.requestor
!= x11
->selection_window
||
599 event
->xselection
.property
!= prop
) {
600 SELPRINTF("SelectionNotify parameters mismatch\n");
605 if (XGetWindowProperty(x11
->display
, x11
->selection_window
, prop
, 0,
606 LONG_MAX
, del
, type
, &type_ret
, &format_ret
, &len
,
607 &remain
, &data
) != Success
) {
608 SELPRINTF("XGetWindowProperty failed\n");
612 if (!incr
&& prop
!= x11
->targets_atom
) {
613 if (type_ret
== x11
->incr_atom
) {
614 int prop_min_size
= *(uint32_t*)data
;
616 if (x11
->expect_property_notify
) {
617 SELPRINTF("received an incr SelectionNotify while "
618 "still reading another incr property\n");
622 if (x11
->clipboard_data_space
< prop_min_size
) {
623 free(x11
->clipboard_data
);
624 x11
->clipboard_data
= malloc(prop_min_size
);
625 if (!x11
->clipboard_data
) {
626 SELPRINTF("out of memory allocating clipboard buffer\n");
627 x11
->clipboard_data_space
= 0;
630 x11
->clipboard_data_space
= prop_min_size
;
632 x11
->expect_property_notify
= 1;
633 XSelectInput(x11
->display
, x11
->selection_window
,
635 XDeleteProperty(x11
->display
, x11
->selection_window
, prop
);
637 return 0; /* Wait for more data */
639 XDeleteProperty(x11
->display
, x11
->selection_window
, prop
);
642 if (type_ret
!= type
) {
643 SELPRINTF("expected property type: %s, got: %s\n",
644 vdagent_x11_get_atom_name(x11
, type
),
645 vdagent_x11_get_atom_name(x11
, type_ret
));
649 if (format_ret
!= format
) {
650 SELPRINTF("expected %d bit format, got %d bits\n", format
, format_ret
);
654 /* Convert len to bytes */
659 len
*= sizeof(short);
668 if (x11
->clipboard_data_size
+ len
> x11
->clipboard_data_space
) {
669 void *old_clipboard_data
= x11
->clipboard_data
;
671 x11
->clipboard_data_space
= x11
->clipboard_data_size
+ len
;
672 x11
->clipboard_data
= realloc(x11
->clipboard_data
,
673 x11
->clipboard_data_space
);
674 if (!x11
->clipboard_data
) {
675 SELPRINTF("out of memory allocating clipboard buffer\n");
676 x11
->clipboard_data_space
= 0;
677 free(old_clipboard_data
);
681 memcpy(x11
->clipboard_data
+ x11
->clipboard_data_size
, data
, len
);
682 x11
->clipboard_data_size
+= len
;
683 VSELPRINTF("Appended %ld bytes to buffer\n", len
);
685 return 0; /* Wait for more data */
687 len
= x11
->clipboard_data_size
;
688 *data_ret
= x11
->clipboard_data
;
695 SELPRINTF("property contains no data (zero length)\n");
700 if ((incr
|| ret_val
== -1) && data
)
704 x11
->clipboard_data_size
= 0;
705 x11
->expect_property_notify
= 0;
711 static void vdagent_x11_get_selection_free(struct vdagent_x11
*x11
,
712 unsigned char *data
, int incr
)
715 /* If the clipboard has grown large return the memory to the system */
716 if (x11
->clipboard_data_space
> 512 * 1024) {
717 free(x11
->clipboard_data
);
718 x11
->clipboard_data
= NULL
;
719 x11
->clipboard_data_space
= 0;
725 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11
*x11
,
726 uint8_t selection
, Atom target
)
730 for (i
= 0; i
< clipboard_format_count
; i
++) {
731 for (j
= 0; j
< x11
->clipboard_formats
[i
].atom_count
; i
++) {
732 if (x11
->clipboard_formats
[i
].atoms
[j
] == target
) {
733 return x11
->clipboard_formats
[i
].type
;
738 SELPRINTF("unexpected selection type %s\n",
739 vdagent_x11_get_atom_name(x11
, target
));
740 return VD_AGENT_CLIPBOARD_NONE
;
743 static Atom
vdagent_x11_type_to_target(struct vdagent_x11
*x11
,
744 uint8_t selection
, uint32_t type
)
748 for (i
= 0; i
< x11
->clipboard_type_count
[selection
]; i
++) {
749 if (x11
->clipboard_agent_types
[selection
][i
] == type
) {
750 return x11
->clipboard_x11_targets
[selection
][i
];
753 SELPRINTF("client requested unavailable type %u\n", type
);
757 static void vdagent_x11_handle_conversion_request(struct vdagent_x11
*x11
)
761 if (!x11
->conversion_req
) {
765 vdagent_x11_get_clipboard_atom(x11
, x11
->conversion_req
->selection
, &clip
);
766 XConvertSelection(x11
->display
, clip
, x11
->conversion_req
->target
,
767 clip
, x11
->selection_window
, CurrentTime
);
770 static void vdagent_x11_handle_selection_notify(struct vdagent_x11
*x11
,
771 XEvent
*event
, int incr
)
774 unsigned char *data
= NULL
;
776 uint8_t selection
= -1;
779 if (!x11
->conversion_req
) {
780 fprintf(x11
->errfile
, "SelectionNotify received without a target\n");
783 vdagent_x11_get_clipboard_atom(x11
, x11
->conversion_req
->selection
, &clip
);
786 if (event
->xproperty
.atom
!= clip
||
787 event
->xproperty
.window
!= x11
->selection_window
) {
791 if (vdagent_x11_get_clipboard_selection(x11
, event
, &selection
)) {
793 } else if (selection
!= x11
->conversion_req
->selection
) {
794 SELPRINTF("Requested data for selection %d got %d\n",
795 (int)x11
->conversion_req
->selection
, (int)selection
);
798 if (event
->xselection
.target
!= x11
->conversion_req
->target
&&
799 event
->xselection
.target
!= x11
->incr_atom
) {
800 SELPRINTF("Requested %s target got %s\n",
801 vdagent_x11_get_atom_name(x11
, x11
->conversion_req
->target
),
802 vdagent_x11_get_atom_name(x11
, event
->xselection
.target
));
807 selection
= x11
->conversion_req
->selection
;
808 type
= vdagent_x11_target_to_type(x11
, selection
,
809 x11
->conversion_req
->target
);
810 if (len
== 0) { /* No errors so far */
811 len
= vdagent_x11_get_selection(x11
, event
, selection
,
812 x11
->conversion_req
->target
,
813 clip
, 8, &data
, incr
);
814 if (len
== 0) { /* waiting for more data? */
819 type
= VD_AGENT_CLIPBOARD_NONE
;
823 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_DATA
, selection
, type
,
825 vdagent_x11_get_selection_free(x11
, data
, incr
);
827 vdagent_x11_next_conversion_request(x11
);
828 vdagent_x11_handle_conversion_request(x11
);
831 static Atom
atom_lists_overlap(Atom
*atoms1
, Atom
*atoms2
, int l1
, int l2
)
835 for (i
= 0; i
< l1
; i
++)
836 for (j
= 0; j
< l2
; j
++)
837 if (atoms1
[i
] == atoms2
[j
])
843 static void vdagent_x11_print_targets(struct vdagent_x11
*x11
,
844 uint8_t selection
, const char *action
, Atom
*atoms
, int c
)
847 VSELPRINTF("%s %d targets:\n", action
, c
);
848 for (i
= 0; i
< c
; i
++)
849 VSELPRINTF("%s\n", vdagent_x11_get_atom_name(x11
, atoms
[i
]));
852 static void vdagent_x11_handle_targets_notify(struct vdagent_x11
*x11
,
856 Atom atom
, *atoms
= NULL
;
860 if (vdagent_x11_get_clipboard_selection(x11
, event
, &selection
)) {
864 if (!x11
->expected_targets_notifies
[selection
]) {
865 SELPRINTF("unexpected selection notify TARGETS\n");
869 x11
->expected_targets_notifies
[selection
]--;
871 /* If we have more targets_notifies pending, ignore this one, we
872 are only interested in the targets list of the current owner
873 (which is the last one we've requested a targets list from) */
874 if (x11
->expected_targets_notifies
[selection
]) {
878 len
= vdagent_x11_get_selection(x11
, event
, selection
,
879 XA_ATOM
, x11
->targets_atom
, 32,
880 (unsigned char **)&atoms
, 0);
881 if (len
== 0 || len
== -1) /* waiting for more data or error? */
886 vdagent_x11_print_targets(x11
, selection
, "received", atoms
, len
);
888 type_count
= &x11
->clipboard_type_count
[selection
];
890 for (i
= 0; i
< clipboard_format_count
; i
++) {
891 atom
= atom_lists_overlap(x11
->clipboard_formats
[i
].atoms
, atoms
,
892 x11
->clipboard_formats
[i
].atom_count
, len
);
894 x11
->clipboard_agent_types
[selection
][*type_count
] =
895 x11
->clipboard_formats
[i
].type
;
896 x11
->clipboard_x11_targets
[selection
][*type_count
] = atom
;
899 sizeof(x11
->clipboard_agent_types
[0])/sizeof(uint32_t)) {
900 SELPRINTF("handle_targets_notify: too many types\n");
907 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_GRAB
, selection
, 0,
908 (uint8_t *)x11
->clipboard_agent_types
[selection
],
909 *type_count
* sizeof(uint32_t));
910 vdagent_x11_set_clipboard_owner(x11
, selection
, owner_guest
);
913 vdagent_x11_get_selection_free(x11
, (unsigned char *)atoms
, 0);
916 static void vdagent_x11_send_selection_notify(struct vdagent_x11
*x11
,
917 Atom prop
, struct vdagent_x11_selection_request
*request
)
922 event
= &request
->event
;
924 event
= &x11
->selection_req
->event
;
927 res
.xselection
.property
= prop
;
928 res
.xselection
.type
= SelectionNotify
;
929 res
.xselection
.display
= event
->xselectionrequest
.display
;
930 res
.xselection
.requestor
= event
->xselectionrequest
.requestor
;
931 res
.xselection
.selection
= event
->xselectionrequest
.selection
;
932 res
.xselection
.target
= event
->xselectionrequest
.target
;
933 res
.xselection
.time
= event
->xselectionrequest
.time
;
934 XSendEvent(x11
->display
, event
->xselectionrequest
.requestor
, 0, 0, &res
);
937 vdagent_x11_next_selection_request(x11
);
938 vdagent_x11_handle_selection_request(x11
);
942 static void vdagent_x11_send_targets(struct vdagent_x11
*x11
,
943 uint8_t selection
, XEvent
*event
)
945 Atom prop
, targets
[256] = { x11
->targets_atom
, };
946 int i
, j
, k
, target_count
= 1;
948 for (i
= 0; i
< x11
->clipboard_type_count
[selection
]; i
++) {
949 for (j
= 0; j
< clipboard_format_count
; j
++) {
950 if (x11
->clipboard_formats
[j
].type
!=
951 x11
->clipboard_agent_types
[selection
][i
])
954 for (k
= 0; k
< x11
->clipboard_formats
[j
].atom_count
; k
++) {
955 targets
[target_count
] = x11
->clipboard_formats
[j
].atoms
[k
];
957 if (target_count
== sizeof(targets
)/sizeof(Atom
)) {
958 SELPRINTF("send_targets: too many targets\n");
966 prop
= event
->xselectionrequest
.property
;
968 prop
= event
->xselectionrequest
.target
;
970 XChangeProperty(x11
->display
, event
->xselectionrequest
.requestor
, prop
,
971 XA_ATOM
, 32, PropModeReplace
, (unsigned char *)&targets
,
973 vdagent_x11_print_targets(x11
, selection
, "sent", targets
, target_count
);
974 vdagent_x11_send_selection_notify(x11
, prop
, NULL
);
977 static void vdagent_x11_handle_selection_request(struct vdagent_x11
*x11
)
980 uint32_t type
= VD_AGENT_CLIPBOARD_NONE
;
983 if (!x11
->selection_req
)
986 event
= &x11
->selection_req
->event
;
987 selection
= x11
->selection_req
->selection
;
989 if (x11
->clipboard_owner
[selection
] != owner_client
) {
990 SELPRINTF("received selection request event for target %s, "
991 "while not owning client clipboard\n",
992 vdagent_x11_get_atom_name(x11
, event
->xselectionrequest
.target
));
993 vdagent_x11_send_selection_notify(x11
, None
, NULL
);
997 if (event
->xselectionrequest
.target
== x11
->multiple_atom
) {
998 SELPRINTF("multiple target not supported\n");
999 vdagent_x11_send_selection_notify(x11
, None
, NULL
);
1003 if (event
->xselectionrequest
.target
== x11
->targets_atom
) {
1004 vdagent_x11_send_targets(x11
, selection
, event
);
1008 type
= vdagent_x11_target_to_type(x11
, selection
,
1009 event
->xselectionrequest
.target
);
1010 if (type
== VD_AGENT_CLIPBOARD_NONE
) {
1011 vdagent_x11_send_selection_notify(x11
, None
, NULL
);
1015 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_REQUEST
, selection
, type
,
1019 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11
*x11
,
1026 assert(x11
->selection_req
);
1027 sel_event
= &x11
->selection_req
->event
;
1028 selection
= x11
->selection_req
->selection
;
1029 if (del_event
->xproperty
.window
!= sel_event
->xselectionrequest
.requestor
1030 || del_event
->xproperty
.atom
!= x11
->selection_req_atom
) {
1034 len
= x11
->selection_req_data_size
- x11
->selection_req_data_pos
;
1035 if (len
> x11
->max_prop_size
) {
1036 len
= x11
->max_prop_size
;
1040 VSELPRINTF("Sending %d-%d/%d bytes of clipboard data\n",
1041 x11
->selection_req_data_pos
,
1042 x11
->selection_req_data_pos
+ len
- 1,
1043 x11
->selection_req_data_size
);
1045 VSELPRINTF("Ending incr send of clipboard data\n");
1047 XChangeProperty(x11
->display
, sel_event
->xselectionrequest
.requestor
,
1048 x11
->selection_req_atom
,
1049 sel_event
->xselectionrequest
.target
, 8, PropModeReplace
,
1050 x11
->selection_req_data
+ x11
->selection_req_data_pos
,
1052 x11
->selection_req_data_pos
+= len
;
1054 /* Note we must explictly send a 0 sized XChangeProperty to signal the
1055 incr transfer is done. Hence we do not check if we've send all data
1056 but instead check we've send the final 0 sized XChangeProperty. */
1058 free(x11
->selection_req_data
);
1059 x11
->selection_req_data
= NULL
;
1060 x11
->selection_req_data_pos
= 0;
1061 x11
->selection_req_data_size
= 0;
1062 x11
->selection_req_atom
= None
;
1063 vdagent_x11_next_selection_request(x11
);
1064 vdagent_x11_handle_selection_request(x11
);
1068 void vdagent_x11_set_monitor_config(struct vdagent_x11
*x11
,
1069 VDAgentMonitorsConfig
*mon_config
)
1071 int i
, num_sizes
= 0;
1073 unsigned int closest_diff
= -1;
1074 XRRScreenSize
* sizes
;
1075 XRRScreenConfiguration
* config
;
1078 if (!x11
->has_xrandr
)
1081 if (mon_config
->num_of_monitors
!= 1) {
1082 fprintf(x11
->errfile
,
1083 "Only 1 monitor supported, ignoring additional monitors\n");
1086 sizes
= XRRSizes(x11
->display
, x11
->screen
, &num_sizes
);
1087 if (!sizes
|| !num_sizes
) {
1088 fprintf(x11
->errfile
, "XRRSizes failed\n");
1092 /* Find the closest size which will fit within the monitor */
1093 for (i
= 0; i
< num_sizes
; i
++) {
1094 if (sizes
[i
].width
> mon_config
->monitors
[0].width
||
1095 sizes
[i
].height
> mon_config
->monitors
[0].height
)
1096 continue; /* Too large for the monitor */
1098 unsigned int wdiff
= mon_config
->monitors
[0].width
- sizes
[i
].width
;
1099 unsigned int hdiff
= mon_config
->monitors
[0].height
- sizes
[i
].height
;
1100 unsigned int diff
= wdiff
* wdiff
+ hdiff
* hdiff
;
1101 if (diff
< closest_diff
) {
1102 closest_diff
= diff
;
1108 fprintf(x11
->errfile
, "no suitable resolution found for monitor\n");
1112 config
= XRRGetScreenInfo(x11
->display
, x11
->root_window
);
1114 fprintf(x11
->errfile
, "get screen info failed\n");
1117 XRRConfigCurrentConfiguration(config
, &rotation
);
1118 XRRSetScreenConfig(x11
->display
, config
, x11
->root_window
, best
,
1119 rotation
, CurrentTime
);
1120 XRRFreeScreenConfigInfo(config
);
1121 x11
->width
= sizes
[best
].width
;
1122 x11
->height
= sizes
[best
].height
;
1123 vdagent_x11_send_daemon_guest_xorg_res(x11
);
1125 /* Flush output buffers and consume any pending events */
1126 vdagent_x11_do_read(x11
);
1129 void vdagent_x11_clipboard_request(struct vdagent_x11
*x11
,
1130 uint8_t selection
, uint32_t type
)
1133 struct vdagent_x11_conversion_request
*req
, *new_req
;
1135 /* We don't use clip here, but we call get_clipboard_atom to verify
1136 selection is valid */
1137 if (vdagent_x11_get_clipboard_atom(x11
, selection
, &clip
)) {
1141 if (x11
->clipboard_owner
[selection
] != owner_guest
) {
1142 SELPRINTF("received clipboard req while not owning guest clipboard\n");
1146 target
= vdagent_x11_type_to_target(x11
, selection
, type
);
1147 if (target
== None
) {
1151 new_req
= malloc(sizeof(*new_req
));
1153 SELPRINTF("out of memory on client clipboard request, ignoring.\n");
1157 new_req
->target
= target
;
1158 new_req
->selection
= selection
;
1159 new_req
->next
= NULL
;
1161 if (!x11
->conversion_req
) {
1162 x11
->conversion_req
= new_req
;
1163 vdagent_x11_handle_conversion_request(x11
);
1164 /* Flush output buffers and consume any pending events */
1165 vdagent_x11_do_read(x11
);
1169 /* maybe we should limit the conversion_request stack depth ? */
1170 req
= x11
->conversion_req
;
1174 req
->next
= new_req
;
1178 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_DATA
,
1179 selection
, VD_AGENT_CLIPBOARD_NONE
, NULL
, 0);
1182 void vdagent_x11_clipboard_grab(struct vdagent_x11
*x11
, uint8_t selection
,
1183 uint32_t *types
, uint32_t type_count
)
1187 if (vdagent_x11_get_clipboard_atom(x11
, selection
, &clip
)) {
1191 if (type_count
> sizeof(x11
->clipboard_agent_types
[0])/sizeof(uint32_t)) {
1192 SELPRINTF("x11_clipboard_grab: too many types\n");
1193 type_count
= sizeof(x11
->clipboard_agent_types
[0])/sizeof(uint32_t);
1196 memcpy(x11
->clipboard_agent_types
[selection
], types
,
1197 type_count
* sizeof(uint32_t));
1198 x11
->clipboard_type_count
[selection
] = type_count
;
1200 XSetSelectionOwner(x11
->display
, clip
,
1201 x11
->selection_window
, CurrentTime
);
1202 vdagent_x11_set_clipboard_owner(x11
, selection
, owner_client
);
1204 /* Flush output buffers and consume any pending events */
1205 vdagent_x11_do_read(x11
);
1208 void vdagent_x11_clipboard_data(struct vdagent_x11
*x11
, uint8_t selection
,
1209 uint32_t type
, uint8_t *data
, uint32_t size
)
1213 uint32_t type_from_event
;
1215 if (x11
->selection_req_data
) {
1217 SELPRINTF("received clipboard data while still sending"
1218 " data from previous request, ignoring\n");
1224 if (!x11
->selection_req
) {
1226 SELPRINTF("received clipboard data without an "
1227 "outstanding selection request, ignoring\n");
1233 event
= &x11
->selection_req
->event
;
1234 type_from_event
= vdagent_x11_target_to_type(x11
,
1235 x11
->selection_req
->selection
,
1236 event
->xselectionrequest
.target
);
1237 if (type_from_event
!= type
||
1238 selection
!= x11
->selection_req
->selection
) {
1239 if (selection
!= x11
->selection_req
->selection
) {
1240 SELPRINTF("expecting data for selection %d got %d\n",
1241 (int)x11
->selection_req
->selection
, (int)selection
);
1243 if (type_from_event
!= type
) {
1244 SELPRINTF("expecting type %u clipboard data got %u\n",
1245 type_from_event
, type
);
1247 vdagent_x11_send_selection_notify(x11
, None
, NULL
);
1250 /* Flush output buffers and consume any pending events */
1251 vdagent_x11_do_read(x11
);
1255 prop
= event
->xselectionrequest
.property
;
1257 prop
= event
->xselectionrequest
.target
;
1259 if (size
> x11
->max_prop_size
) {
1260 unsigned long len
= size
;
1261 VSELPRINTF("Starting incr send of clipboard data\n");
1262 x11
->selection_req_data
= data
;
1263 x11
->selection_req_data_pos
= 0;
1264 x11
->selection_req_data_size
= size
;
1265 x11
->selection_req_atom
= prop
;
1266 XSelectInput(x11
->display
, event
->xselectionrequest
.requestor
,
1267 PropertyChangeMask
);
1268 XChangeProperty(x11
->display
, event
->xselectionrequest
.requestor
, prop
,
1269 x11
->incr_atom
, 32, PropModeReplace
,
1270 (unsigned char*)&len
, 1);
1271 vdagent_x11_send_selection_notify(x11
, prop
, x11
->selection_req
);
1273 XChangeProperty(x11
->display
, event
->xselectionrequest
.requestor
, prop
,
1274 event
->xselectionrequest
.target
, 8, PropModeReplace
,
1276 vdagent_x11_send_selection_notify(x11
, prop
, NULL
);
1280 /* Flush output buffers and consume any pending events */
1281 vdagent_x11_do_read(x11
);
1284 void vdagent_x11_clipboard_release(struct vdagent_x11
*x11
, uint8_t selection
)
1289 if (vdagent_x11_get_clipboard_atom(x11
, selection
, &clip
)) {
1293 if (x11
->clipboard_owner
[selection
] != owner_client
) {
1294 SELPRINTF("received release while not owning client clipboard\n");
1298 XSetSelectionOwner(x11
->display
, clip
, None
, CurrentTime
);
1299 /* Make sure we process the XFixesSetSelectionOwnerNotify event caused
1300 by this, so we don't end up changing the clipboard owner to none, after
1301 it has already been re-owned because this event is still pending. */
1302 XSync(x11
->display
, False
);
1303 while (XCheckTypedEvent(x11
->display
, x11
->xfixes_event_base
,
1305 vdagent_x11_handle_event(x11
, event
);
1307 /* Note no need to do a set_clipboard_owner(owner_none) here, as that is
1308 already done by processing the XFixesSetSelectionOwnerNotify event. */
1310 /* Flush output buffers and consume any pending events */
1311 vdagent_x11_do_read(x11
);