vdagent-x11: Add printing of selection to relevant log messages
[vd_agent.git] / src / vdagent-x11.c
blobf4ef431f9d1a38048331d5d75c011c07e6afd5e6
1 /* vdagent-x11.c vdagent x11 code
3 Copyright 2010-2011 Red Hat, Inc.
5 Red Hat Authors:
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. */
34 #include <stdlib.h>
35 #include <limits.h>
36 #include <string.h>
37 #include <assert.h>
38 #include <X11/Xatom.h>
39 #include <X11/Xlib.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, ...) \
51 do { \
52 if (x11->verbose) { \
53 fprintf(x11->errfile, "%s: " format, \
54 vdagent_x11_sel_to_str(selection), ##__VA_ARGS__); \
55 } \
56 } while (0)
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 {
65 XEvent event;
66 uint8_t selection;
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 {
75 Atom target;
76 uint8_t selection;
77 struct vdagent_x11_conversion_request *next;
80 struct clipboard_format_tmpl {
81 uint32_t type;
82 const char *atom_names[16];
85 struct clipboard_format_info {
86 uint32_t type;
87 Atom atoms[16];
88 int atom_count;
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]))
103 struct vdagent_x11 {
104 struct clipboard_format_info clipboard_formats[clipboard_format_count];
105 Display *display;
106 Atom clipboard_atom;
107 Atom clipboard_primary_atom;
108 Atom targets_atom;
109 Atom incr_atom;
110 Atom multiple_atom;
111 Window root_window;
112 Window selection_window;
113 struct udscs_connection *vdagentd;
114 FILE *errfile;
115 int verbose;
116 int fd;
117 int screen;
118 int width;
119 int height;
120 int has_xrandr;
121 int has_xfixes;
122 int xfixes_event_base;
123 int max_prop_size;
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,
148 XEvent *event);
149 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11,
150 XEvent *del_event);
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) {
157 switch (selection) {
158 case VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD:
159 return "clipboard";
160 case VD_AGENT_CLIPBOARD_SELECTION_PRIMARY:
161 return "primary";
162 case VD_AGENT_CLIPBOARD_SELECTION_SECONDARY:
163 return "secondary";
164 default:
165 return "unknown";
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));
177 if (!x11) {
178 fprintf(errfile, "out of memory allocating vdagent_x11 struct\n");
179 return NULL;
182 x11->vdagentd = vdagentd;
183 x11->errfile = errfile;
184 x11->verbose = verbose;
186 x11->display = XOpenDisplay(NULL);
187 if (!x11->display) {
188 fprintf(x11->errfile, "could not connect to X-server\n");
189 free(x11);
190 return NULL;
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],
207 False);
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);
215 if (x11->verbose)
216 fprintf(x11->errfile, "Selection window: %u\n",
217 (unsigned int)x11->selection_window);
219 if (XRRQueryExtension(x11->display, &i, &i))
220 x11->has_xrandr = 1;
221 else
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) {
226 x11->has_xfixes = 1;
227 XFixesSelectSelectionInput(x11->display, x11->root_window,
228 x11->clipboard_atom,
229 XFixesSetSelectionOwnerNotifyMask|
230 XFixesSelectionWindowDestroyNotifyMask|
231 XFixesSelectionClientCloseNotifyMask);
232 XFixesSelectSelectionInput(x11->display, x11->root_window,
233 x11->clipboard_primary_atom,
234 XFixesSetSelectionOwnerNotifyMask|
235 XFixesSelectionWindowDestroyNotifyMask|
236 XFixesSelectionClientCloseNotifyMask);
237 } else
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;
244 } else {
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);
263 return x11;
266 void vdagent_x11_destroy(struct vdagent_x11 *x11)
268 uint8_t sel;
270 if (!x11)
271 return;
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);
278 free(x11);
281 int vdagent_x11_get_fd(struct vdagent_x11 *x11)
283 return x11->fd;
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;
307 int once;
309 /* Clear pending requests and clipboard data */
310 once = 1;
311 prev_sel = NULL;
312 next_sel = x11->selection_req;
313 while (next_sel) {
314 curr_sel = next_sel;
315 next_sel = curr_sel->next;
316 if (curr_sel->selection == selection) {
317 if (once) {
318 SELPRINTF("selection requests pending on clipboard ownership "
319 "change, clearing\n");
320 once = 0;
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;
330 } else {
331 prev_sel->next = next_sel;
333 free(curr_sel);
334 } else {
335 prev_sel = curr_sel;
339 once = 1;
340 prev_conv = NULL;
341 next_conv = x11->conversion_req;
342 while (next_conv) {
343 curr_conv = next_conv;
344 next_conv = curr_conv->next;
345 if (curr_conv->selection == selection) {
346 if (once) {
347 SELPRINTF("client clipboard request pending on clipboard "
348 "ownership change, clearing\n");
349 once = 0;
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;
357 } else {
358 prev_conv->next = next_conv;
360 free(curr_conv);
361 } else {
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,
371 0, NULL, 0);
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;
384 } else {
385 fprintf(x11->errfile, "get_clipboard_atom: unknown selection\n");
386 return -1;
389 return 0;
392 static int vdagent_x11_get_clipboard_selection(struct vdagent_x11 *x11,
393 XEvent *event, uint8_t *selection)
395 Atom atom;
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;
404 } else {
405 fprintf(x11->errfile, "get_clipboard_selection: unknown event type\n");
406 return -1;
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;
413 } else {
414 fprintf(x11->errfile, "get_clipboard_selection: unknown selection\n");
415 return -1;
418 return 0;
421 static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
423 int handled = 0;
424 uint8_t selection;
426 if (event.type == x11->xfixes_event_base) {
427 union {
428 XEvent ev;
429 XFixesSelectionNotifyEvent xfev;
430 } ev;
432 if (vdagent_x11_get_clipboard_selection(x11, &event, &selection)) {
433 return;
436 ev.ev = event;
437 switch (ev.xfev.subtype) {
438 case XFixesSetSelectionOwnerNotify:
439 break;
440 /* Treat ... as a SelectionOwnerNotify None */
441 case XFixesSelectionWindowDestroyNotify:
442 case XFixesSelectionClientCloseNotify:
443 ev.xfev.owner = None;
444 break;
445 default:
446 VSELPRINTF("unexpected xfix event subtype %d window %d\n",
447 (int)ev.xfev.subtype, (int)event.xany.window);
448 return;
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)
454 return;
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)
460 return;
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,
465 CurrentTime);
466 x11->expected_targets_notifies[selection]++;
467 return;
470 switch (event.type) {
471 case ConfigureNotify:
472 if (event.xconfigure.window != x11->root_window)
473 break;
475 handled = 1;
477 if (event.xconfigure.width == x11->width &&
478 event.xconfigure.height == x11->height)
479 break;
481 x11->width = event.xconfigure.width;
482 x11->height = event.xconfigure.height;
484 vdagent_x11_send_daemon_guest_xorg_res(x11);
485 break;
486 case SelectionNotify:
487 if (event.xselection.target == x11->targets_atom)
488 vdagent_x11_handle_targets_notify(x11, &event);
489 else
490 vdagent_x11_handle_selection_notify(x11, &event, 0);
492 handled = 1;
493 break;
494 case PropertyNotify:
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. */
505 handled = 1;
506 break;
507 case SelectionClear:
508 /* Do nothing the clipboard ownership will get updated through
509 the XFixesSetSelectionOwnerNotify event */
510 handled = 1;
511 break;
512 case SelectionRequest: {
513 struct vdagent_x11_selection_request *req, *new_req;
515 if (vdagent_x11_get_clipboard_selection(x11, &event, &selection)) {
516 return;
519 new_req = malloc(sizeof(*new_req));
520 if (!new_req) {
521 SELPRINTF("out of memory on SelectionRequest, ignoring.\n");
522 break;
525 handled = 1;
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);
534 break;
537 /* maybe we should limit the selection_request stack depth ? */
538 req = x11->selection_req;
539 while (req->next)
540 req = req->next;
542 req->next = new_req;
543 break;
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)
553 XEvent event;
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)
574 if (a == None)
575 return "None";
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;
585 Atom type_ret;
586 int format_ret, ret_val = -1;
587 unsigned long len, remain;
588 unsigned char *data = NULL;
590 *data_ret = NULL;
592 if (!incr) {
593 if (event->xselection.property == None) {
594 VSELPRINTF("XConvertSelection refused by clipboard owner\n");
595 goto exit;
598 if (event->xselection.requestor != x11->selection_window ||
599 event->xselection.property != prop) {
600 SELPRINTF("SelectionNotify parameters mismatch\n");
601 goto exit;
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");
609 goto exit;
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");
619 goto exit;
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;
628 goto exit;
630 x11->clipboard_data_space = prop_min_size;
632 x11->expect_property_notify = 1;
633 XSelectInput(x11->display, x11->selection_window,
634 PropertyChangeMask);
635 XDeleteProperty(x11->display, x11->selection_window, prop);
636 XFree(data);
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));
646 goto exit;
649 if (format_ret != format) {
650 SELPRINTF("expected %d bit format, got %d bits\n", format, format_ret);
651 goto exit;
654 /* Convert len to bytes */
655 switch(format) {
656 case 8:
657 break;
658 case 16:
659 len *= sizeof(short);
660 break;
661 case 32:
662 len *= sizeof(long);
663 break;
666 if (incr) {
667 if (len) {
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);
678 goto exit;
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);
684 XFree(data);
685 return 0; /* Wait for more data */
687 len = x11->clipboard_data_size;
688 *data_ret = x11->clipboard_data;
689 } else
690 *data_ret = data;
692 if (len > 0) {
693 ret_val = len;
694 } else {
695 SELPRINTF("property contains no data (zero length)\n");
696 *data_ret = NULL;
699 exit:
700 if ((incr || ret_val == -1) && data)
701 XFree(data);
703 if (incr) {
704 x11->clipboard_data_size = 0;
705 x11->expect_property_notify = 0;
708 return ret_val;
711 static void vdagent_x11_get_selection_free(struct vdagent_x11 *x11,
712 unsigned char *data, int incr)
714 if (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;
721 } else if (data)
722 XFree(data);
725 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11 *x11,
726 uint8_t selection, Atom target)
728 int i, j;
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)
746 int i;
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);
754 return None;
757 static void vdagent_x11_handle_conversion_request(struct vdagent_x11 *x11)
759 Atom clip;
761 if (!x11->conversion_req) {
762 return;
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)
773 int len = 0;
774 unsigned char *data = NULL;
775 uint32_t type;
776 uint8_t selection = -1;
777 Atom clip;
779 if (!x11->conversion_req) {
780 fprintf(x11->errfile, "SelectionNotify received without a target\n");
781 return;
783 vdagent_x11_get_clipboard_atom(x11, x11->conversion_req->selection, &clip);
785 if (incr) {
786 if (event->xproperty.atom != clip ||
787 event->xproperty.window != x11->selection_window) {
788 return;
790 } else {
791 if (vdagent_x11_get_clipboard_selection(x11, event, &selection)) {
792 len = -1;
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);
796 len = -1;
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));
803 len = -1;
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? */
815 return;
818 if (len == -1) {
819 type = VD_AGENT_CLIPBOARD_NONE;
820 len = 0;
823 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, selection, type,
824 data, len);
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)
833 int i, j;
835 for (i = 0; i < l1; i++)
836 for (j = 0; j < l2; j++)
837 if (atoms1[i] == atoms2[j])
838 return atoms1[i];
840 return 0;
843 static void vdagent_x11_print_targets(struct vdagent_x11 *x11,
844 uint8_t selection, const char *action, Atom *atoms, int c)
846 int i;
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,
853 XEvent *event)
855 int i, len;
856 Atom atom, *atoms = NULL;
857 uint8_t selection;
858 int *type_count;
860 if (vdagent_x11_get_clipboard_selection(x11, event, &selection)) {
861 return;
864 if (!x11->expected_targets_notifies[selection]) {
865 SELPRINTF("unexpected selection notify TARGETS\n");
866 return;
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]) {
875 return;
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? */
882 return;
884 /* bytes -> atoms */
885 len /= sizeof(Atom);
886 vdagent_x11_print_targets(x11, selection, "received", atoms, len);
888 type_count = &x11->clipboard_type_count[selection];
889 *type_count = 0;
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);
893 if (atom) {
894 x11->clipboard_agent_types[selection][*type_count] =
895 x11->clipboard_formats[i].type;
896 x11->clipboard_x11_targets[selection][*type_count] = atom;
897 (*type_count)++;
898 if (*type_count ==
899 sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t)) {
900 SELPRINTF("handle_targets_notify: too many types\n");
901 break;
906 if (*type_count) {
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)
919 XEvent res, *event;
921 if (request) {
922 event = &request->event;
923 } else {
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);
936 if (!request) {
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])
952 continue;
954 for (k = 0; k < x11->clipboard_formats[j].atom_count; k++) {
955 targets[target_count] = x11->clipboard_formats[j].atoms[k];
956 target_count++;
957 if (target_count == sizeof(targets)/sizeof(Atom)) {
958 SELPRINTF("send_targets: too many targets\n");
959 goto exit_loop;
964 exit_loop:
966 prop = event->xselectionrequest.property;
967 if (prop == None)
968 prop = event->xselectionrequest.target;
970 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
971 XA_ATOM, 32, PropModeReplace, (unsigned char *)&targets,
972 target_count);
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)
979 XEvent *event;
980 uint32_t type = VD_AGENT_CLIPBOARD_NONE;
981 uint8_t selection;
983 if (!x11->selection_req)
984 return;
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);
994 return;
997 if (event->xselectionrequest.target == x11->multiple_atom) {
998 SELPRINTF("multiple target not supported\n");
999 vdagent_x11_send_selection_notify(x11, None, NULL);
1000 return;
1003 if (event->xselectionrequest.target == x11->targets_atom) {
1004 vdagent_x11_send_targets(x11, selection, event);
1005 return;
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);
1012 return;
1015 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_REQUEST, selection, type,
1016 NULL, 0);
1019 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11,
1020 XEvent *del_event)
1022 XEvent *sel_event;
1023 int len;
1024 uint8_t selection;
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) {
1031 return;
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;
1039 if (len) {
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);
1044 } else {
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,
1051 len);
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. */
1057 if (len == 0) {
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;
1072 int best = -1;
1073 unsigned int closest_diff = -1;
1074 XRRScreenSize* sizes;
1075 XRRScreenConfiguration* config;
1076 Rotation rotation;
1078 if (!x11->has_xrandr)
1079 return;
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");
1089 return;
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;
1103 best = i;
1107 if (best == -1) {
1108 fprintf(x11->errfile, "no suitable resolution found for monitor\n");
1109 return;
1112 config = XRRGetScreenInfo(x11->display, x11->root_window);
1113 if(!config) {
1114 fprintf(x11->errfile, "get screen info failed\n");
1115 return;
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)
1132 Atom target, clip;
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)) {
1138 goto none;
1141 if (x11->clipboard_owner[selection] != owner_guest) {
1142 SELPRINTF("received clipboard req while not owning guest clipboard\n");
1143 goto none;
1146 target = vdagent_x11_type_to_target(x11, selection, type);
1147 if (target == None) {
1148 goto none;
1151 new_req = malloc(sizeof(*new_req));
1152 if (!new_req) {
1153 SELPRINTF("out of memory on client clipboard request, ignoring.\n");
1154 return;
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);
1166 return;
1169 /* maybe we should limit the conversion_request stack depth ? */
1170 req = x11->conversion_req;
1171 while (req->next)
1172 req = req->next;
1174 req->next = new_req;
1175 return;
1177 none:
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)
1185 Atom clip;
1187 if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) {
1188 return;
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)
1211 Atom prop;
1212 XEvent *event;
1213 uint32_t type_from_event;
1215 if (x11->selection_req_data) {
1216 if (type || size) {
1217 SELPRINTF("received clipboard data while still sending"
1218 " data from previous request, ignoring\n");
1220 free(data);
1221 return;
1224 if (!x11->selection_req) {
1225 if (type || size) {
1226 SELPRINTF("received clipboard data without an "
1227 "outstanding selection request, ignoring\n");
1229 free(data);
1230 return;
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);
1248 free(data);
1250 /* Flush output buffers and consume any pending events */
1251 vdagent_x11_do_read(x11);
1252 return;
1255 prop = event->xselectionrequest.property;
1256 if (prop == None)
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);
1272 } else {
1273 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
1274 event->xselectionrequest.target, 8, PropModeReplace,
1275 data, size);
1276 vdagent_x11_send_selection_notify(x11, prop, NULL);
1277 free(data);
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)
1286 XEvent event;
1287 Atom clip;
1289 if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) {
1290 return;
1293 if (x11->clipboard_owner[selection] != owner_client) {
1294 SELPRINTF("received release while not owning client clipboard\n");
1295 return;
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,
1304 &event))
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);