Add a --enable-static-uinput option
[vd_agent/hramrach.git] / src / vdagent-x11.c
blobc9e99ee38aaac5068395e5e406b2ba88955e1606
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/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, ...) \
52 do { \
53 if (x11->verbose) { \
54 fprintf(x11->errfile, "%s: " format, \
55 vdagent_x11_sel_to_str(selection), ##__VA_ARGS__); \
56 } \
57 } while (0)
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 {
66 XEvent event;
67 uint8_t selection;
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 {
76 Atom target;
77 uint8_t selection;
78 struct vdagent_x11_conversion_request *next;
81 struct clipboard_format_tmpl {
82 uint32_t type;
83 const char *atom_names[16];
86 struct clipboard_format_info {
87 uint32_t type;
88 Atom atoms[16];
89 int atom_count;
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]))
104 struct vdagent_x11 {
105 struct clipboard_format_info clipboard_formats[clipboard_format_count];
106 Display *display;
107 Atom clipboard_atom;
108 Atom clipboard_primary_atom;
109 Atom targets_atom;
110 Atom incr_atom;
111 Atom multiple_atom;
112 Window root_window;
113 Window selection_window;
114 struct udscs_connection *vdagentd;
115 FILE *errfile;
116 int verbose;
117 int fd;
118 int screen;
119 int width;
120 int height;
121 int has_xrandr;
122 int has_xinerama;
123 int has_xfixes;
124 int xfixes_event_base;
125 int max_prop_size;
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,
150 XEvent *event);
151 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11,
152 XEvent *del_event);
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) {
159 switch (selection) {
160 case VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD:
161 return "clipboard";
162 case VD_AGENT_CLIPBOARD_SELECTION_PRIMARY:
163 return "primary";
164 case VD_AGENT_CLIPBOARD_SELECTION_SECONDARY:
165 return "secondary";
166 default:
167 return "unknown";
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));
179 if (!x11) {
180 fprintf(errfile, "out of memory allocating vdagent_x11 struct\n");
181 return NULL;
184 x11->vdagentd = vdagentd;
185 x11->errfile = errfile;
186 x11->verbose = verbose;
188 x11->display = XOpenDisplay(NULL);
189 if (!x11->display) {
190 fprintf(x11->errfile, "could not connect to X-server\n");
191 free(x11);
192 return NULL;
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],
209 False);
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);
217 if (x11->verbose)
218 fprintf(x11->errfile, "Selection window: %u\n",
219 (unsigned int)x11->selection_window);
221 if (XRRQueryExtension(x11->display, &i, &i))
222 x11->has_xrandr = 1;
224 if (XineramaQueryExtension(x11->display, &i, &i))
225 x11->has_xinerama = 1;
227 switch (x11->has_xrandr << 4 | x11->has_xinerama) {
228 case 0x00:
229 fprintf(x11->errfile, "Neither Xrandr nor Xinerama found, assuming single monitor setup\n");
230 break;
231 case 0x01:
232 if (x11->verbose)
233 fprintf(x11->errfile, "Found Xinerama extension without Xrandr, assuming a multi monitor setup\n");
234 break;
235 case 0x10:
236 fprintf(x11->errfile, "Found Xrandr but no Xinerama, weird! Assuming a single monitor setup\n");
237 break;
238 case 0x11:
239 /* Standard single monitor setup, nothing to see here */
240 break;
243 if (XFixesQueryExtension(x11->display, &x11->xfixes_event_base, &i) &&
244 XFixesQueryVersion(x11->display, &major, &minor) && major >= 1) {
245 x11->has_xfixes = 1;
246 XFixesSelectSelectionInput(x11->display, x11->root_window,
247 x11->clipboard_atom,
248 XFixesSetSelectionOwnerNotifyMask|
249 XFixesSelectionWindowDestroyNotifyMask|
250 XFixesSelectionClientCloseNotifyMask);
251 XFixesSelectSelectionInput(x11->display, x11->root_window,
252 x11->clipboard_primary_atom,
253 XFixesSetSelectionOwnerNotifyMask|
254 XFixesSelectionWindowDestroyNotifyMask|
255 XFixesSelectionClientCloseNotifyMask);
256 } else
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;
263 } else {
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);
282 return x11;
285 void vdagent_x11_destroy(struct vdagent_x11 *x11)
287 uint8_t sel;
289 if (!x11)
290 return;
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);
297 free(x11);
300 int vdagent_x11_get_fd(struct vdagent_x11 *x11)
302 return x11->fd;
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;
326 int once;
328 /* Clear pending requests and clipboard data */
329 once = 1;
330 prev_sel = NULL;
331 next_sel = x11->selection_req;
332 while (next_sel) {
333 curr_sel = next_sel;
334 next_sel = curr_sel->next;
335 if (curr_sel->selection == selection) {
336 if (once) {
337 SELPRINTF("selection requests pending on clipboard ownership "
338 "change, clearing\n");
339 once = 0;
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;
349 } else {
350 prev_sel->next = next_sel;
352 free(curr_sel);
353 } else {
354 prev_sel = curr_sel;
358 once = 1;
359 prev_conv = NULL;
360 next_conv = x11->conversion_req;
361 while (next_conv) {
362 curr_conv = next_conv;
363 next_conv = curr_conv->next;
364 if (curr_conv->selection == selection) {
365 if (once) {
366 SELPRINTF("client clipboard request pending on clipboard "
367 "ownership change, clearing\n");
368 once = 0;
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;
376 } else {
377 prev_conv->next = next_conv;
379 free(curr_conv);
380 } else {
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,
390 0, NULL, 0);
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;
403 } else {
404 fprintf(x11->errfile, "get_clipboard_atom: unknown selection\n");
405 return -1;
408 return 0;
411 static int vdagent_x11_get_clipboard_selection(struct vdagent_x11 *x11,
412 XEvent *event, uint8_t *selection)
414 Atom atom;
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;
423 } else {
424 fprintf(x11->errfile, "get_clipboard_selection: unknown event type\n");
425 return -1;
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;
432 } else {
433 fprintf(x11->errfile, "get_clipboard_selection: unknown selection\n");
434 return -1;
437 return 0;
440 static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
442 int handled = 0;
443 uint8_t selection;
445 if (event.type == x11->xfixes_event_base) {
446 union {
447 XEvent ev;
448 XFixesSelectionNotifyEvent xfev;
449 } ev;
451 if (vdagent_x11_get_clipboard_selection(x11, &event, &selection)) {
452 return;
455 ev.ev = event;
456 switch (ev.xfev.subtype) {
457 case XFixesSetSelectionOwnerNotify:
458 break;
459 /* Treat ... as a SelectionOwnerNotify None */
460 case XFixesSelectionWindowDestroyNotify:
461 case XFixesSelectionClientCloseNotify:
462 ev.xfev.owner = None;
463 break;
464 default:
465 VSELPRINTF("unexpected xfix event subtype %d window %d\n",
466 (int)ev.xfev.subtype, (int)event.xany.window);
467 return;
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)
473 return;
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)
479 return;
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,
484 CurrentTime);
485 x11->expected_targets_notifies[selection]++;
486 return;
489 switch (event.type) {
490 case ConfigureNotify:
491 if (event.xconfigure.window != x11->root_window)
492 break;
494 handled = 1;
496 if (event.xconfigure.width == x11->width &&
497 event.xconfigure.height == x11->height)
498 break;
500 x11->width = event.xconfigure.width;
501 x11->height = event.xconfigure.height;
503 vdagent_x11_send_daemon_guest_xorg_res(x11);
504 break;
505 case MappingNotify:
506 /* These are uninteresting */
507 handled = 1;
508 break;
509 case SelectionNotify:
510 if (event.xselection.target == x11->targets_atom)
511 vdagent_x11_handle_targets_notify(x11, &event);
512 else
513 vdagent_x11_handle_selection_notify(x11, &event, 0);
515 handled = 1;
516 break;
517 case PropertyNotify:
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. */
528 handled = 1;
529 break;
530 case SelectionClear:
531 /* Do nothing the clipboard ownership will get updated through
532 the XFixesSetSelectionOwnerNotify event */
533 handled = 1;
534 break;
535 case SelectionRequest: {
536 struct vdagent_x11_selection_request *req, *new_req;
538 if (vdagent_x11_get_clipboard_selection(x11, &event, &selection)) {
539 return;
542 new_req = malloc(sizeof(*new_req));
543 if (!new_req) {
544 SELPRINTF("out of memory on SelectionRequest, ignoring.\n");
545 break;
548 handled = 1;
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);
557 break;
560 /* maybe we should limit the selection_request stack depth ? */
561 req = x11->selection_req;
562 while (req->next)
563 req = req->next;
565 req->next = new_req;
566 break;
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)
576 XEvent event;
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)
594 screen_count = 1;
596 res = malloc(screen_count * sizeof(*res));
597 if (!res) {
598 fprintf(x11->errfile, "out of memory while trying to send resolutions, not sending resolutions.\n");
599 if (screen_info)
600 XFree(screen_info);
601 return;
604 if (screen_info) {
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);
609 XFree(screen_info);
610 free(res);
611 return;
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;
618 XFree(screen_info);
619 } else {
620 res[0].width = x11->width;
621 res[0].height = x11->height;
622 res[0].x = 0;
623 res[0].y = 0;
626 udscs_write(x11->vdagentd, VDAGENTD_GUEST_XORG_RESOLUTION, x11->width,
627 x11->height, (uint8_t *)res, screen_count * sizeof(*res));
628 free(res);
631 static const char *vdagent_x11_get_atom_name(struct vdagent_x11 *x11, Atom a)
633 if (a == None)
634 return "None";
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;
644 Atom type_ret;
645 int format_ret, ret_val = -1;
646 unsigned long len, remain;
647 unsigned char *data = NULL;
649 *data_ret = NULL;
651 if (!incr) {
652 if (event->xselection.property == None) {
653 VSELPRINTF("XConvertSelection refused by clipboard owner\n");
654 goto exit;
657 if (event->xselection.requestor != x11->selection_window ||
658 event->xselection.property != prop) {
659 SELPRINTF("SelectionNotify parameters mismatch\n");
660 goto exit;
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");
668 goto exit;
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");
678 goto exit;
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;
687 goto exit;
689 x11->clipboard_data_space = prop_min_size;
691 x11->expect_property_notify = 1;
692 XSelectInput(x11->display, x11->selection_window,
693 PropertyChangeMask);
694 XDeleteProperty(x11->display, x11->selection_window, prop);
695 XFree(data);
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));
705 goto exit;
708 if (format_ret != format) {
709 SELPRINTF("expected %d bit format, got %d bits\n", format, format_ret);
710 goto exit;
713 /* Convert len to bytes */
714 switch(format) {
715 case 8:
716 break;
717 case 16:
718 len *= sizeof(short);
719 break;
720 case 32:
721 len *= sizeof(long);
722 break;
725 if (incr) {
726 if (len) {
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);
737 goto exit;
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);
743 XFree(data);
744 return 0; /* Wait for more data */
746 len = x11->clipboard_data_size;
747 *data_ret = x11->clipboard_data;
748 } else
749 *data_ret = data;
751 if (len > 0) {
752 ret_val = len;
753 } else {
754 SELPRINTF("property contains no data (zero length)\n");
755 *data_ret = NULL;
758 exit:
759 if ((incr || ret_val == -1) && data)
760 XFree(data);
762 if (incr) {
763 x11->clipboard_data_size = 0;
764 x11->expect_property_notify = 0;
767 return ret_val;
770 static void vdagent_x11_get_selection_free(struct vdagent_x11 *x11,
771 unsigned char *data, int incr)
773 if (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;
780 } else if (data)
781 XFree(data);
784 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11 *x11,
785 uint8_t selection, Atom target)
787 int i, j;
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)
805 int i;
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);
813 return None;
816 static void vdagent_x11_handle_conversion_request(struct vdagent_x11 *x11)
818 Atom clip;
820 if (!x11->conversion_req) {
821 return;
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)
832 int len = 0;
833 unsigned char *data = NULL;
834 uint32_t type;
835 uint8_t selection = -1;
836 Atom clip;
838 if (!x11->conversion_req) {
839 fprintf(x11->errfile, "SelectionNotify received without a target\n");
840 return;
842 vdagent_x11_get_clipboard_atom(x11, x11->conversion_req->selection, &clip);
844 if (incr) {
845 if (event->xproperty.atom != clip ||
846 event->xproperty.window != x11->selection_window) {
847 return;
849 } else {
850 if (vdagent_x11_get_clipboard_selection(x11, event, &selection)) {
851 len = -1;
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);
855 len = -1;
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));
862 len = -1;
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? */
874 return;
877 if (len == -1) {
878 type = VD_AGENT_CLIPBOARD_NONE;
879 len = 0;
882 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, selection, type,
883 data, len);
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)
892 int i, j;
894 for (i = 0; i < l1; i++)
895 for (j = 0; j < l2; j++)
896 if (atoms1[i] == atoms2[j])
897 return atoms1[i];
899 return 0;
902 static void vdagent_x11_print_targets(struct vdagent_x11 *x11,
903 uint8_t selection, const char *action, Atom *atoms, int c)
905 int i;
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,
912 XEvent *event)
914 int i, len;
915 Atom atom, *atoms = NULL;
916 uint8_t selection;
917 int *type_count;
919 if (vdagent_x11_get_clipboard_selection(x11, event, &selection)) {
920 return;
923 if (!x11->expected_targets_notifies[selection]) {
924 SELPRINTF("unexpected selection notify TARGETS\n");
925 return;
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]) {
934 return;
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? */
941 return;
943 /* bytes -> atoms */
944 len /= sizeof(Atom);
945 vdagent_x11_print_targets(x11, selection, "received", atoms, len);
947 type_count = &x11->clipboard_type_count[selection];
948 *type_count = 0;
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);
952 if (atom) {
953 x11->clipboard_agent_types[selection][*type_count] =
954 x11->clipboard_formats[i].type;
955 x11->clipboard_x11_targets[selection][*type_count] = atom;
956 (*type_count)++;
957 if (*type_count ==
958 sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t)) {
959 SELPRINTF("handle_targets_notify: too many types\n");
960 break;
965 if (*type_count) {
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)
978 XEvent res, *event;
980 if (request) {
981 event = &request->event;
982 } else {
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);
995 if (!request) {
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])
1011 continue;
1013 for (k = 0; k < x11->clipboard_formats[j].atom_count; k++) {
1014 targets[target_count] = x11->clipboard_formats[j].atoms[k];
1015 target_count++;
1016 if (target_count == sizeof(targets)/sizeof(Atom)) {
1017 SELPRINTF("send_targets: too many targets\n");
1018 goto exit_loop;
1023 exit_loop:
1025 prop = event->xselectionrequest.property;
1026 if (prop == None)
1027 prop = event->xselectionrequest.target;
1029 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
1030 XA_ATOM, 32, PropModeReplace, (unsigned char *)&targets,
1031 target_count);
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)
1038 XEvent *event;
1039 uint32_t type = VD_AGENT_CLIPBOARD_NONE;
1040 uint8_t selection;
1042 if (!x11->selection_req)
1043 return;
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);
1053 return;
1056 if (event->xselectionrequest.target == x11->multiple_atom) {
1057 SELPRINTF("multiple target not supported\n");
1058 vdagent_x11_send_selection_notify(x11, None, NULL);
1059 return;
1062 if (event->xselectionrequest.target == x11->targets_atom) {
1063 vdagent_x11_send_targets(x11, selection, event);
1064 return;
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);
1071 return;
1074 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_REQUEST, selection, type,
1075 NULL, 0);
1078 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11,
1079 XEvent *del_event)
1081 XEvent *sel_event;
1082 int len;
1083 uint8_t selection;
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) {
1090 return;
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;
1098 if (len) {
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);
1103 } else {
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,
1110 len);
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. */
1116 if (len == 0) {
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;
1131 int best = -1;
1132 unsigned int closest_diff = -1;
1133 XRRScreenSize* sizes;
1134 XRRScreenConfiguration* config;
1135 Rotation rotation;
1137 if (!x11->has_xrandr)
1138 return;
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");
1148 return;
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;
1162 best = i;
1166 if (best == -1) {
1167 fprintf(x11->errfile, "no suitable resolution found for monitor\n");
1168 return;
1171 config = XRRGetScreenInfo(x11->display, x11->root_window);
1172 if(!config) {
1173 fprintf(x11->errfile, "get screen info failed\n");
1174 return;
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)
1191 Atom target, clip;
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)) {
1197 goto none;
1200 if (x11->clipboard_owner[selection] != owner_guest) {
1201 SELPRINTF("received clipboard req while not owning guest clipboard\n");
1202 goto none;
1205 target = vdagent_x11_type_to_target(x11, selection, type);
1206 if (target == None) {
1207 goto none;
1210 new_req = malloc(sizeof(*new_req));
1211 if (!new_req) {
1212 SELPRINTF("out of memory on client clipboard request, ignoring.\n");
1213 return;
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);
1225 return;
1228 /* maybe we should limit the conversion_request stack depth ? */
1229 req = x11->conversion_req;
1230 while (req->next)
1231 req = req->next;
1233 req->next = new_req;
1234 return;
1236 none:
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)
1244 Atom clip;
1246 if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) {
1247 return;
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)
1270 Atom prop;
1271 XEvent *event;
1272 uint32_t type_from_event;
1274 if (x11->selection_req_data) {
1275 if (type || size) {
1276 SELPRINTF("received clipboard data while still sending"
1277 " data from previous request, ignoring\n");
1279 free(data);
1280 return;
1283 if (!x11->selection_req) {
1284 if (type || size) {
1285 SELPRINTF("received clipboard data without an "
1286 "outstanding selection request, ignoring\n");
1288 free(data);
1289 return;
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);
1307 free(data);
1309 /* Flush output buffers and consume any pending events */
1310 vdagent_x11_do_read(x11);
1311 return;
1314 prop = event->xselectionrequest.property;
1315 if (prop == None)
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);
1331 } else {
1332 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
1333 event->xselectionrequest.target, 8, PropModeReplace,
1334 data, size);
1335 vdagent_x11_send_selection_notify(x11, prop, NULL);
1336 free(data);
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)
1345 XEvent event;
1346 Atom clip;
1348 if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) {
1349 return;
1352 if (x11->clipboard_owner[selection] != owner_client) {
1353 SELPRINTF("received release while not owning client clipboard\n");
1354 return;
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,
1363 &event))
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);