vdagent-x11: Release clipboard on exit
[vd_agent.git] / vdagent-x11.c
blobebc2ad4b64d48b9a39b2bc0431e0a4892c3e5596
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 *all* X11 calls in this file which do not wait for a result must be
23 followed by an XFlush, given that the X11 code pumping the event loop
24 (and thus flushing queued writes) is only called when there is data to be
25 read from the X11 socket. */
27 #include <stdlib.h>
28 #include <limits.h>
29 #include <string.h>
30 #include <X11/Xatom.h>
31 #include <X11/Xlib.h>
32 #include <X11/extensions/Xrandr.h>
33 #include <X11/extensions/Xfixes.h>
34 #include "vdagentd-proto.h"
35 #include "vdagent-x11.h"
37 enum { owner_none, owner_guest, owner_client };
39 struct vdagent_x11_selection_request {
40 XEvent event;
41 struct vdagent_x11_selection_request *next;
44 struct clipboard_format_tmpl {
45 uint32_t type;
46 const char *atom_names[16];
49 struct clipboard_format_info {
50 uint32_t type;
51 Atom atoms[16];
52 int atom_count;
55 static const struct clipboard_format_tmpl clipboard_format_templates[] = {
56 { VD_AGENT_CLIPBOARD_UTF8_TEXT, { "UTF8_STRING",
57 "text/plain;charset=UTF-8", "text/plain;charset=utf-8", NULL }, },
58 { VD_AGENT_CLIPBOARD_IMAGE_PNG, { "image/png", NULL }, },
59 { VD_AGENT_CLIPBOARD_IMAGE_BMP, { "image/bmp", "image/x-bmp",
60 "image/x-MS-bmp", "image/x-win-bitmap", NULL }, },
61 { VD_AGENT_CLIPBOARD_IMAGE_TIFF, { "image/tiff", NULL }, },
62 { VD_AGENT_CLIPBOARD_IMAGE_JPG, { "image/jpeg", NULL }, },
65 #define clipboard_format_count (sizeof(clipboard_format_templates)/sizeof(clipboard_format_templates[0]))
67 struct vdagent_x11 {
68 struct clipboard_format_info clipboard_formats[clipboard_format_count];
69 Display *display;
70 Atom clipboard_atom;
71 Atom targets_atom;
72 Atom incr_atom;
73 Atom multiple_atom;
74 Window root_window;
75 Window selection_window;
76 struct udscs_connection *vdagentd;
77 FILE *errfile;
78 int verbose;
79 int fd;
80 int screen;
81 int width;
82 int height;
83 int has_xrandr;
84 int has_xfixes;
85 int xfixes_event_base;
86 int expected_targets_notifies;
87 int expect_property_notify;
88 int clipboard_owner;
89 Atom clipboard_request_target;
90 int clipboard_type_count;
91 uint32_t clipboard_agent_types[256];
92 Atom clipboard_x11_targets[256];
93 uint8_t *clipboard_data;
94 uint32_t clipboard_data_size;
95 uint32_t clipboard_data_space;
96 struct vdagent_x11_selection_request *selection_request;
99 static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11);
100 static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
101 XEvent *event, int incr);
102 static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11);
103 static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
104 XEvent *event, int incr);
105 static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
106 Atom prop,
107 int process_next_req);
108 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11,
109 int new_owner);
111 struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd,
112 FILE *errfile, int verbose)
114 struct vdagent_x11 *x11;
115 XWindowAttributes attrib;
116 int i, j, major, minor;
118 x11 = calloc(1, sizeof(*x11));
119 if (!x11) {
120 fprintf(errfile, "out of memory allocating vdagent_x11 struct\n");
121 return NULL;
124 x11->vdagentd = vdagentd;
125 x11->errfile = errfile;
126 x11->verbose = verbose;
128 x11->display = XOpenDisplay(NULL);
129 if (!x11->display) {
130 fprintf(x11->errfile, "could not connect to X-server\n");
131 free(x11);
132 return NULL;
135 x11->screen = DefaultScreen(x11->display);
136 x11->root_window = RootWindow(x11->display, x11->screen);
137 x11->fd = ConnectionNumber(x11->display);
138 x11->clipboard_atom = XInternAtom(x11->display, "CLIPBOARD", False);
139 x11->targets_atom = XInternAtom(x11->display, "TARGETS", False);
140 x11->incr_atom = XInternAtom(x11->display, "INCR", False);
141 x11->multiple_atom = XInternAtom(x11->display, "MULTIPLE", False);
142 for(i = 0; i < clipboard_format_count; i++) {
143 x11->clipboard_formats[i].type = clipboard_format_templates[i].type;
144 for(j = 0; clipboard_format_templates[i].atom_names[j]; j++) {
145 x11->clipboard_formats[i].atoms[j] =
146 XInternAtom(x11->display,
147 clipboard_format_templates[i].atom_names[j],
148 False);
150 x11->clipboard_formats[i].atom_count = j;
153 /* We should not store properties (for selections) on the root window */
154 x11->selection_window = XCreateSimpleWindow(x11->display, x11->root_window,
155 0, 0, 1, 1, 0, 0, 0);
156 if (x11->verbose)
157 fprintf(x11->errfile, "Selection window: %u\n",
158 (unsigned int)x11->selection_window);
160 if (XRRQueryExtension(x11->display, &i, &i))
161 x11->has_xrandr = 1;
162 else
163 fprintf(x11->errfile, "no xrandr\n");
165 if (XFixesQueryExtension(x11->display, &x11->xfixes_event_base, &i) &&
166 XFixesQueryVersion(x11->display, &major, &minor) && major >= 1) {
167 x11->has_xfixes = 1;
168 XFixesSelectSelectionInput(x11->display, x11->root_window,
169 x11->clipboard_atom,
170 XFixesSetSelectionOwnerNotifyMask|
171 XFixesSelectionWindowDestroyNotifyMask|
172 XFixesSelectionClientCloseNotifyMask);
173 } else
174 fprintf(x11->errfile,
175 "no xfixes, no guest -> client copy paste support\n");
177 /* Catch resolution changes */
178 XSelectInput(x11->display, x11->root_window, StructureNotifyMask);
180 /* Get the current resolution */
181 XGetWindowAttributes(x11->display, x11->root_window, &attrib);
182 x11->width = attrib.width;
183 x11->height = attrib.height;
184 vdagent_x11_send_daemon_guest_xorg_res(x11);
186 /* No need for XFlush as XGetWindowAttributes does an implicit XFlush */
188 return x11;
191 void vdagent_x11_destroy(struct vdagent_x11 *x11)
193 if (!x11)
194 return;
196 vdagent_x11_set_clipboard_owner(x11, owner_none);
197 XCloseDisplay(x11->display);
198 free(x11);
201 int vdagent_x11_get_fd(struct vdagent_x11 *x11)
203 return x11->fd;
206 static void vdagent_x11_next_selection_request(struct vdagent_x11 *x11)
208 struct vdagent_x11_selection_request *selection_request;
209 selection_request = x11->selection_request;
210 x11->selection_request = selection_request->next;
211 free(selection_request);
214 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11,
215 int new_owner)
217 /* Clear pending requests and clipboard data */
218 if (x11->selection_request) {
219 fprintf(x11->errfile,
220 "selection requests pending on clipboard ownership change, "
221 "clearing");
222 while (x11->selection_request) {
223 vdagent_x11_send_selection_notify(x11, None, 0);
224 vdagent_x11_next_selection_request(x11);
227 if (x11->clipboard_request_target != None) {
228 fprintf(x11->errfile,
229 "client clipboard request pending on clipboard ownership "
230 "change, clearing");
231 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
232 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
233 x11->clipboard_request_target = None;
235 x11->clipboard_data_size = 0;
236 x11->expect_property_notify = 0;
238 if (new_owner == owner_none) {
239 /* When going from owner_guest to owner_none we need to send a
240 clipboard release message to the client */
241 if (x11->clipboard_owner == owner_guest)
242 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_RELEASE, 0, NULL, 0);
244 x11->clipboard_type_count = 0;
246 x11->clipboard_owner = new_owner;
249 static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
251 int handled = 0;
254 if (event.type == x11->xfixes_event_base) {
255 union {
256 XEvent ev;
257 XFixesSelectionNotifyEvent xfev;
258 } ev;
260 ev.ev = event;
261 switch (ev.xfev.subtype) {
262 case XFixesSetSelectionOwnerNotify:
263 break;
264 /* Treat ... as a SelectionOwnerNotify None */
265 case XFixesSelectionWindowDestroyNotify:
266 case XFixesSelectionClientCloseNotify:
267 ev.xfev.owner = None;
268 break;
269 default:
270 if (x11->verbose)
271 fprintf(x11->errfile,
272 "unexpected xfix event subtype %d window %d\n",
273 (int)ev.xfev.subtype, (int)event.xany.window);
274 return;
277 if (x11->verbose)
278 fprintf(x11->errfile, "New selection owner: %u\n",
279 (unsigned int)ev.xfev.owner);
281 /* Ignore becoming the owner ourselves */
282 if (ev.xfev.owner == x11->selection_window)
283 return;
285 /* If the clipboard owner is changed we no longer own it */
286 vdagent_x11_set_clipboard_owner(x11, owner_none);
288 if (ev.xfev.owner == None)
289 return;
291 /* Request the supported targets from the new owner */
292 XConvertSelection(x11->display, x11->clipboard_atom, x11->targets_atom,
293 x11->targets_atom, x11->selection_window,
294 CurrentTime);
295 XFlush(x11->display);
296 x11->expected_targets_notifies++;
297 return;
300 switch (event.type) {
301 case ConfigureNotify:
302 if (event.xconfigure.window != x11->root_window)
303 break;
305 handled = 1;
307 if (event.xconfigure.width == x11->width &&
308 event.xconfigure.height == x11->height)
309 break;
311 x11->width = event.xconfigure.width;
312 x11->height = event.xconfigure.height;
314 vdagent_x11_send_daemon_guest_xorg_res(x11);
315 break;
316 case SelectionNotify:
317 if (event.xselection.target == x11->targets_atom)
318 vdagent_x11_handle_targets_notify(x11, &event, 0);
319 else
320 vdagent_x11_handle_selection_notify(x11, &event, 0);
322 handled = 1;
323 break;
324 case PropertyNotify:
325 handled = 1;
326 if (!x11->expect_property_notify ||
327 event.xproperty.state != PropertyNewValue)
328 break;
330 if (event.xproperty.atom == x11->targets_atom)
331 vdagent_x11_handle_targets_notify(x11, &event, 1);
332 else
333 vdagent_x11_handle_selection_notify(x11, &event, 1);
334 break;
335 case SelectionClear:
336 /* Do nothing the clipboard ownership will get updated through
337 the XFixesSetSelectionOwnerNotify event */
338 handled = 1;
339 break;
340 case SelectionRequest: {
341 struct vdagent_x11_selection_request *req, *new_req;
343 new_req = malloc(sizeof(*new_req));
344 if (!new_req) {
345 fprintf(x11->errfile,
346 "out of memory on SelectionRequest, ignoring.\n");
347 break;
350 handled = 1;
352 new_req->event = event;
353 new_req->next = NULL;
355 if (!x11->selection_request) {
356 x11->selection_request = new_req;
357 vdagent_x11_handle_selection_request(x11);
358 break;
361 /* maybe we should limit the selection_request stack depth ? */
362 req = x11->selection_request;
363 while (req->next)
364 req = req->next;
366 req->next = new_req;
367 break;
370 if (!handled && x11->verbose)
371 fprintf(x11->errfile, "unhandled x11 event, type %d, window %d\n",
372 (int)event.type, (int)event.xany.window);
375 void vdagent_x11_do_read(struct vdagent_x11 *x11)
377 XEvent event;
379 while (XPending(x11->display)) {
380 XNextEvent(x11->display, &event);
381 vdagent_x11_handle_event(x11, event);
385 static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11)
387 struct vdagentd_guest_xorg_resolution res;
389 res.width = x11->width;
390 res.height = x11->height;
392 udscs_write(x11->vdagentd, VDAGENTD_GUEST_XORG_RESOLUTION, 0,
393 (uint8_t *)&res, sizeof(res));
396 static const char *vdagent_x11_get_atom_name(struct vdagent_x11 *x11, Atom a)
398 if (a == None)
399 return "None";
401 return XGetAtomName(x11->display, a);
404 static int vdagent_x11_get_selection(struct vdagent_x11 *x11, XEvent *event,
405 Atom type, Atom prop, int format,
406 unsigned char **data_ret, int incr)
408 Bool del = incr ? True: False;
409 Atom type_ret;
410 int format_ret, ret_val = -1;
411 unsigned long len, remain;
412 unsigned char *data = NULL;
414 *data_ret = NULL;
416 if (incr) {
417 if (event->xproperty.atom != prop) {
418 fprintf(x11->errfile, "PropertyNotify parameters mismatch\n");
419 goto exit;
421 } else {
422 if (event->xselection.property == None) {
423 if (x11->verbose)
424 fprintf(x11->errfile,
425 "XConvertSelection refused by clipboard owner\n");
426 goto exit;
429 if (event->xselection.requestor != x11->selection_window ||
430 event->xselection.selection != x11->clipboard_atom ||
431 event->xselection.property != prop) {
432 fprintf(x11->errfile, "SelectionNotify parameters mismatch\n");
433 goto exit;
437 if (XGetWindowProperty(x11->display, x11->selection_window, prop, 0,
438 LONG_MAX, del, type, &type_ret, &format_ret, &len,
439 &remain, &data) != Success) {
440 fprintf(x11->errfile, "XGetWindowProperty failed\n");
441 goto exit;
444 if (!incr) {
445 if (type_ret == x11->incr_atom) {
446 int prop_min_size = *(uint32_t*)data;
448 if (x11->expect_property_notify) {
449 fprintf(x11->errfile,
450 "received an incr SelectionNotify while "
451 "still reading another incr property\n");
452 goto exit;
455 if (x11->clipboard_data_space < prop_min_size) {
456 free(x11->clipboard_data);
457 x11->clipboard_data = malloc(prop_min_size);
458 if (!x11->clipboard_data) {
459 fprintf(x11->errfile,
460 "out of memory allocating clipboard buffer\n");
461 x11->clipboard_data_space = 0;
462 goto exit;
464 x11->clipboard_data_space = prop_min_size;
466 x11->expect_property_notify = 1;
467 XSelectInput(x11->display, x11->selection_window,
468 PropertyChangeMask);
469 XDeleteProperty(x11->display, x11->selection_window, prop);
470 XFlush(x11->display);
471 XFree(data);
472 return 0; /* Wait for more data */
474 XDeleteProperty(x11->display, x11->selection_window, prop);
475 XFlush(x11->display);
478 if (type_ret != type) {
479 fprintf(x11->errfile, "expected property type: %s, got: %s\n",
480 vdagent_x11_get_atom_name(x11, type),
481 vdagent_x11_get_atom_name(x11, type_ret));
482 goto exit;
485 if (format_ret != format) {
486 fprintf(x11->errfile, "expected %d bit format, got %d bits\n", format,
487 format_ret);
488 goto exit;
491 /* Convert len to bytes */
492 switch(format) {
493 case 8:
494 break;
495 case 16:
496 len *= sizeof(short);
497 break;
498 case 32:
499 len *= sizeof(long);
500 break;
503 if (incr) {
504 if (len) {
505 if (x11->clipboard_data_size + len > x11->clipboard_data_space) {
506 void *old_clipboard_data = x11->clipboard_data;
508 x11->clipboard_data_space = x11->clipboard_data_size + len;
509 x11->clipboard_data = realloc(x11->clipboard_data,
510 x11->clipboard_data_space);
511 if (!x11->clipboard_data) {
512 fprintf(x11->errfile,
513 "out of memory allocating clipboard buffer\n");
514 x11->clipboard_data_space = 0;
515 free(old_clipboard_data);
516 goto exit;
519 memcpy(x11->clipboard_data + x11->clipboard_data_size, data, len);
520 x11->clipboard_data_size += len;
521 if (x11->verbose)
522 fprintf(x11->errfile, "Appended %ld bytes to buffer\n", len);
523 XFree(data);
524 return 0; /* Wait for more data */
526 len = x11->clipboard_data_size;
527 *data_ret = x11->clipboard_data;
528 } else
529 *data_ret = data;
531 if (len > 0) {
532 ret_val = len;
533 } else {
534 fprintf(x11->errfile, "property contains no data (zero length)\n");
535 *data_ret = NULL;
538 exit:
539 if ((incr || ret_val == -1) && data)
540 XFree(data);
542 if (incr) {
543 x11->clipboard_data_size = 0;
544 x11->expect_property_notify = 0;
547 return ret_val;
550 static void vdagent_x11_get_selection_free(struct vdagent_x11 *x11,
551 unsigned char *data, int incr)
553 if (incr) {
554 /* If the clipboard has grown large return the memory to the system */
555 if (x11->clipboard_data_space > 512 * 1024) {
556 free(x11->clipboard_data);
557 x11->clipboard_data = NULL;
558 x11->clipboard_data_space = 0;
560 } else if (data)
561 XFree(data);
564 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11 *x11,
565 Atom target)
567 int i, j;
569 for (i = 0; i < clipboard_format_count; i++) {
570 for (j = 0; j < x11->clipboard_formats[i].atom_count; i++) {
571 if (x11->clipboard_formats[i].atoms[j] == target) {
572 return x11->clipboard_formats[i].type;
577 fprintf(x11->errfile, "unexpected selection type %s\n",
578 vdagent_x11_get_atom_name(x11, target));
579 return VD_AGENT_CLIPBOARD_NONE;
582 static Atom vdagent_x11_type_to_target(struct vdagent_x11 *x11, uint32_t type)
584 int i;
586 for (i = 0; i < x11->clipboard_type_count; i++)
587 if (x11->clipboard_agent_types[i] == type)
588 return x11->clipboard_x11_targets[i];
590 fprintf(x11->errfile, "client requested unavailable type %u\n", type);
591 return None;
594 static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
595 XEvent *event, int incr)
597 int len = -1;
598 unsigned char *data = NULL;
599 uint32_t type;
601 if (x11->clipboard_request_target == None) {
602 fprintf(x11->errfile, "SelectionNotify received without a target\n");
603 return;
606 type = vdagent_x11_target_to_type(x11, x11->clipboard_request_target);
607 if (!incr &&
608 event->xselection.target != x11->clipboard_request_target &&
609 event->xselection.target != x11->incr_atom)
610 fprintf(x11->errfile, "Requested %s target got %s\n",
611 vdagent_x11_get_atom_name(x11, x11->clipboard_request_target),
612 vdagent_x11_get_atom_name(x11, event->xselection.target));
613 else
614 len = vdagent_x11_get_selection(x11, event, x11->clipboard_request_target,
615 x11->clipboard_atom, 8, &data, incr);
616 if (len == 0) /* waiting for more data? */
617 return;
618 if (len == -1) {
619 type = VD_AGENT_CLIPBOARD_NONE;
620 len = 0;
623 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, type, data, len);
624 x11->clipboard_request_target = None;
625 vdagent_x11_get_selection_free(x11, data, incr);
628 static Atom atom_lists_overlap(Atom *atoms1, Atom *atoms2, int l1, int l2)
630 int i, j;
632 for (i = 0; i < l1; i++)
633 for (j = 0; j < l2; j++)
634 if (atoms1[i] == atoms2[j])
635 return atoms1[i];
637 return 0;
640 static void vdagent_x11_print_targets(struct vdagent_x11 *x11,
641 const char *action, Atom *atoms, int c)
643 int i;
645 if (!x11->verbose)
646 return;
648 fprintf(x11->errfile, "%s %d targets:\n", action, c);
649 for (i = 0; i < c; i++)
650 fprintf(x11->errfile, "%s\n",
651 vdagent_x11_get_atom_name(x11, atoms[i]));
654 static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
655 XEvent *event, int incr)
657 int i, len;
658 Atom atom, *atoms = NULL;
660 if (!x11->expected_targets_notifies) {
661 fprintf(x11->errfile, "unexpected selection notify TARGETS\n");
662 return;
665 x11->expected_targets_notifies--;
667 /* If we have more targets_notifies pending, ignore this one, we
668 are only interested in the targets list of the current owner
669 (which is the last one we've requested a targets list from) */
670 if (x11->expected_targets_notifies)
671 return;
673 len = vdagent_x11_get_selection(x11, event, XA_ATOM, x11->targets_atom, 32,
674 (unsigned char **)&atoms, incr);
675 if (len == 0 || len == -1) /* waiting for more data or error? */
676 return;
678 /* bytes -> atoms */
679 len /= sizeof(Atom);
680 vdagent_x11_print_targets(x11, "received", atoms, len);
682 x11->clipboard_type_count = 0;
683 for (i = 0; i < clipboard_format_count; i++) {
684 atom = atom_lists_overlap(x11->clipboard_formats[i].atoms, atoms,
685 x11->clipboard_formats[i].atom_count, len);
686 if (atom) {
687 x11->clipboard_agent_types[x11->clipboard_type_count] =
688 x11->clipboard_formats[i].type;
689 x11->clipboard_x11_targets[x11->clipboard_type_count] = atom;
690 x11->clipboard_type_count++;
691 if (x11->clipboard_type_count ==
692 sizeof(x11->clipboard_agent_types)/sizeof(uint32_t)) {
693 fprintf(x11->errfile,
694 "handle_targets_notify: too many types\n");
695 break;
700 if (x11->clipboard_type_count) {
701 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_GRAB, 0,
702 (uint8_t *)x11->clipboard_agent_types,
703 x11->clipboard_type_count * sizeof(uint32_t));
704 vdagent_x11_set_clipboard_owner(x11, owner_guest);
707 vdagent_x11_get_selection_free(x11, (unsigned char *)atoms, incr);
710 static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
711 Atom prop, int process_next_req)
713 XEvent res, *event = &x11->selection_request->event;
715 res.xselection.property = prop;
716 res.xselection.type = SelectionNotify;
717 res.xselection.display = event->xselectionrequest.display;
718 res.xselection.requestor = event->xselectionrequest.requestor;
719 res.xselection.selection = event->xselectionrequest.selection;
720 res.xselection.target = event->xselectionrequest.target;
721 res.xselection.time = event->xselectionrequest.time;
722 XSendEvent(x11->display, event->xselectionrequest.requestor, 0, 0, &res);
723 XFlush(x11->display);
725 if (process_next_req) {
726 vdagent_x11_next_selection_request(x11);
727 vdagent_x11_handle_selection_request(x11);
731 static void vdagent_x11_send_targets(struct vdagent_x11 *x11, XEvent *event)
733 Atom prop, targets[256] = { x11->targets_atom, };
734 int i, j, k, target_count = 1;
736 for (i = 0; i < x11->clipboard_type_count; i++) {
737 for (j = 0; j < clipboard_format_count; j++) {
738 if (x11->clipboard_formats[j].type !=
739 x11->clipboard_agent_types[i])
740 continue;
742 for (k = 0; k < x11->clipboard_formats[j].atom_count; k++) {
743 targets[target_count] = x11->clipboard_formats[j].atoms[k];
744 target_count++;
745 if (target_count == sizeof(targets)/sizeof(Atom)) {
746 fprintf(x11->errfile, "send_targets: too many targets\n");
747 goto exit_loop;
752 exit_loop:
754 prop = event->xselectionrequest.property;
755 if (prop == None)
756 prop = event->xselectionrequest.target;
758 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
759 XA_ATOM, 32, PropModeReplace, (unsigned char *)&targets,
760 target_count);
761 vdagent_x11_print_targets(x11, "sent", targets, target_count);
762 vdagent_x11_send_selection_notify(x11, prop, 1);
765 static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11)
767 XEvent *event;
768 uint32_t type = VD_AGENT_CLIPBOARD_NONE;
770 if (!x11->selection_request)
771 return;
773 event = &x11->selection_request->event;
775 if (x11->clipboard_owner != owner_client) {
776 fprintf(x11->errfile,
777 "received selection request event for target %s, "
778 "while not owning client clipboard\n",
779 vdagent_x11_get_atom_name(x11, event->xselectionrequest.target));
780 vdagent_x11_send_selection_notify(x11, None, 1);
781 return;
784 if (event->xselectionrequest.target == x11->multiple_atom) {
785 fprintf(x11->errfile, "multiple target not supported\n");
786 vdagent_x11_send_selection_notify(x11, None, 1);
787 return;
790 if (event->xselectionrequest.target == x11->targets_atom) {
791 vdagent_x11_send_targets(x11, event);
792 return;
795 type = vdagent_x11_target_to_type(x11, event->xselectionrequest.target);
796 if (type == VD_AGENT_CLIPBOARD_NONE) {
797 vdagent_x11_send_selection_notify(x11, None, 1);
798 return;
801 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_REQUEST, type, NULL, 0);
804 void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
805 VDAgentMonitorsConfig *mon_config)
807 int i, num_sizes = 0;
808 int best = -1;
809 unsigned int closest_diff = -1;
810 XRRScreenSize* sizes;
811 XRRScreenConfiguration* config;
812 Rotation rotation;
814 if (!x11->has_xrandr)
815 return;
817 if (mon_config->num_of_monitors != 1) {
818 fprintf(x11->errfile,
819 "Only 1 monitor supported, ignoring additional monitors\n");
822 sizes = XRRSizes(x11->display, x11->screen, &num_sizes);
823 if (!sizes || !num_sizes) {
824 fprintf(x11->errfile, "XRRSizes failed\n");
825 return;
828 /* Find the closest size which will fit within the monitor */
829 for (i = 0; i < num_sizes; i++) {
830 if (sizes[i].width > mon_config->monitors[0].width ||
831 sizes[i].height > mon_config->monitors[0].height)
832 continue; /* Too large for the monitor */
834 unsigned int wdiff = mon_config->monitors[0].width - sizes[i].width;
835 unsigned int hdiff = mon_config->monitors[0].height - sizes[i].height;
836 unsigned int diff = wdiff * wdiff + hdiff * hdiff;
837 if (diff < closest_diff) {
838 closest_diff = diff;
839 best = i;
843 if (best == -1) {
844 fprintf(x11->errfile, "no suitable resolution found for monitor\n");
845 return;
848 config = XRRGetScreenInfo(x11->display, x11->root_window);
849 if(!config) {
850 fprintf(x11->errfile, "get screen info failed\n");
851 return;
853 XRRConfigCurrentConfiguration(config, &rotation);
854 XRRSetScreenConfig(x11->display, config, x11->root_window, best,
855 rotation, CurrentTime);
856 XRRFreeScreenConfigInfo(config);
857 XFlush(x11->display);
858 x11->width = sizes[best].width;
859 x11->height = sizes[best].height;
860 vdagent_x11_send_daemon_guest_xorg_res(x11);
863 void vdagent_x11_clipboard_request(struct vdagent_x11 *x11, uint32_t type)
865 Atom target;
867 if (x11->clipboard_owner != owner_guest) {
868 fprintf(x11->errfile,
869 "received clipboard req while not owning guest clipboard\n");
870 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
871 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
872 return;
875 target = vdagent_x11_type_to_target(x11, type);
876 if (target == None) {
877 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
878 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
879 return;
882 if (x11->clipboard_request_target) {
883 fprintf(x11->errfile,
884 "XConvertSelection request is already pending\n");
885 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
886 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
887 return;
889 x11->clipboard_request_target = target;
890 XConvertSelection(x11->display, x11->clipboard_atom, target,
891 x11->clipboard_atom, x11->selection_window, CurrentTime);
892 XFlush(x11->display);
895 void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint32_t *types,
896 uint32_t type_count)
898 if (type_count > sizeof(x11->clipboard_agent_types)/sizeof(uint32_t)) {
899 fprintf(x11->errfile, "x11_clipboard_grab: too many types\n");
900 type_count = sizeof(x11->clipboard_agent_types)/sizeof(uint32_t);
903 memcpy(x11->clipboard_agent_types, types, type_count * sizeof(uint32_t));
904 x11->clipboard_type_count = type_count;
906 XSetSelectionOwner(x11->display, x11->clipboard_atom,
907 x11->selection_window, CurrentTime);
908 XFlush(x11->display);
909 vdagent_x11_set_clipboard_owner(x11, owner_client);
912 void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint32_t type,
913 uint8_t *data, uint32_t size)
915 Atom prop;
916 XEvent *event;
917 uint32_t type_from_event;
919 if (!x11->selection_request) {
920 fprintf(x11->errfile, "received clipboard data without an outstanding"
921 "selection request, ignoring\n");
922 free(data);
923 return;
926 event = &x11->selection_request->event;
927 type_from_event = vdagent_x11_target_to_type(x11,
928 event->xselectionrequest.target);
929 if (type_from_event != type) {
930 fprintf(x11->errfile, "expecting type %u clipboard data got %u\n",
931 type_from_event, type);
932 vdagent_x11_send_selection_notify(x11, None, 1);
933 free(data);
934 return;
937 prop = event->xselectionrequest.property;
938 if (prop == None)
939 prop = event->xselectionrequest.target;
941 /* FIXME: use INCR for large data transfers */
942 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
943 event->xselectionrequest.target, 8, PropModeReplace,
944 data, size);
945 vdagent_x11_send_selection_notify(x11, prop, 1);
946 free(data);
949 void vdagent_x11_clipboard_release(struct vdagent_x11 *x11)
951 XEvent event;
953 if (x11->clipboard_owner != owner_client) {
954 fprintf(x11->errfile,
955 "received clipboard release while not owning client clipboard\n");
956 return;
959 XSetSelectionOwner(x11->display, x11->clipboard_atom, None, CurrentTime);
960 /* Make sure we process the XFixesSetSelectionOwnerNotify event caused
961 by this, so we don't end up changing the clipboard owner to none, after
962 it has already been re-owned because this event is still pending. */
963 XSync(x11->display, False);
964 while (XCheckTypedEvent(x11->display, x11->xfixes_event_base,
965 &event))
966 vdagent_x11_handle_event(x11, event);
968 /* Note no need to do a set_clipboard_owner(owner_none) here, as that is
969 already done by processing the XFixesSetSelectionOwnerNotify event. */