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/Xinerama.h>
42 #include <X11/extensions/Xfixes.h>
43 #include "vdagentd-proto.h"
44 #include "vdagent-x11.h"
46 /* Macros to print a message to the logfile prefixed by the selection */
47 #define SELPRINTF(format, ...) \
48 fprintf(x11->errfile, "%s: " format, \
49 vdagent_x11_sel_to_str(selection), ##__VA_ARGS__)
51 #define VSELPRINTF(format, ...) \
54 fprintf(x11->errfile, "%s: " format, \
55 vdagent_x11_sel_to_str(selection), ##__VA_ARGS__); \
59 enum { owner_none
, owner_guest
, owner_client
};
61 /* X11 terminology is confusing a selection request is a request from an
62 app to get clipboard data from us, so iow from the spice client through
63 the vdagent channel. We handle these one at a time and queue any which
64 come in while we are still handling the current one. */
65 struct vdagent_x11_selection_request
{
68 struct vdagent_x11_selection_request
*next
;
71 /* A conversion request is X11 speak for asking an other app to give its
72 clipboard data to us, we do these on behalf of the spice client to copy
73 data from the guest to the client. Like selection requests we process
74 these one at a time. */
75 struct vdagent_x11_conversion_request
{
78 struct vdagent_x11_conversion_request
*next
;
81 struct clipboard_format_tmpl
{
83 const char *atom_names
[16];
86 struct clipboard_format_info
{
92 static const struct clipboard_format_tmpl clipboard_format_templates
[] = {
93 { VD_AGENT_CLIPBOARD_UTF8_TEXT
, { "UTF8_STRING",
94 "text/plain;charset=UTF-8", "text/plain;charset=utf-8", NULL
}, },
95 { VD_AGENT_CLIPBOARD_IMAGE_PNG
, { "image/png", NULL
}, },
96 { VD_AGENT_CLIPBOARD_IMAGE_BMP
, { "image/bmp", "image/x-bmp",
97 "image/x-MS-bmp", "image/x-win-bitmap", NULL
}, },
98 { VD_AGENT_CLIPBOARD_IMAGE_TIFF
, { "image/tiff", NULL
}, },
99 { VD_AGENT_CLIPBOARD_IMAGE_JPG
, { "image/jpeg", NULL
}, },
102 #define clipboard_format_count (sizeof(clipboard_format_templates)/sizeof(clipboard_format_templates[0]))
105 struct clipboard_format_info clipboard_formats
[clipboard_format_count
];
108 Atom clipboard_primary_atom
;
113 Window selection_window
;
114 struct udscs_connection
*vdagentd
;
124 int xfixes_event_base
;
126 int expected_targets_notifies
[256];
127 int clipboard_owner
[256];
128 int clipboard_type_count
[256];
129 uint32_t clipboard_agent_types
[256][256];
130 Atom clipboard_x11_targets
[256][256];
131 /* Data for conversion_req which is currently being processed */
132 struct vdagent_x11_conversion_request
*conversion_req
;
133 int expect_property_notify
;
134 uint8_t *clipboard_data
;
135 uint32_t clipboard_data_size
;
136 uint32_t clipboard_data_space
;
137 /* Data for selection_req which is currently being processed */
138 struct vdagent_x11_selection_request
*selection_req
;
139 uint8_t *selection_req_data
;
140 uint32_t selection_req_data_pos
;
141 uint32_t selection_req_data_size
;
142 Atom selection_req_atom
;
145 static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11
*x11
);
146 static void vdagent_x11_handle_selection_notify(struct vdagent_x11
*x11
,
147 XEvent
*event
, int incr
);
148 static void vdagent_x11_handle_selection_request(struct vdagent_x11
*x11
);
149 static void vdagent_x11_handle_targets_notify(struct vdagent_x11
*x11
,
151 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11
*x11
,
153 static void vdagent_x11_send_selection_notify(struct vdagent_x11
*x11
,
154 Atom prop
, struct vdagent_x11_selection_request
*request
);
155 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11
*x11
,
156 uint8_t selection
, int new_owner
);
158 static const char *vdagent_x11_sel_to_str(uint8_t selection
) {
160 case VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD
:
162 case VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
:
164 case VD_AGENT_CLIPBOARD_SELECTION_SECONDARY
:
171 struct vdagent_x11
*vdagent_x11_create(struct udscs_connection
*vdagentd
,
172 FILE *errfile
, int verbose
)
174 struct vdagent_x11
*x11
;
175 XWindowAttributes attrib
;
176 int i
, j
, major
, minor
;
178 x11
= calloc(1, sizeof(*x11
));
180 fprintf(errfile
, "out of memory allocating vdagent_x11 struct\n");
184 x11
->vdagentd
= vdagentd
;
185 x11
->errfile
= errfile
;
186 x11
->verbose
= verbose
;
188 x11
->display
= XOpenDisplay(NULL
);
190 fprintf(x11
->errfile
, "could not connect to X-server\n");
195 x11
->screen
= DefaultScreen(x11
->display
);
196 x11
->root_window
= RootWindow(x11
->display
, x11
->screen
);
197 x11
->fd
= ConnectionNumber(x11
->display
);
198 x11
->clipboard_atom
= XInternAtom(x11
->display
, "CLIPBOARD", False
);
199 x11
->clipboard_primary_atom
= XInternAtom(x11
->display
, "PRIMARY", False
);
200 x11
->targets_atom
= XInternAtom(x11
->display
, "TARGETS", False
);
201 x11
->incr_atom
= XInternAtom(x11
->display
, "INCR", False
);
202 x11
->multiple_atom
= XInternAtom(x11
->display
, "MULTIPLE", False
);
203 for(i
= 0; i
< clipboard_format_count
; i
++) {
204 x11
->clipboard_formats
[i
].type
= clipboard_format_templates
[i
].type
;
205 for(j
= 0; clipboard_format_templates
[i
].atom_names
[j
]; j
++) {
206 x11
->clipboard_formats
[i
].atoms
[j
] =
207 XInternAtom(x11
->display
,
208 clipboard_format_templates
[i
].atom_names
[j
],
211 x11
->clipboard_formats
[i
].atom_count
= j
;
214 /* We should not store properties (for selections) on the root window */
215 x11
->selection_window
= XCreateSimpleWindow(x11
->display
, x11
->root_window
,
216 0, 0, 1, 1, 0, 0, 0);
218 fprintf(x11
->errfile
, "Selection window: %u\n",
219 (unsigned int)x11
->selection_window
);
221 if (XRRQueryExtension(x11
->display
, &i
, &i
))
224 if (XineramaQueryExtension(x11
->display
, &i
, &i
))
225 x11
->has_xinerama
= 1;
227 switch (x11
->has_xrandr
<< 4 | x11
->has_xinerama
) {
229 fprintf(x11
->errfile
, "Neither Xrandr nor Xinerama found, assuming single monitor setup\n");
233 fprintf(x11
->errfile
, "Found Xinerama extension without Xrandr, assuming a multi monitor setup\n");
236 fprintf(x11
->errfile
, "Found Xrandr but no Xinerama, weird! Assuming a single monitor setup\n");
239 /* Standard single monitor setup, nothing to see here */
243 if (XFixesQueryExtension(x11
->display
, &x11
->xfixes_event_base
, &i
) &&
244 XFixesQueryVersion(x11
->display
, &major
, &minor
) && major
>= 1) {
246 XFixesSelectSelectionInput(x11
->display
, x11
->root_window
,
248 XFixesSetSelectionOwnerNotifyMask
|
249 XFixesSelectionWindowDestroyNotifyMask
|
250 XFixesSelectionClientCloseNotifyMask
);
251 XFixesSelectSelectionInput(x11
->display
, x11
->root_window
,
252 x11
->clipboard_primary_atom
,
253 XFixesSetSelectionOwnerNotifyMask
|
254 XFixesSelectionWindowDestroyNotifyMask
|
255 XFixesSelectionClientCloseNotifyMask
);
257 fprintf(x11
->errfile
,
258 "no xfixes, no guest -> client copy paste support\n");
260 x11
->max_prop_size
= XExtendedMaxRequestSize(x11
->display
);
261 if (x11
->max_prop_size
) {
262 x11
->max_prop_size
-= 100;
264 x11
->max_prop_size
= XMaxRequestSize(x11
->display
) - 100;
266 /* Be a good X11 citizen and maximize the amount of data we send at once */
267 if (x11
->max_prop_size
> 262144)
268 x11
->max_prop_size
= 262144;
270 /* Catch resolution changes */
271 XSelectInput(x11
->display
, x11
->root_window
, StructureNotifyMask
);
273 /* Get the current resolution */
274 XGetWindowAttributes(x11
->display
, x11
->root_window
, &attrib
);
275 x11
->width
= attrib
.width
;
276 x11
->height
= attrib
.height
;
277 vdagent_x11_send_daemon_guest_xorg_res(x11
);
279 /* Flush output buffers and consume any pending events */
280 vdagent_x11_do_read(x11
);
285 void vdagent_x11_destroy(struct vdagent_x11
*x11
)
292 for (sel
= 0; sel
< VD_AGENT_CLIPBOARD_SELECTION_SECONDARY
; ++sel
) {
293 vdagent_x11_set_clipboard_owner(x11
, sel
, owner_none
);
296 XCloseDisplay(x11
->display
);
300 int vdagent_x11_get_fd(struct vdagent_x11
*x11
)
305 static void vdagent_x11_next_selection_request(struct vdagent_x11
*x11
)
307 struct vdagent_x11_selection_request
*selection_request
;
308 selection_request
= x11
->selection_req
;
309 x11
->selection_req
= selection_request
->next
;
310 free(selection_request
);
313 static void vdagent_x11_next_conversion_request(struct vdagent_x11
*x11
)
315 struct vdagent_x11_conversion_request
*conversion_req
;
316 conversion_req
= x11
->conversion_req
;
317 x11
->conversion_req
= conversion_req
->next
;
318 free(conversion_req
);
321 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11
*x11
,
322 uint8_t selection
, int new_owner
)
324 struct vdagent_x11_selection_request
*prev_sel
, *curr_sel
, *next_sel
;
325 struct vdagent_x11_conversion_request
*prev_conv
, *curr_conv
, *next_conv
;
328 /* Clear pending requests and clipboard data */
331 next_sel
= x11
->selection_req
;
334 next_sel
= curr_sel
->next
;
335 if (curr_sel
->selection
== selection
) {
337 SELPRINTF("selection requests pending on clipboard ownership "
338 "change, clearing\n");
341 vdagent_x11_send_selection_notify(x11
, None
, curr_sel
);
342 if (curr_sel
== x11
->selection_req
) {
343 x11
->selection_req
= next_sel
;
344 free(x11
->selection_req_data
);
345 x11
->selection_req_data
= NULL
;
346 x11
->selection_req_data_pos
= 0;
347 x11
->selection_req_data_size
= 0;
348 x11
->selection_req_atom
= None
;
350 prev_sel
->next
= next_sel
;
360 next_conv
= x11
->conversion_req
;
362 curr_conv
= next_conv
;
363 next_conv
= curr_conv
->next
;
364 if (curr_conv
->selection
== selection
) {
366 SELPRINTF("client clipboard request pending on clipboard "
367 "ownership change, clearing\n");
370 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_DATA
, selection
,
371 VD_AGENT_CLIPBOARD_NONE
, NULL
, 0);
372 if (curr_conv
== x11
->conversion_req
) {
373 x11
->conversion_req
= next_conv
;
374 x11
->clipboard_data_size
= 0;
375 x11
->expect_property_notify
= 0;
377 prev_conv
->next
= next_conv
;
381 prev_conv
= curr_conv
;
385 if (new_owner
== owner_none
) {
386 /* When going from owner_guest to owner_none we need to send a
387 clipboard release message to the client */
388 if (x11
->clipboard_owner
[selection
] == owner_guest
) {
389 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_RELEASE
, selection
,
392 x11
->clipboard_type_count
[selection
] = 0;
394 x11
->clipboard_owner
[selection
] = new_owner
;
397 static int vdagent_x11_get_clipboard_atom(struct vdagent_x11
*x11
, uint8_t selection
, Atom
* clipboard
)
399 if (selection
== VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD
) {
400 *clipboard
= x11
->clipboard_atom
;
401 } else if (selection
== VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
) {
402 *clipboard
= x11
->clipboard_primary_atom
;
404 fprintf(x11
->errfile
, "get_clipboard_atom: unknown selection\n");
411 static int vdagent_x11_get_clipboard_selection(struct vdagent_x11
*x11
,
412 XEvent
*event
, uint8_t *selection
)
416 if (event
->type
== x11
->xfixes_event_base
) {
417 XFixesSelectionNotifyEvent
*xfev
= (XFixesSelectionNotifyEvent
*)event
;
418 atom
= xfev
->selection
;
419 } else if (event
->type
== SelectionNotify
) {
420 atom
= event
->xselection
.selection
;
421 } else if (event
->type
== SelectionRequest
) {
422 atom
= event
->xselectionrequest
.selection
;
424 fprintf(x11
->errfile
, "get_clipboard_selection: unknown event type\n");
428 if (atom
== x11
->clipboard_atom
) {
429 *selection
= VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD
;
430 } else if (atom
== x11
->clipboard_primary_atom
) {
431 *selection
= VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
;
433 fprintf(x11
->errfile
, "get_clipboard_selection: unknown selection\n");
440 static void vdagent_x11_handle_event(struct vdagent_x11
*x11
, XEvent event
)
445 if (event
.type
== x11
->xfixes_event_base
) {
448 XFixesSelectionNotifyEvent xfev
;
451 if (vdagent_x11_get_clipboard_selection(x11
, &event
, &selection
)) {
456 switch (ev
.xfev
.subtype
) {
457 case XFixesSetSelectionOwnerNotify
:
459 /* Treat ... as a SelectionOwnerNotify None */
460 case XFixesSelectionWindowDestroyNotify
:
461 case XFixesSelectionClientCloseNotify
:
462 ev
.xfev
.owner
= None
;
465 VSELPRINTF("unexpected xfix event subtype %d window %d\n",
466 (int)ev
.xfev
.subtype
, (int)event
.xany
.window
);
469 VSELPRINTF("New selection owner: %u\n", (unsigned int)ev
.xfev
.owner
);
471 /* Ignore becoming the owner ourselves */
472 if (ev
.xfev
.owner
== x11
->selection_window
)
475 /* If the clipboard owner is changed we no longer own it */
476 vdagent_x11_set_clipboard_owner(x11
, selection
, owner_none
);
478 if (ev
.xfev
.owner
== None
)
481 /* Request the supported targets from the new owner */
482 XConvertSelection(x11
->display
, ev
.xfev
.selection
, x11
->targets_atom
,
483 x11
->targets_atom
, x11
->selection_window
,
485 x11
->expected_targets_notifies
[selection
]++;
489 switch (event
.type
) {
490 case ConfigureNotify
:
491 if (event
.xconfigure
.window
!= x11
->root_window
)
496 if (event
.xconfigure
.width
== x11
->width
&&
497 event
.xconfigure
.height
== x11
->height
)
500 x11
->width
= event
.xconfigure
.width
;
501 x11
->height
= event
.xconfigure
.height
;
503 vdagent_x11_send_daemon_guest_xorg_res(x11
);
506 /* These are uninteresting */
509 case SelectionNotify
:
510 if (event
.xselection
.target
== x11
->targets_atom
)
511 vdagent_x11_handle_targets_notify(x11
, &event
);
513 vdagent_x11_handle_selection_notify(x11
, &event
, 0);
518 if (x11
->expect_property_notify
&&
519 event
.xproperty
.state
== PropertyNewValue
) {
520 vdagent_x11_handle_selection_notify(x11
, &event
, 1);
522 if (x11
->selection_req_data
&&
523 event
.xproperty
.state
== PropertyDelete
) {
524 vdagent_x11_handle_property_delete_notify(x11
, &event
);
526 /* Always mark as handled, since we cannot unselect input for property
527 notifications once we are done with handling the incr transfer. */
531 /* Do nothing the clipboard ownership will get updated through
532 the XFixesSetSelectionOwnerNotify event */
535 case SelectionRequest
: {
536 struct vdagent_x11_selection_request
*req
, *new_req
;
538 if (vdagent_x11_get_clipboard_selection(x11
, &event
, &selection
)) {
542 new_req
= malloc(sizeof(*new_req
));
544 SELPRINTF("out of memory on SelectionRequest, ignoring.\n");
550 new_req
->event
= event
;
551 new_req
->selection
= selection
;
552 new_req
->next
= NULL
;
554 if (!x11
->selection_req
) {
555 x11
->selection_req
= new_req
;
556 vdagent_x11_handle_selection_request(x11
);
560 /* maybe we should limit the selection_request stack depth ? */
561 req
= x11
->selection_req
;
569 if (!handled
&& x11
->verbose
)
570 fprintf(x11
->errfile
, "unhandled x11 event, type %d, window %d\n",
571 (int)event
.type
, (int)event
.xany
.window
);
574 void vdagent_x11_do_read(struct vdagent_x11
*x11
)
578 while (XPending(x11
->display
)) {
579 XNextEvent(x11
->display
, &event
);
580 vdagent_x11_handle_event(x11
, event
);
584 static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11
*x11
)
586 struct vdagentd_guest_xorg_resolution
*res
= NULL
;
587 XineramaScreenInfo
*screen_info
= NULL
;
588 int i
, screen_count
= 0;
590 if (x11
->has_xinerama
)
591 screen_info
= XineramaQueryScreens(x11
->display
, &screen_count
);
593 if (screen_count
== 0)
596 res
= malloc(screen_count
* sizeof(*res
));
598 fprintf(x11
->errfile
, "out of memory while trying to send resolutions, not sending resolutions.\n");
605 for (i
= 0; i
< screen_count
; i
++) {
606 if (screen_info
[i
].screen_number
>= screen_count
) {
607 fprintf(x11
->errfile
, "Invalid screen number in xinerama screen info (%d >= %d)\n",
608 screen_info
[i
].screen_number
, screen_count
);
613 res
[screen_info
[i
].screen_number
].width
= screen_info
[i
].width
;
614 res
[screen_info
[i
].screen_number
].height
= screen_info
[i
].height
;
615 res
[screen_info
[i
].screen_number
].x
= screen_info
[i
].x_org
;
616 res
[screen_info
[i
].screen_number
].y
= screen_info
[i
].y_org
;
620 res
[0].width
= x11
->width
;
621 res
[0].height
= x11
->height
;
626 udscs_write(x11
->vdagentd
, VDAGENTD_GUEST_XORG_RESOLUTION
, x11
->width
,
627 x11
->height
, (uint8_t *)res
, screen_count
* sizeof(*res
));
631 static const char *vdagent_x11_get_atom_name(struct vdagent_x11
*x11
, Atom a
)
636 return XGetAtomName(x11
->display
, a
);
639 static int vdagent_x11_get_selection(struct vdagent_x11
*x11
, XEvent
*event
,
640 uint8_t selection
, Atom type
, Atom prop
, int format
,
641 unsigned char **data_ret
, int incr
)
643 Bool del
= incr
? True
: False
;
645 int format_ret
, ret_val
= -1;
646 unsigned long len
, remain
;
647 unsigned char *data
= NULL
;
652 if (event
->xselection
.property
== None
) {
653 VSELPRINTF("XConvertSelection refused by clipboard owner\n");
657 if (event
->xselection
.requestor
!= x11
->selection_window
||
658 event
->xselection
.property
!= prop
) {
659 SELPRINTF("SelectionNotify parameters mismatch\n");
664 if (XGetWindowProperty(x11
->display
, x11
->selection_window
, prop
, 0,
665 LONG_MAX
, del
, type
, &type_ret
, &format_ret
, &len
,
666 &remain
, &data
) != Success
) {
667 SELPRINTF("XGetWindowProperty failed\n");
671 if (!incr
&& prop
!= x11
->targets_atom
) {
672 if (type_ret
== x11
->incr_atom
) {
673 int prop_min_size
= *(uint32_t*)data
;
675 if (x11
->expect_property_notify
) {
676 SELPRINTF("received an incr SelectionNotify while "
677 "still reading another incr property\n");
681 if (x11
->clipboard_data_space
< prop_min_size
) {
682 free(x11
->clipboard_data
);
683 x11
->clipboard_data
= malloc(prop_min_size
);
684 if (!x11
->clipboard_data
) {
685 SELPRINTF("out of memory allocating clipboard buffer\n");
686 x11
->clipboard_data_space
= 0;
689 x11
->clipboard_data_space
= prop_min_size
;
691 x11
->expect_property_notify
= 1;
692 XSelectInput(x11
->display
, x11
->selection_window
,
694 XDeleteProperty(x11
->display
, x11
->selection_window
, prop
);
696 return 0; /* Wait for more data */
698 XDeleteProperty(x11
->display
, x11
->selection_window
, prop
);
701 if (type_ret
!= type
) {
702 SELPRINTF("expected property type: %s, got: %s\n",
703 vdagent_x11_get_atom_name(x11
, type
),
704 vdagent_x11_get_atom_name(x11
, type_ret
));
708 if (format_ret
!= format
) {
709 SELPRINTF("expected %d bit format, got %d bits\n", format
, format_ret
);
713 /* Convert len to bytes */
718 len
*= sizeof(short);
727 if (x11
->clipboard_data_size
+ len
> x11
->clipboard_data_space
) {
728 void *old_clipboard_data
= x11
->clipboard_data
;
730 x11
->clipboard_data_space
= x11
->clipboard_data_size
+ len
;
731 x11
->clipboard_data
= realloc(x11
->clipboard_data
,
732 x11
->clipboard_data_space
);
733 if (!x11
->clipboard_data
) {
734 SELPRINTF("out of memory allocating clipboard buffer\n");
735 x11
->clipboard_data_space
= 0;
736 free(old_clipboard_data
);
740 memcpy(x11
->clipboard_data
+ x11
->clipboard_data_size
, data
, len
);
741 x11
->clipboard_data_size
+= len
;
742 VSELPRINTF("Appended %ld bytes to buffer\n", len
);
744 return 0; /* Wait for more data */
746 len
= x11
->clipboard_data_size
;
747 *data_ret
= x11
->clipboard_data
;
754 SELPRINTF("property contains no data (zero length)\n");
759 if ((incr
|| ret_val
== -1) && data
)
763 x11
->clipboard_data_size
= 0;
764 x11
->expect_property_notify
= 0;
770 static void vdagent_x11_get_selection_free(struct vdagent_x11
*x11
,
771 unsigned char *data
, int incr
)
774 /* If the clipboard has grown large return the memory to the system */
775 if (x11
->clipboard_data_space
> 512 * 1024) {
776 free(x11
->clipboard_data
);
777 x11
->clipboard_data
= NULL
;
778 x11
->clipboard_data_space
= 0;
784 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11
*x11
,
785 uint8_t selection
, Atom target
)
789 for (i
= 0; i
< clipboard_format_count
; i
++) {
790 for (j
= 0; j
< x11
->clipboard_formats
[i
].atom_count
; i
++) {
791 if (x11
->clipboard_formats
[i
].atoms
[j
] == target
) {
792 return x11
->clipboard_formats
[i
].type
;
797 SELPRINTF("unexpected selection type %s\n",
798 vdagent_x11_get_atom_name(x11
, target
));
799 return VD_AGENT_CLIPBOARD_NONE
;
802 static Atom
vdagent_x11_type_to_target(struct vdagent_x11
*x11
,
803 uint8_t selection
, uint32_t type
)
807 for (i
= 0; i
< x11
->clipboard_type_count
[selection
]; i
++) {
808 if (x11
->clipboard_agent_types
[selection
][i
] == type
) {
809 return x11
->clipboard_x11_targets
[selection
][i
];
812 SELPRINTF("client requested unavailable type %u\n", type
);
816 static void vdagent_x11_handle_conversion_request(struct vdagent_x11
*x11
)
820 if (!x11
->conversion_req
) {
824 vdagent_x11_get_clipboard_atom(x11
, x11
->conversion_req
->selection
, &clip
);
825 XConvertSelection(x11
->display
, clip
, x11
->conversion_req
->target
,
826 clip
, x11
->selection_window
, CurrentTime
);
829 static void vdagent_x11_handle_selection_notify(struct vdagent_x11
*x11
,
830 XEvent
*event
, int incr
)
833 unsigned char *data
= NULL
;
835 uint8_t selection
= -1;
838 if (!x11
->conversion_req
) {
839 fprintf(x11
->errfile
, "SelectionNotify received without a target\n");
842 vdagent_x11_get_clipboard_atom(x11
, x11
->conversion_req
->selection
, &clip
);
845 if (event
->xproperty
.atom
!= clip
||
846 event
->xproperty
.window
!= x11
->selection_window
) {
850 if (vdagent_x11_get_clipboard_selection(x11
, event
, &selection
)) {
852 } else if (selection
!= x11
->conversion_req
->selection
) {
853 SELPRINTF("Requested data for selection %d got %d\n",
854 (int)x11
->conversion_req
->selection
, (int)selection
);
857 if (event
->xselection
.target
!= x11
->conversion_req
->target
&&
858 event
->xselection
.target
!= x11
->incr_atom
) {
859 SELPRINTF("Requested %s target got %s\n",
860 vdagent_x11_get_atom_name(x11
, x11
->conversion_req
->target
),
861 vdagent_x11_get_atom_name(x11
, event
->xselection
.target
));
866 selection
= x11
->conversion_req
->selection
;
867 type
= vdagent_x11_target_to_type(x11
, selection
,
868 x11
->conversion_req
->target
);
869 if (len
== 0) { /* No errors so far */
870 len
= vdagent_x11_get_selection(x11
, event
, selection
,
871 x11
->conversion_req
->target
,
872 clip
, 8, &data
, incr
);
873 if (len
== 0) { /* waiting for more data? */
878 type
= VD_AGENT_CLIPBOARD_NONE
;
882 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_DATA
, selection
, type
,
884 vdagent_x11_get_selection_free(x11
, data
, incr
);
886 vdagent_x11_next_conversion_request(x11
);
887 vdagent_x11_handle_conversion_request(x11
);
890 static Atom
atom_lists_overlap(Atom
*atoms1
, Atom
*atoms2
, int l1
, int l2
)
894 for (i
= 0; i
< l1
; i
++)
895 for (j
= 0; j
< l2
; j
++)
896 if (atoms1
[i
] == atoms2
[j
])
902 static void vdagent_x11_print_targets(struct vdagent_x11
*x11
,
903 uint8_t selection
, const char *action
, Atom
*atoms
, int c
)
906 VSELPRINTF("%s %d targets:\n", action
, c
);
907 for (i
= 0; i
< c
; i
++)
908 VSELPRINTF("%s\n", vdagent_x11_get_atom_name(x11
, atoms
[i
]));
911 static void vdagent_x11_handle_targets_notify(struct vdagent_x11
*x11
,
915 Atom atom
, *atoms
= NULL
;
919 if (vdagent_x11_get_clipboard_selection(x11
, event
, &selection
)) {
923 if (!x11
->expected_targets_notifies
[selection
]) {
924 SELPRINTF("unexpected selection notify TARGETS\n");
928 x11
->expected_targets_notifies
[selection
]--;
930 /* If we have more targets_notifies pending, ignore this one, we
931 are only interested in the targets list of the current owner
932 (which is the last one we've requested a targets list from) */
933 if (x11
->expected_targets_notifies
[selection
]) {
937 len
= vdagent_x11_get_selection(x11
, event
, selection
,
938 XA_ATOM
, x11
->targets_atom
, 32,
939 (unsigned char **)&atoms
, 0);
940 if (len
== 0 || len
== -1) /* waiting for more data or error? */
945 vdagent_x11_print_targets(x11
, selection
, "received", atoms
, len
);
947 type_count
= &x11
->clipboard_type_count
[selection
];
949 for (i
= 0; i
< clipboard_format_count
; i
++) {
950 atom
= atom_lists_overlap(x11
->clipboard_formats
[i
].atoms
, atoms
,
951 x11
->clipboard_formats
[i
].atom_count
, len
);
953 x11
->clipboard_agent_types
[selection
][*type_count
] =
954 x11
->clipboard_formats
[i
].type
;
955 x11
->clipboard_x11_targets
[selection
][*type_count
] = atom
;
958 sizeof(x11
->clipboard_agent_types
[0])/sizeof(uint32_t)) {
959 SELPRINTF("handle_targets_notify: too many types\n");
966 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_GRAB
, selection
, 0,
967 (uint8_t *)x11
->clipboard_agent_types
[selection
],
968 *type_count
* sizeof(uint32_t));
969 vdagent_x11_set_clipboard_owner(x11
, selection
, owner_guest
);
972 vdagent_x11_get_selection_free(x11
, (unsigned char *)atoms
, 0);
975 static void vdagent_x11_send_selection_notify(struct vdagent_x11
*x11
,
976 Atom prop
, struct vdagent_x11_selection_request
*request
)
981 event
= &request
->event
;
983 event
= &x11
->selection_req
->event
;
986 res
.xselection
.property
= prop
;
987 res
.xselection
.type
= SelectionNotify
;
988 res
.xselection
.display
= event
->xselectionrequest
.display
;
989 res
.xselection
.requestor
= event
->xselectionrequest
.requestor
;
990 res
.xselection
.selection
= event
->xselectionrequest
.selection
;
991 res
.xselection
.target
= event
->xselectionrequest
.target
;
992 res
.xselection
.time
= event
->xselectionrequest
.time
;
993 XSendEvent(x11
->display
, event
->xselectionrequest
.requestor
, 0, 0, &res
);
996 vdagent_x11_next_selection_request(x11
);
997 vdagent_x11_handle_selection_request(x11
);
1001 static void vdagent_x11_send_targets(struct vdagent_x11
*x11
,
1002 uint8_t selection
, XEvent
*event
)
1004 Atom prop
, targets
[256] = { x11
->targets_atom
, };
1005 int i
, j
, k
, target_count
= 1;
1007 for (i
= 0; i
< x11
->clipboard_type_count
[selection
]; i
++) {
1008 for (j
= 0; j
< clipboard_format_count
; j
++) {
1009 if (x11
->clipboard_formats
[j
].type
!=
1010 x11
->clipboard_agent_types
[selection
][i
])
1013 for (k
= 0; k
< x11
->clipboard_formats
[j
].atom_count
; k
++) {
1014 targets
[target_count
] = x11
->clipboard_formats
[j
].atoms
[k
];
1016 if (target_count
== sizeof(targets
)/sizeof(Atom
)) {
1017 SELPRINTF("send_targets: too many targets\n");
1025 prop
= event
->xselectionrequest
.property
;
1027 prop
= event
->xselectionrequest
.target
;
1029 XChangeProperty(x11
->display
, event
->xselectionrequest
.requestor
, prop
,
1030 XA_ATOM
, 32, PropModeReplace
, (unsigned char *)&targets
,
1032 vdagent_x11_print_targets(x11
, selection
, "sent", targets
, target_count
);
1033 vdagent_x11_send_selection_notify(x11
, prop
, NULL
);
1036 static void vdagent_x11_handle_selection_request(struct vdagent_x11
*x11
)
1039 uint32_t type
= VD_AGENT_CLIPBOARD_NONE
;
1042 if (!x11
->selection_req
)
1045 event
= &x11
->selection_req
->event
;
1046 selection
= x11
->selection_req
->selection
;
1048 if (x11
->clipboard_owner
[selection
] != owner_client
) {
1049 SELPRINTF("received selection request event for target %s, "
1050 "while not owning client clipboard\n",
1051 vdagent_x11_get_atom_name(x11
, event
->xselectionrequest
.target
));
1052 vdagent_x11_send_selection_notify(x11
, None
, NULL
);
1056 if (event
->xselectionrequest
.target
== x11
->multiple_atom
) {
1057 SELPRINTF("multiple target not supported\n");
1058 vdagent_x11_send_selection_notify(x11
, None
, NULL
);
1062 if (event
->xselectionrequest
.target
== x11
->targets_atom
) {
1063 vdagent_x11_send_targets(x11
, selection
, event
);
1067 type
= vdagent_x11_target_to_type(x11
, selection
,
1068 event
->xselectionrequest
.target
);
1069 if (type
== VD_AGENT_CLIPBOARD_NONE
) {
1070 vdagent_x11_send_selection_notify(x11
, None
, NULL
);
1074 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_REQUEST
, selection
, type
,
1078 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11
*x11
,
1085 assert(x11
->selection_req
);
1086 sel_event
= &x11
->selection_req
->event
;
1087 selection
= x11
->selection_req
->selection
;
1088 if (del_event
->xproperty
.window
!= sel_event
->xselectionrequest
.requestor
1089 || del_event
->xproperty
.atom
!= x11
->selection_req_atom
) {
1093 len
= x11
->selection_req_data_size
- x11
->selection_req_data_pos
;
1094 if (len
> x11
->max_prop_size
) {
1095 len
= x11
->max_prop_size
;
1099 VSELPRINTF("Sending %d-%d/%d bytes of clipboard data\n",
1100 x11
->selection_req_data_pos
,
1101 x11
->selection_req_data_pos
+ len
- 1,
1102 x11
->selection_req_data_size
);
1104 VSELPRINTF("Ending incr send of clipboard data\n");
1106 XChangeProperty(x11
->display
, sel_event
->xselectionrequest
.requestor
,
1107 x11
->selection_req_atom
,
1108 sel_event
->xselectionrequest
.target
, 8, PropModeReplace
,
1109 x11
->selection_req_data
+ x11
->selection_req_data_pos
,
1111 x11
->selection_req_data_pos
+= len
;
1113 /* Note we must explictly send a 0 sized XChangeProperty to signal the
1114 incr transfer is done. Hence we do not check if we've send all data
1115 but instead check we've send the final 0 sized XChangeProperty. */
1117 free(x11
->selection_req_data
);
1118 x11
->selection_req_data
= NULL
;
1119 x11
->selection_req_data_pos
= 0;
1120 x11
->selection_req_data_size
= 0;
1121 x11
->selection_req_atom
= None
;
1122 vdagent_x11_next_selection_request(x11
);
1123 vdagent_x11_handle_selection_request(x11
);
1127 void vdagent_x11_set_monitor_config(struct vdagent_x11
*x11
,
1128 VDAgentMonitorsConfig
*mon_config
)
1130 int i
, num_sizes
= 0;
1132 unsigned int closest_diff
= -1;
1133 XRRScreenSize
* sizes
;
1134 XRRScreenConfiguration
* config
;
1137 if (!x11
->has_xrandr
)
1140 if (mon_config
->num_of_monitors
!= 1) {
1141 fprintf(x11
->errfile
,
1142 "Only 1 monitor supported, ignoring additional monitors\n");
1145 sizes
= XRRSizes(x11
->display
, x11
->screen
, &num_sizes
);
1146 if (!sizes
|| !num_sizes
) {
1147 fprintf(x11
->errfile
, "XRRSizes failed\n");
1151 /* Find the closest size which will fit within the monitor */
1152 for (i
= 0; i
< num_sizes
; i
++) {
1153 if (sizes
[i
].width
> mon_config
->monitors
[0].width
||
1154 sizes
[i
].height
> mon_config
->monitors
[0].height
)
1155 continue; /* Too large for the monitor */
1157 unsigned int wdiff
= mon_config
->monitors
[0].width
- sizes
[i
].width
;
1158 unsigned int hdiff
= mon_config
->monitors
[0].height
- sizes
[i
].height
;
1159 unsigned int diff
= wdiff
* wdiff
+ hdiff
* hdiff
;
1160 if (diff
< closest_diff
) {
1161 closest_diff
= diff
;
1167 fprintf(x11
->errfile
, "no suitable resolution found for monitor\n");
1171 config
= XRRGetScreenInfo(x11
->display
, x11
->root_window
);
1173 fprintf(x11
->errfile
, "get screen info failed\n");
1176 XRRConfigCurrentConfiguration(config
, &rotation
);
1177 XRRSetScreenConfig(x11
->display
, config
, x11
->root_window
, best
,
1178 rotation
, CurrentTime
);
1179 XRRFreeScreenConfigInfo(config
);
1180 x11
->width
= sizes
[best
].width
;
1181 x11
->height
= sizes
[best
].height
;
1182 vdagent_x11_send_daemon_guest_xorg_res(x11
);
1184 /* Flush output buffers and consume any pending events */
1185 vdagent_x11_do_read(x11
);
1188 void vdagent_x11_clipboard_request(struct vdagent_x11
*x11
,
1189 uint8_t selection
, uint32_t type
)
1192 struct vdagent_x11_conversion_request
*req
, *new_req
;
1194 /* We don't use clip here, but we call get_clipboard_atom to verify
1195 selection is valid */
1196 if (vdagent_x11_get_clipboard_atom(x11
, selection
, &clip
)) {
1200 if (x11
->clipboard_owner
[selection
] != owner_guest
) {
1201 SELPRINTF("received clipboard req while not owning guest clipboard\n");
1205 target
= vdagent_x11_type_to_target(x11
, selection
, type
);
1206 if (target
== None
) {
1210 new_req
= malloc(sizeof(*new_req
));
1212 SELPRINTF("out of memory on client clipboard request, ignoring.\n");
1216 new_req
->target
= target
;
1217 new_req
->selection
= selection
;
1218 new_req
->next
= NULL
;
1220 if (!x11
->conversion_req
) {
1221 x11
->conversion_req
= new_req
;
1222 vdagent_x11_handle_conversion_request(x11
);
1223 /* Flush output buffers and consume any pending events */
1224 vdagent_x11_do_read(x11
);
1228 /* maybe we should limit the conversion_request stack depth ? */
1229 req
= x11
->conversion_req
;
1233 req
->next
= new_req
;
1237 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_DATA
,
1238 selection
, VD_AGENT_CLIPBOARD_NONE
, NULL
, 0);
1241 void vdagent_x11_clipboard_grab(struct vdagent_x11
*x11
, uint8_t selection
,
1242 uint32_t *types
, uint32_t type_count
)
1246 if (vdagent_x11_get_clipboard_atom(x11
, selection
, &clip
)) {
1250 if (type_count
> sizeof(x11
->clipboard_agent_types
[0])/sizeof(uint32_t)) {
1251 SELPRINTF("x11_clipboard_grab: too many types\n");
1252 type_count
= sizeof(x11
->clipboard_agent_types
[0])/sizeof(uint32_t);
1255 memcpy(x11
->clipboard_agent_types
[selection
], types
,
1256 type_count
* sizeof(uint32_t));
1257 x11
->clipboard_type_count
[selection
] = type_count
;
1259 XSetSelectionOwner(x11
->display
, clip
,
1260 x11
->selection_window
, CurrentTime
);
1261 vdagent_x11_set_clipboard_owner(x11
, selection
, owner_client
);
1263 /* Flush output buffers and consume any pending events */
1264 vdagent_x11_do_read(x11
);
1267 void vdagent_x11_clipboard_data(struct vdagent_x11
*x11
, uint8_t selection
,
1268 uint32_t type
, uint8_t *data
, uint32_t size
)
1272 uint32_t type_from_event
;
1274 if (x11
->selection_req_data
) {
1276 SELPRINTF("received clipboard data while still sending"
1277 " data from previous request, ignoring\n");
1283 if (!x11
->selection_req
) {
1285 SELPRINTF("received clipboard data without an "
1286 "outstanding selection request, ignoring\n");
1292 event
= &x11
->selection_req
->event
;
1293 type_from_event
= vdagent_x11_target_to_type(x11
,
1294 x11
->selection_req
->selection
,
1295 event
->xselectionrequest
.target
);
1296 if (type_from_event
!= type
||
1297 selection
!= x11
->selection_req
->selection
) {
1298 if (selection
!= x11
->selection_req
->selection
) {
1299 SELPRINTF("expecting data for selection %d got %d\n",
1300 (int)x11
->selection_req
->selection
, (int)selection
);
1302 if (type_from_event
!= type
) {
1303 SELPRINTF("expecting type %u clipboard data got %u\n",
1304 type_from_event
, type
);
1306 vdagent_x11_send_selection_notify(x11
, None
, NULL
);
1309 /* Flush output buffers and consume any pending events */
1310 vdagent_x11_do_read(x11
);
1314 prop
= event
->xselectionrequest
.property
;
1316 prop
= event
->xselectionrequest
.target
;
1318 if (size
> x11
->max_prop_size
) {
1319 unsigned long len
= size
;
1320 VSELPRINTF("Starting incr send of clipboard data\n");
1321 x11
->selection_req_data
= data
;
1322 x11
->selection_req_data_pos
= 0;
1323 x11
->selection_req_data_size
= size
;
1324 x11
->selection_req_atom
= prop
;
1325 XSelectInput(x11
->display
, event
->xselectionrequest
.requestor
,
1326 PropertyChangeMask
);
1327 XChangeProperty(x11
->display
, event
->xselectionrequest
.requestor
, prop
,
1328 x11
->incr_atom
, 32, PropModeReplace
,
1329 (unsigned char*)&len
, 1);
1330 vdagent_x11_send_selection_notify(x11
, prop
, x11
->selection_req
);
1332 XChangeProperty(x11
->display
, event
->xselectionrequest
.requestor
, prop
,
1333 event
->xselectionrequest
.target
, 8, PropModeReplace
,
1335 vdagent_x11_send_selection_notify(x11
, prop
, NULL
);
1339 /* Flush output buffers and consume any pending events */
1340 vdagent_x11_do_read(x11
);
1343 void vdagent_x11_clipboard_release(struct vdagent_x11
*x11
, uint8_t selection
)
1348 if (vdagent_x11_get_clipboard_atom(x11
, selection
, &clip
)) {
1352 if (x11
->clipboard_owner
[selection
] != owner_client
) {
1353 SELPRINTF("received release while not owning client clipboard\n");
1357 XSetSelectionOwner(x11
->display
, clip
, None
, CurrentTime
);
1358 /* Make sure we process the XFixesSetSelectionOwnerNotify event caused
1359 by this, so we don't end up changing the clipboard owner to none, after
1360 it has already been re-owned because this event is still pending. */
1361 XSync(x11
->display
, False
);
1362 while (XCheckTypedEvent(x11
->display
, x11
->xfixes_event_base
,
1364 vdagent_x11_handle_event(x11
, event
);
1366 /* Note no need to do a set_clipboard_owner(owner_none) here, as that is
1367 already done by processing the XFixesSetSelectionOwnerNotify event. */
1369 /* Flush output buffers and consume any pending events */
1370 vdagent_x11_do_read(x11
);