x11: request notifications of PRIMARY clipboard
[vd_agent.git] / vdagent-x11.c
bloba0bdeb6ca175888e4c9a1846303503c62a577daa
1 /* vdagent-x11.c vdagent x11 code
3 Copyright 2010 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 enum { owner_none, owner_guest, owner_client };
47 struct vdagent_x11_selection_request {
48 XEvent event;
49 struct vdagent_x11_selection_request *next;
52 struct clipboard_format_tmpl {
53 uint32_t type;
54 const char *atom_names[16];
57 struct clipboard_format_info {
58 uint32_t type;
59 Atom atoms[16];
60 int atom_count;
63 static const struct clipboard_format_tmpl clipboard_format_templates[] = {
64 { VD_AGENT_CLIPBOARD_UTF8_TEXT, { "UTF8_STRING",
65 "text/plain;charset=UTF-8", "text/plain;charset=utf-8", NULL }, },
66 { VD_AGENT_CLIPBOARD_IMAGE_PNG, { "image/png", NULL }, },
67 { VD_AGENT_CLIPBOARD_IMAGE_BMP, { "image/bmp", "image/x-bmp",
68 "image/x-MS-bmp", "image/x-win-bitmap", NULL }, },
69 { VD_AGENT_CLIPBOARD_IMAGE_TIFF, { "image/tiff", NULL }, },
70 { VD_AGENT_CLIPBOARD_IMAGE_JPG, { "image/jpeg", NULL }, },
73 #define clipboard_format_count (sizeof(clipboard_format_templates)/sizeof(clipboard_format_templates[0]))
75 struct vdagent_x11 {
76 struct clipboard_format_info clipboard_formats[clipboard_format_count];
77 Display *display;
78 Atom clipboard_atom;
79 Atom clipboard_primary_atom;
80 Atom targets_atom;
81 Atom incr_atom;
82 Atom multiple_atom;
83 Window root_window;
84 Window selection_window;
85 struct udscs_connection *vdagentd;
86 FILE *errfile;
87 int verbose;
88 int fd;
89 int screen;
90 int width;
91 int height;
92 int has_xrandr;
93 int has_xfixes;
94 int xfixes_event_base;
95 int max_prop_size;
96 int expected_targets_notifies;
97 int expect_property_notify;
98 int clipboard_owner;
99 Atom clipboard_request_target;
100 int clipboard_type_count;
101 uint32_t clipboard_agent_types[256];
102 Atom clipboard_x11_targets[256];
103 uint8_t *clipboard_data;
104 uint32_t clipboard_data_size;
105 uint32_t clipboard_data_space;
106 struct vdagent_x11_selection_request *selection_request;
107 uint8_t *selection_req_data;
108 uint32_t selection_req_data_pos;
109 uint32_t selection_req_data_size;
110 Atom selection_req_atom;
113 static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11);
114 static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
115 XEvent *event, int incr);
116 static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11);
117 static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
118 XEvent *event, int incr);
119 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11,
120 XEvent *del_event);
121 static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
122 Atom prop,
123 int process_next_req);
124 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11,
125 int new_owner);
127 struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd,
128 FILE *errfile, int verbose)
130 struct vdagent_x11 *x11;
131 XWindowAttributes attrib;
132 int i, j, major, minor;
134 x11 = calloc(1, sizeof(*x11));
135 if (!x11) {
136 fprintf(errfile, "out of memory allocating vdagent_x11 struct\n");
137 return NULL;
140 x11->vdagentd = vdagentd;
141 x11->errfile = errfile;
142 x11->verbose = verbose;
144 x11->display = XOpenDisplay(NULL);
145 if (!x11->display) {
146 fprintf(x11->errfile, "could not connect to X-server\n");
147 free(x11);
148 return NULL;
151 x11->screen = DefaultScreen(x11->display);
152 x11->root_window = RootWindow(x11->display, x11->screen);
153 x11->fd = ConnectionNumber(x11->display);
154 x11->clipboard_atom = XInternAtom(x11->display, "CLIPBOARD", False);
155 x11->clipboard_primary_atom = XInternAtom(x11->display, "PRIMARY", False);
156 x11->targets_atom = XInternAtom(x11->display, "TARGETS", False);
157 x11->incr_atom = XInternAtom(x11->display, "INCR", False);
158 x11->multiple_atom = XInternAtom(x11->display, "MULTIPLE", False);
159 for(i = 0; i < clipboard_format_count; i++) {
160 x11->clipboard_formats[i].type = clipboard_format_templates[i].type;
161 for(j = 0; clipboard_format_templates[i].atom_names[j]; j++) {
162 x11->clipboard_formats[i].atoms[j] =
163 XInternAtom(x11->display,
164 clipboard_format_templates[i].atom_names[j],
165 False);
167 x11->clipboard_formats[i].atom_count = j;
170 /* We should not store properties (for selections) on the root window */
171 x11->selection_window = XCreateSimpleWindow(x11->display, x11->root_window,
172 0, 0, 1, 1, 0, 0, 0);
173 if (x11->verbose)
174 fprintf(x11->errfile, "Selection window: %u\n",
175 (unsigned int)x11->selection_window);
177 if (XRRQueryExtension(x11->display, &i, &i))
178 x11->has_xrandr = 1;
179 else
180 fprintf(x11->errfile, "no xrandr\n");
182 if (XFixesQueryExtension(x11->display, &x11->xfixes_event_base, &i) &&
183 XFixesQueryVersion(x11->display, &major, &minor) && major >= 1) {
184 x11->has_xfixes = 1;
185 XFixesSelectSelectionInput(x11->display, x11->root_window,
186 x11->clipboard_atom,
187 XFixesSetSelectionOwnerNotifyMask|
188 XFixesSelectionWindowDestroyNotifyMask|
189 XFixesSelectionClientCloseNotifyMask);
190 XFixesSelectSelectionInput(x11->display, x11->root_window,
191 x11->clipboard_primary_atom,
192 XFixesSetSelectionOwnerNotifyMask|
193 XFixesSelectionWindowDestroyNotifyMask|
194 XFixesSelectionClientCloseNotifyMask);
195 } else
196 fprintf(x11->errfile,
197 "no xfixes, no guest -> client copy paste support\n");
199 x11->max_prop_size = XExtendedMaxRequestSize(x11->display);
200 if (x11->max_prop_size) {
201 x11->max_prop_size -= 100;
202 } else {
203 x11->max_prop_size = XMaxRequestSize(x11->display) - 100;
205 /* Be a good X11 citizen and maximize the amount of data we send at once */
206 if (x11->max_prop_size > 262144)
207 x11->max_prop_size = 262144;
209 /* Catch resolution changes */
210 XSelectInput(x11->display, x11->root_window, StructureNotifyMask);
212 /* Get the current resolution */
213 XGetWindowAttributes(x11->display, x11->root_window, &attrib);
214 x11->width = attrib.width;
215 x11->height = attrib.height;
216 vdagent_x11_send_daemon_guest_xorg_res(x11);
218 /* Flush output buffers and consume any pending events */
219 vdagent_x11_do_read(x11);
221 return x11;
224 void vdagent_x11_destroy(struct vdagent_x11 *x11)
226 if (!x11)
227 return;
229 vdagent_x11_set_clipboard_owner(x11, owner_none);
230 XCloseDisplay(x11->display);
231 free(x11);
234 int vdagent_x11_get_fd(struct vdagent_x11 *x11)
236 return x11->fd;
239 static void vdagent_x11_next_selection_request(struct vdagent_x11 *x11)
241 struct vdagent_x11_selection_request *selection_request;
243 free(x11->selection_req_data);
244 x11->selection_req_data = NULL;
245 x11->selection_req_data_pos = 0;
246 x11->selection_req_data_size = 0;
247 x11->selection_req_atom = None;
249 selection_request = x11->selection_request;
250 x11->selection_request = selection_request->next;
251 free(selection_request);
254 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11,
255 int new_owner)
257 /* Clear pending requests and clipboard data */
258 if (x11->selection_request) {
259 fprintf(x11->errfile,
260 "selection requests pending on clipboard ownership change, "
261 "clearing");
262 while (x11->selection_request) {
263 vdagent_x11_send_selection_notify(x11, None, 0);
264 vdagent_x11_next_selection_request(x11);
267 if (x11->clipboard_request_target != None) {
268 fprintf(x11->errfile,
269 "client clipboard request pending on clipboard ownership "
270 "change, clearing");
271 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
272 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
273 x11->clipboard_request_target = None;
275 x11->clipboard_data_size = 0;
276 x11->expect_property_notify = 0;
278 if (new_owner == owner_none) {
279 /* When going from owner_guest to owner_none we need to send a
280 clipboard release message to the client */
281 if (x11->clipboard_owner == owner_guest)
282 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_RELEASE, 0, NULL, 0);
284 x11->clipboard_type_count = 0;
286 x11->clipboard_owner = new_owner;
289 static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
291 int handled = 0;
294 if (event.type == x11->xfixes_event_base) {
295 union {
296 XEvent ev;
297 XFixesSelectionNotifyEvent xfev;
298 } ev;
300 ev.ev = event;
301 switch (ev.xfev.subtype) {
302 case XFixesSetSelectionOwnerNotify:
303 break;
304 /* Treat ... as a SelectionOwnerNotify None */
305 case XFixesSelectionWindowDestroyNotify:
306 case XFixesSelectionClientCloseNotify:
307 ev.xfev.owner = None;
308 break;
309 default:
310 if (x11->verbose)
311 fprintf(x11->errfile,
312 "unexpected xfix event subtype %d window %d\n",
313 (int)ev.xfev.subtype, (int)event.xany.window);
314 return;
317 if (x11->verbose)
318 fprintf(x11->errfile, "New selection owner: %u\n",
319 (unsigned int)ev.xfev.owner);
321 /* Ignore becoming the owner ourselves */
322 if (ev.xfev.owner == x11->selection_window)
323 return;
325 /* If the clipboard owner is changed we no longer own it */
326 vdagent_x11_set_clipboard_owner(x11, owner_none);
328 if (ev.xfev.owner == None)
329 return;
331 /* Request the supported targets from the new owner */
332 XConvertSelection(x11->display, x11->clipboard_atom, x11->targets_atom,
333 x11->targets_atom, x11->selection_window,
334 CurrentTime);
335 x11->expected_targets_notifies++;
336 return;
339 switch (event.type) {
340 case ConfigureNotify:
341 if (event.xconfigure.window != x11->root_window)
342 break;
344 handled = 1;
346 if (event.xconfigure.width == x11->width &&
347 event.xconfigure.height == x11->height)
348 break;
350 x11->width = event.xconfigure.width;
351 x11->height = event.xconfigure.height;
353 vdagent_x11_send_daemon_guest_xorg_res(x11);
354 break;
355 case SelectionNotify:
356 if (event.xselection.target == x11->targets_atom)
357 vdagent_x11_handle_targets_notify(x11, &event, 0);
358 else
359 vdagent_x11_handle_selection_notify(x11, &event, 0);
361 handled = 1;
362 break;
363 case PropertyNotify:
364 if (x11->expect_property_notify &&
365 event.xproperty.state == PropertyNewValue) {
366 if (event.xproperty.atom == x11->targets_atom) {
367 vdagent_x11_handle_targets_notify(x11, &event, 1);
368 } else {
369 vdagent_x11_handle_selection_notify(x11, &event, 1);
372 if (x11->selection_req_data &&
373 event.xproperty.state == PropertyDelete) {
374 vdagent_x11_handle_property_delete_notify(x11, &event);
376 /* Always mark as handled, since we cannot unselect input for property
377 notifications once we are done with handling the incr transfer. */
378 handled = 1;
379 break;
380 case SelectionClear:
381 /* Do nothing the clipboard ownership will get updated through
382 the XFixesSetSelectionOwnerNotify event */
383 handled = 1;
384 break;
385 case SelectionRequest: {
386 struct vdagent_x11_selection_request *req, *new_req;
388 new_req = malloc(sizeof(*new_req));
389 if (!new_req) {
390 fprintf(x11->errfile,
391 "out of memory on SelectionRequest, ignoring.\n");
392 break;
395 handled = 1;
397 new_req->event = event;
398 new_req->next = NULL;
400 if (!x11->selection_request) {
401 x11->selection_request = new_req;
402 vdagent_x11_handle_selection_request(x11);
403 break;
406 /* maybe we should limit the selection_request stack depth ? */
407 req = x11->selection_request;
408 while (req->next)
409 req = req->next;
411 req->next = new_req;
412 break;
415 if (!handled && x11->verbose)
416 fprintf(x11->errfile, "unhandled x11 event, type %d, window %d\n",
417 (int)event.type, (int)event.xany.window);
420 void vdagent_x11_do_read(struct vdagent_x11 *x11)
422 XEvent event;
424 while (XPending(x11->display)) {
425 XNextEvent(x11->display, &event);
426 vdagent_x11_handle_event(x11, event);
430 static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11)
432 struct vdagentd_guest_xorg_resolution res;
434 res.width = x11->width;
435 res.height = x11->height;
437 udscs_write(x11->vdagentd, VDAGENTD_GUEST_XORG_RESOLUTION, 0,
438 (uint8_t *)&res, sizeof(res));
441 static const char *vdagent_x11_get_atom_name(struct vdagent_x11 *x11, Atom a)
443 if (a == None)
444 return "None";
446 return XGetAtomName(x11->display, a);
449 static int vdagent_x11_get_selection(struct vdagent_x11 *x11, XEvent *event,
450 Atom type, Atom prop, int format,
451 unsigned char **data_ret, int incr)
453 Bool del = incr ? True: False;
454 Atom type_ret;
455 int format_ret, ret_val = -1;
456 unsigned long len, remain;
457 unsigned char *data = NULL;
459 *data_ret = NULL;
461 if (incr) {
462 if (event->xproperty.atom != prop) {
463 fprintf(x11->errfile, "PropertyNotify parameters mismatch\n");
464 goto exit;
466 } else {
467 if (event->xselection.property == None) {
468 if (x11->verbose)
469 fprintf(x11->errfile,
470 "XConvertSelection refused by clipboard owner\n");
471 goto exit;
474 if (event->xselection.requestor != x11->selection_window ||
475 event->xselection.selection != x11->clipboard_atom ||
476 event->xselection.property != prop) {
477 fprintf(x11->errfile, "SelectionNotify parameters mismatch\n");
478 goto exit;
482 if (XGetWindowProperty(x11->display, x11->selection_window, prop, 0,
483 LONG_MAX, del, type, &type_ret, &format_ret, &len,
484 &remain, &data) != Success) {
485 fprintf(x11->errfile, "XGetWindowProperty failed\n");
486 goto exit;
489 if (!incr) {
490 if (type_ret == x11->incr_atom) {
491 int prop_min_size = *(uint32_t*)data;
493 if (x11->expect_property_notify) {
494 fprintf(x11->errfile,
495 "received an incr SelectionNotify while "
496 "still reading another incr property\n");
497 goto exit;
500 if (x11->clipboard_data_space < prop_min_size) {
501 free(x11->clipboard_data);
502 x11->clipboard_data = malloc(prop_min_size);
503 if (!x11->clipboard_data) {
504 fprintf(x11->errfile,
505 "out of memory allocating clipboard buffer\n");
506 x11->clipboard_data_space = 0;
507 goto exit;
509 x11->clipboard_data_space = prop_min_size;
511 x11->expect_property_notify = 1;
512 XSelectInput(x11->display, x11->selection_window,
513 PropertyChangeMask);
514 XDeleteProperty(x11->display, x11->selection_window, prop);
515 XFree(data);
516 return 0; /* Wait for more data */
518 XDeleteProperty(x11->display, x11->selection_window, prop);
521 if (type_ret != type) {
522 fprintf(x11->errfile, "expected property type: %s, got: %s\n",
523 vdagent_x11_get_atom_name(x11, type),
524 vdagent_x11_get_atom_name(x11, type_ret));
525 goto exit;
528 if (format_ret != format) {
529 fprintf(x11->errfile, "expected %d bit format, got %d bits\n", format,
530 format_ret);
531 goto exit;
534 /* Convert len to bytes */
535 switch(format) {
536 case 8:
537 break;
538 case 16:
539 len *= sizeof(short);
540 break;
541 case 32:
542 len *= sizeof(long);
543 break;
546 if (incr) {
547 if (len) {
548 if (x11->clipboard_data_size + len > x11->clipboard_data_space) {
549 void *old_clipboard_data = x11->clipboard_data;
551 x11->clipboard_data_space = x11->clipboard_data_size + len;
552 x11->clipboard_data = realloc(x11->clipboard_data,
553 x11->clipboard_data_space);
554 if (!x11->clipboard_data) {
555 fprintf(x11->errfile,
556 "out of memory allocating clipboard buffer\n");
557 x11->clipboard_data_space = 0;
558 free(old_clipboard_data);
559 goto exit;
562 memcpy(x11->clipboard_data + x11->clipboard_data_size, data, len);
563 x11->clipboard_data_size += len;
564 if (x11->verbose)
565 fprintf(x11->errfile, "Appended %ld bytes to buffer\n", len);
566 XFree(data);
567 return 0; /* Wait for more data */
569 len = x11->clipboard_data_size;
570 *data_ret = x11->clipboard_data;
571 } else
572 *data_ret = data;
574 if (len > 0) {
575 ret_val = len;
576 } else {
577 fprintf(x11->errfile, "property contains no data (zero length)\n");
578 *data_ret = NULL;
581 exit:
582 if ((incr || ret_val == -1) && data)
583 XFree(data);
585 if (incr) {
586 x11->clipboard_data_size = 0;
587 x11->expect_property_notify = 0;
590 return ret_val;
593 static void vdagent_x11_get_selection_free(struct vdagent_x11 *x11,
594 unsigned char *data, int incr)
596 if (incr) {
597 /* If the clipboard has grown large return the memory to the system */
598 if (x11->clipboard_data_space > 512 * 1024) {
599 free(x11->clipboard_data);
600 x11->clipboard_data = NULL;
601 x11->clipboard_data_space = 0;
603 } else if (data)
604 XFree(data);
607 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11 *x11,
608 Atom target)
610 int i, j;
612 for (i = 0; i < clipboard_format_count; i++) {
613 for (j = 0; j < x11->clipboard_formats[i].atom_count; i++) {
614 if (x11->clipboard_formats[i].atoms[j] == target) {
615 return x11->clipboard_formats[i].type;
620 fprintf(x11->errfile, "unexpected selection type %s\n",
621 vdagent_x11_get_atom_name(x11, target));
622 return VD_AGENT_CLIPBOARD_NONE;
625 static Atom vdagent_x11_type_to_target(struct vdagent_x11 *x11, uint32_t type)
627 int i;
629 for (i = 0; i < x11->clipboard_type_count; i++)
630 if (x11->clipboard_agent_types[i] == type)
631 return x11->clipboard_x11_targets[i];
633 fprintf(x11->errfile, "client requested unavailable type %u\n", type);
634 return None;
637 static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
638 XEvent *event, int incr)
640 int len = -1;
641 unsigned char *data = NULL;
642 uint32_t type;
644 if (x11->clipboard_request_target == None) {
645 fprintf(x11->errfile, "SelectionNotify received without a target\n");
646 return;
649 type = vdagent_x11_target_to_type(x11, x11->clipboard_request_target);
650 if (!incr &&
651 event->xselection.target != x11->clipboard_request_target &&
652 event->xselection.target != x11->incr_atom)
653 fprintf(x11->errfile, "Requested %s target got %s\n",
654 vdagent_x11_get_atom_name(x11, x11->clipboard_request_target),
655 vdagent_x11_get_atom_name(x11, event->xselection.target));
656 else
657 len = vdagent_x11_get_selection(x11, event, x11->clipboard_request_target,
658 x11->clipboard_atom, 8, &data, incr);
659 if (len == 0) /* waiting for more data? */
660 return;
661 if (len == -1) {
662 type = VD_AGENT_CLIPBOARD_NONE;
663 len = 0;
666 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, type, data, len);
667 x11->clipboard_request_target = None;
668 vdagent_x11_get_selection_free(x11, data, incr);
671 static Atom atom_lists_overlap(Atom *atoms1, Atom *atoms2, int l1, int l2)
673 int i, j;
675 for (i = 0; i < l1; i++)
676 for (j = 0; j < l2; j++)
677 if (atoms1[i] == atoms2[j])
678 return atoms1[i];
680 return 0;
683 static void vdagent_x11_print_targets(struct vdagent_x11 *x11,
684 const char *action, Atom *atoms, int c)
686 int i;
688 if (!x11->verbose)
689 return;
691 fprintf(x11->errfile, "%s %d targets:\n", action, c);
692 for (i = 0; i < c; i++)
693 fprintf(x11->errfile, "%s\n",
694 vdagent_x11_get_atom_name(x11, atoms[i]));
697 static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
698 XEvent *event, int incr)
700 int i, len;
701 Atom atom, *atoms = NULL;
703 if (!x11->expected_targets_notifies) {
704 fprintf(x11->errfile, "unexpected selection notify TARGETS\n");
705 return;
708 x11->expected_targets_notifies--;
710 /* If we have more targets_notifies pending, ignore this one, we
711 are only interested in the targets list of the current owner
712 (which is the last one we've requested a targets list from) */
713 if (x11->expected_targets_notifies)
714 return;
716 len = vdagent_x11_get_selection(x11, event, XA_ATOM, x11->targets_atom, 32,
717 (unsigned char **)&atoms, incr);
718 if (len == 0 || len == -1) /* waiting for more data or error? */
719 return;
721 /* bytes -> atoms */
722 len /= sizeof(Atom);
723 vdagent_x11_print_targets(x11, "received", atoms, len);
725 x11->clipboard_type_count = 0;
726 for (i = 0; i < clipboard_format_count; i++) {
727 atom = atom_lists_overlap(x11->clipboard_formats[i].atoms, atoms,
728 x11->clipboard_formats[i].atom_count, len);
729 if (atom) {
730 x11->clipboard_agent_types[x11->clipboard_type_count] =
731 x11->clipboard_formats[i].type;
732 x11->clipboard_x11_targets[x11->clipboard_type_count] = atom;
733 x11->clipboard_type_count++;
734 if (x11->clipboard_type_count ==
735 sizeof(x11->clipboard_agent_types)/sizeof(uint32_t)) {
736 fprintf(x11->errfile,
737 "handle_targets_notify: too many types\n");
738 break;
743 if (x11->clipboard_type_count) {
744 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_GRAB, 0,
745 (uint8_t *)x11->clipboard_agent_types,
746 x11->clipboard_type_count * sizeof(uint32_t));
747 vdagent_x11_set_clipboard_owner(x11, owner_guest);
750 vdagent_x11_get_selection_free(x11, (unsigned char *)atoms, incr);
753 static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
754 Atom prop, int process_next_req)
756 XEvent res, *event = &x11->selection_request->event;
758 res.xselection.property = prop;
759 res.xselection.type = SelectionNotify;
760 res.xselection.display = event->xselectionrequest.display;
761 res.xselection.requestor = event->xselectionrequest.requestor;
762 res.xselection.selection = event->xselectionrequest.selection;
763 res.xselection.target = event->xselectionrequest.target;
764 res.xselection.time = event->xselectionrequest.time;
765 XSendEvent(x11->display, event->xselectionrequest.requestor, 0, 0, &res);
767 if (process_next_req) {
768 vdagent_x11_next_selection_request(x11);
769 vdagent_x11_handle_selection_request(x11);
773 static void vdagent_x11_send_targets(struct vdagent_x11 *x11, XEvent *event)
775 Atom prop, targets[256] = { x11->targets_atom, };
776 int i, j, k, target_count = 1;
778 for (i = 0; i < x11->clipboard_type_count; i++) {
779 for (j = 0; j < clipboard_format_count; j++) {
780 if (x11->clipboard_formats[j].type !=
781 x11->clipboard_agent_types[i])
782 continue;
784 for (k = 0; k < x11->clipboard_formats[j].atom_count; k++) {
785 targets[target_count] = x11->clipboard_formats[j].atoms[k];
786 target_count++;
787 if (target_count == sizeof(targets)/sizeof(Atom)) {
788 fprintf(x11->errfile, "send_targets: too many targets\n");
789 goto exit_loop;
794 exit_loop:
796 prop = event->xselectionrequest.property;
797 if (prop == None)
798 prop = event->xselectionrequest.target;
800 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
801 XA_ATOM, 32, PropModeReplace, (unsigned char *)&targets,
802 target_count);
803 vdagent_x11_print_targets(x11, "sent", targets, target_count);
804 vdagent_x11_send_selection_notify(x11, prop, 1);
807 static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11)
809 XEvent *event;
810 uint32_t type = VD_AGENT_CLIPBOARD_NONE;
812 if (!x11->selection_request)
813 return;
815 event = &x11->selection_request->event;
817 if (x11->clipboard_owner != owner_client) {
818 fprintf(x11->errfile,
819 "received selection request event for target %s, "
820 "while not owning client clipboard\n",
821 vdagent_x11_get_atom_name(x11, event->xselectionrequest.target));
822 vdagent_x11_send_selection_notify(x11, None, 1);
823 return;
826 if (event->xselectionrequest.target == x11->multiple_atom) {
827 fprintf(x11->errfile, "multiple target not supported\n");
828 vdagent_x11_send_selection_notify(x11, None, 1);
829 return;
832 if (event->xselectionrequest.target == x11->targets_atom) {
833 vdagent_x11_send_targets(x11, event);
834 return;
837 type = vdagent_x11_target_to_type(x11, event->xselectionrequest.target);
838 if (type == VD_AGENT_CLIPBOARD_NONE) {
839 vdagent_x11_send_selection_notify(x11, None, 1);
840 return;
843 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_REQUEST, type, NULL, 0);
846 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11,
847 XEvent *del_event)
849 XEvent *sel_event;
850 int len;
852 assert(x11->selection_request);
853 sel_event = &x11->selection_request->event;
854 if (del_event->xproperty.window != sel_event->xselectionrequest.requestor
855 || del_event->xproperty.atom != x11->selection_req_atom) {
856 return;
859 len = x11->selection_req_data_size - x11->selection_req_data_pos;
860 if (len > x11->max_prop_size) {
861 len = x11->max_prop_size;
864 if (x11->verbose) {
865 if (len) {
866 fprintf(x11->errfile, "Sending %d-%d/%d bytes of clipboard data\n",
867 x11->selection_req_data_pos,
868 x11->selection_req_data_pos + len - 1,
869 x11->selection_req_data_size);
870 } else {
871 fprintf(x11->errfile, "Ending incr send of clipboard data\n");
874 XChangeProperty(x11->display, sel_event->xselectionrequest.requestor,
875 x11->selection_req_atom,
876 sel_event->xselectionrequest.target, 8, PropModeReplace,
877 x11->selection_req_data + x11->selection_req_data_pos,
878 len);
879 x11->selection_req_data_pos += len;
881 /* Note we must explictly send a 0 sized XChangeProperty to signal the
882 incr transfer is done. Hence we do not check if we've send all data
883 but instead check we've send the final 0 sized XChangeProperty. */
884 if (len == 0) {
885 vdagent_x11_next_selection_request(x11);
886 vdagent_x11_handle_selection_request(x11);
890 void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
891 VDAgentMonitorsConfig *mon_config)
893 int i, num_sizes = 0;
894 int best = -1;
895 unsigned int closest_diff = -1;
896 XRRScreenSize* sizes;
897 XRRScreenConfiguration* config;
898 Rotation rotation;
900 if (!x11->has_xrandr)
901 return;
903 if (mon_config->num_of_monitors != 1) {
904 fprintf(x11->errfile,
905 "Only 1 monitor supported, ignoring additional monitors\n");
908 sizes = XRRSizes(x11->display, x11->screen, &num_sizes);
909 if (!sizes || !num_sizes) {
910 fprintf(x11->errfile, "XRRSizes failed\n");
911 return;
914 /* Find the closest size which will fit within the monitor */
915 for (i = 0; i < num_sizes; i++) {
916 if (sizes[i].width > mon_config->monitors[0].width ||
917 sizes[i].height > mon_config->monitors[0].height)
918 continue; /* Too large for the monitor */
920 unsigned int wdiff = mon_config->monitors[0].width - sizes[i].width;
921 unsigned int hdiff = mon_config->monitors[0].height - sizes[i].height;
922 unsigned int diff = wdiff * wdiff + hdiff * hdiff;
923 if (diff < closest_diff) {
924 closest_diff = diff;
925 best = i;
929 if (best == -1) {
930 fprintf(x11->errfile, "no suitable resolution found for monitor\n");
931 return;
934 config = XRRGetScreenInfo(x11->display, x11->root_window);
935 if(!config) {
936 fprintf(x11->errfile, "get screen info failed\n");
937 return;
939 XRRConfigCurrentConfiguration(config, &rotation);
940 XRRSetScreenConfig(x11->display, config, x11->root_window, best,
941 rotation, CurrentTime);
942 XRRFreeScreenConfigInfo(config);
943 x11->width = sizes[best].width;
944 x11->height = sizes[best].height;
945 vdagent_x11_send_daemon_guest_xorg_res(x11);
947 /* Flush output buffers and consume any pending events */
948 vdagent_x11_do_read(x11);
951 void vdagent_x11_clipboard_request(struct vdagent_x11 *x11, uint32_t type)
953 Atom target;
955 if (x11->clipboard_owner != owner_guest) {
956 fprintf(x11->errfile,
957 "received clipboard req while not owning guest clipboard\n");
958 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
959 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
960 return;
963 target = vdagent_x11_type_to_target(x11, type);
964 if (target == None) {
965 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
966 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
967 return;
970 if (x11->clipboard_request_target) {
971 fprintf(x11->errfile,
972 "XConvertSelection request is already pending\n");
973 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
974 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
975 return;
977 x11->clipboard_request_target = target;
978 XConvertSelection(x11->display, x11->clipboard_atom, target,
979 x11->clipboard_atom, x11->selection_window, CurrentTime);
981 /* Flush output buffers and consume any pending events */
982 vdagent_x11_do_read(x11);
985 void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint32_t *types,
986 uint32_t type_count)
988 if (type_count > sizeof(x11->clipboard_agent_types)/sizeof(uint32_t)) {
989 fprintf(x11->errfile, "x11_clipboard_grab: too many types\n");
990 type_count = sizeof(x11->clipboard_agent_types)/sizeof(uint32_t);
993 memcpy(x11->clipboard_agent_types, types, type_count * sizeof(uint32_t));
994 x11->clipboard_type_count = type_count;
996 XSetSelectionOwner(x11->display, x11->clipboard_atom,
997 x11->selection_window, CurrentTime);
998 vdagent_x11_set_clipboard_owner(x11, owner_client);
1000 /* Flush output buffers and consume any pending events */
1001 vdagent_x11_do_read(x11);
1004 void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint32_t type,
1005 uint8_t *data, uint32_t size)
1007 Atom prop;
1008 XEvent *event;
1009 uint32_t type_from_event;
1011 if (x11->selection_req_data) {
1012 if (type || size) {
1013 fprintf(x11->errfile, "received clipboard data while still sending"
1014 " data from previous request, ignoring\n");
1016 free(data);
1017 return;
1020 if (!x11->selection_request) {
1021 if (type || size) {
1022 fprintf(x11->errfile, "received clipboard data without an "
1023 "outstanding selection request, ignoring\n");
1025 free(data);
1026 return;
1029 event = &x11->selection_request->event;
1030 type_from_event = vdagent_x11_target_to_type(x11,
1031 event->xselectionrequest.target);
1032 if (type_from_event != type) {
1033 fprintf(x11->errfile, "expecting type %u clipboard data got %u\n",
1034 type_from_event, type);
1035 vdagent_x11_send_selection_notify(x11, None, 1);
1036 free(data);
1038 /* Flush output buffers and consume any pending events */
1039 vdagent_x11_do_read(x11);
1040 return;
1043 prop = event->xselectionrequest.property;
1044 if (prop == None)
1045 prop = event->xselectionrequest.target;
1047 if (size > x11->max_prop_size) {
1048 unsigned long len = size;
1049 if (x11->verbose)
1050 fprintf(x11->errfile, "Starting incr send of clipboard data\n");
1051 x11->selection_req_data = data;
1052 x11->selection_req_data_pos = 0;
1053 x11->selection_req_data_size = size;
1054 x11->selection_req_atom = prop;
1055 XSelectInput(x11->display, event->xselectionrequest.requestor,
1056 PropertyChangeMask);
1057 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
1058 x11->incr_atom, 32, PropModeReplace,
1059 (unsigned char*)&len, 1);
1060 vdagent_x11_send_selection_notify(x11, prop, 0);
1061 } else {
1062 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
1063 event->xselectionrequest.target, 8, PropModeReplace,
1064 data, size);
1065 vdagent_x11_send_selection_notify(x11, prop, 1);
1066 free(data);
1069 /* Flush output buffers and consume any pending events */
1070 vdagent_x11_do_read(x11);
1073 void vdagent_x11_clipboard_release(struct vdagent_x11 *x11)
1075 XEvent event;
1077 if (x11->clipboard_owner != owner_client) {
1078 fprintf(x11->errfile,
1079 "received clipboard release while not owning client clipboard\n");
1080 return;
1083 XSetSelectionOwner(x11->display, x11->clipboard_atom, None, CurrentTime);
1084 /* Make sure we process the XFixesSetSelectionOwnerNotify event caused
1085 by this, so we don't end up changing the clipboard owner to none, after
1086 it has already been re-owned because this event is still pending. */
1087 XSync(x11->display, False);
1088 while (XCheckTypedEvent(x11->display, x11->xfixes_event_base,
1089 &event))
1090 vdagent_x11_handle_event(x11, event);
1092 /* Note no need to do a set_clipboard_owner(owner_none) here, as that is
1093 already done by processing the XFixesSetSelectionOwnerNotify event. */
1095 /* Flush output buffers and consume any pending events */
1096 vdagent_x11_do_read(x11);