Only start spice-vdagentd in runlevel 5
[vd_agent.git] / vdagent-x11.c
blob11e9c45caf9b68970783981fb40c639a253844b1
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);
109 struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd,
110 FILE *errfile, int verbose)
112 struct vdagent_x11 *x11;
113 XWindowAttributes attrib;
114 int i, j, major, minor;
116 x11 = calloc(1, sizeof(*x11));
117 if (!x11) {
118 fprintf(errfile, "out of memory allocating vdagent_x11 struct\n");
119 return NULL;
122 x11->vdagentd = vdagentd;
123 x11->errfile = errfile;
124 x11->verbose = verbose;
126 x11->display = XOpenDisplay(NULL);
127 if (!x11->display) {
128 fprintf(x11->errfile, "could not connect to X-server\n");
129 free(x11);
130 return NULL;
133 x11->screen = DefaultScreen(x11->display);
134 x11->root_window = RootWindow(x11->display, x11->screen);
135 x11->fd = ConnectionNumber(x11->display);
136 x11->clipboard_atom = XInternAtom(x11->display, "CLIPBOARD", False);
137 x11->targets_atom = XInternAtom(x11->display, "TARGETS", False);
138 x11->incr_atom = XInternAtom(x11->display, "INCR", False);
139 x11->multiple_atom = XInternAtom(x11->display, "MULTIPLE", False);
140 for(i = 0; i < clipboard_format_count; i++) {
141 x11->clipboard_formats[i].type = clipboard_format_templates[i].type;
142 for(j = 0; clipboard_format_templates[i].atom_names[j]; j++) {
143 x11->clipboard_formats[i].atoms[j] =
144 XInternAtom(x11->display,
145 clipboard_format_templates[i].atom_names[j],
146 False);
148 x11->clipboard_formats[i].atom_count = j;
151 /* We should not store properties (for selections) on the root window */
152 x11->selection_window = XCreateSimpleWindow(x11->display, x11->root_window,
153 0, 0, 1, 1, 0, 0, 0);
154 if (x11->verbose)
155 fprintf(x11->errfile, "Selection window: %u\n",
156 (unsigned int)x11->selection_window);
158 if (XRRQueryExtension(x11->display, &i, &i))
159 x11->has_xrandr = 1;
160 else
161 fprintf(x11->errfile, "no xrandr\n");
163 if (XFixesQueryExtension(x11->display, &x11->xfixes_event_base, &i) &&
164 XFixesQueryVersion(x11->display, &major, &minor) && major >= 1) {
165 x11->has_xfixes = 1;
166 XFixesSelectSelectionInput(x11->display, x11->root_window,
167 x11->clipboard_atom,
168 XFixesSetSelectionOwnerNotifyMask|
169 XFixesSelectionWindowDestroyNotifyMask|
170 XFixesSelectionClientCloseNotifyMask);
171 } else
172 fprintf(x11->errfile,
173 "no xfixes, no guest -> client copy paste support\n");
175 /* Catch resolution changes */
176 XSelectInput(x11->display, x11->root_window, StructureNotifyMask);
178 /* Get the current resolution */
179 XGetWindowAttributes(x11->display, x11->root_window, &attrib);
180 x11->width = attrib.width;
181 x11->height = attrib.height;
182 vdagent_x11_send_daemon_guest_xorg_res(x11);
184 /* No need for XFlush as XGetWindowAttributes does an implicit Xflush */
186 return x11;
189 void vdagent_x11_destroy(struct vdagent_x11 *x11)
191 if (!x11)
192 return;
194 XCloseDisplay(x11->display);
195 free(x11);
198 int vdagent_x11_get_fd(struct vdagent_x11 *x11)
200 return x11->fd;
203 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11,
204 int new_owner)
206 /* Clear pending requests and clipboard data */
207 if (x11->selection_request) {
208 fprintf(x11->errfile,
209 "selection requests pending on clipboard ownership change, "
210 "clearing");
211 while (x11->selection_request)
212 vdagent_x11_send_selection_notify(x11, None, 0);
214 if (x11->clipboard_request_target != None) {
215 fprintf(x11->errfile,
216 "client clipboard request pending on clipboard ownership "
217 "change, clearing");
218 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
219 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
220 x11->clipboard_request_target = None;
222 x11->clipboard_data_size = 0;
223 x11->expect_property_notify = 0;
225 if (new_owner == owner_none) {
226 /* When going from owner_guest to owner_none we need to send a
227 clipboard release message to the client */
228 if (x11->clipboard_owner == owner_guest)
229 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_RELEASE, 0, NULL, 0);
231 x11->clipboard_type_count = 0;
233 x11->clipboard_owner = new_owner;
236 static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
238 int handled = 0;
241 if (event.type == x11->xfixes_event_base) {
242 union {
243 XEvent ev;
244 XFixesSelectionNotifyEvent xfev;
245 } ev;
247 ev.ev = event;
248 switch (ev.xfev.subtype) {
249 case XFixesSetSelectionOwnerNotify:
250 break;
251 /* Treat ... as a SelectionOwnerNotify None */
252 case XFixesSelectionWindowDestroyNotify:
253 case XFixesSelectionClientCloseNotify:
254 ev.xfev.owner = None;
255 break;
256 default:
257 if (x11->verbose)
258 fprintf(x11->errfile,
259 "unexpected xfix event subtype %d window %d\n",
260 (int)ev.xfev.subtype, (int)event.xany.window);
261 return;
264 if (x11->verbose)
265 fprintf(x11->errfile, "New selection owner: %u\n",
266 (unsigned int)ev.xfev.owner);
268 /* Ignore becoming the owner ourselves */
269 if (ev.xfev.owner == x11->selection_window)
270 return;
272 /* If the clipboard owner is changed we no longer own it */
273 vdagent_x11_set_clipboard_owner(x11, owner_none);
275 if (ev.xfev.owner == None)
276 return;
278 /* Request the supported targets from the new owner */
279 XConvertSelection(x11->display, x11->clipboard_atom, x11->targets_atom,
280 x11->targets_atom, x11->selection_window,
281 CurrentTime);
282 XFlush(x11->display);
283 x11->expected_targets_notifies++;
284 return;
287 switch (event.type) {
288 case ConfigureNotify:
289 if (event.xconfigure.window != x11->root_window)
290 break;
292 handled = 1;
294 if (event.xconfigure.width == x11->width &&
295 event.xconfigure.height == x11->height)
296 break;
298 x11->width = event.xconfigure.width;
299 x11->height = event.xconfigure.height;
301 vdagent_x11_send_daemon_guest_xorg_res(x11);
302 break;
303 case SelectionNotify:
304 if (event.xselection.target == x11->targets_atom)
305 vdagent_x11_handle_targets_notify(x11, &event, 0);
306 else
307 vdagent_x11_handle_selection_notify(x11, &event, 0);
309 handled = 1;
310 break;
311 case PropertyNotify:
312 handled = 1;
313 if (!x11->expect_property_notify ||
314 event.xproperty.state != PropertyNewValue)
315 break;
317 if (event.xproperty.atom == x11->targets_atom)
318 vdagent_x11_handle_targets_notify(x11, &event, 1);
319 else
320 vdagent_x11_handle_selection_notify(x11, &event, 1);
321 break;
322 case SelectionClear:
323 /* Do nothing the clipboard ownership will get updated through
324 the XFixesSetSelectionOwnerNotify event */
325 handled = 1;
326 break;
327 case SelectionRequest: {
328 struct vdagent_x11_selection_request *req, *new_req;
330 new_req = malloc(sizeof(*new_req));
331 if (!new_req) {
332 fprintf(x11->errfile,
333 "out of memory on SelectionRequest, ignoring.\n");
334 break;
337 handled = 1;
339 new_req->event = event;
340 new_req->next = NULL;
342 if (!x11->selection_request) {
343 x11->selection_request = new_req;
344 vdagent_x11_handle_selection_request(x11);
345 break;
348 /* maybe we should limit the selection_request stack depth ? */
349 req = x11->selection_request;
350 while (req->next)
351 req = req->next;
353 req->next = new_req;
354 break;
357 if (!handled && x11->verbose)
358 fprintf(x11->errfile, "unhandled x11 event, type %d, window %d\n",
359 (int)event.type, (int)event.xany.window);
362 void vdagent_x11_do_read(struct vdagent_x11 *x11)
364 XEvent event;
366 while (XPending(x11->display)) {
367 XNextEvent(x11->display, &event);
368 vdagent_x11_handle_event(x11, event);
372 static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11)
374 struct vdagentd_guest_xorg_resolution res;
376 res.width = x11->width;
377 res.height = x11->height;
379 udscs_write(x11->vdagentd, VDAGENTD_GUEST_XORG_RESOLUTION, 0,
380 (uint8_t *)&res, sizeof(res));
383 static const char *vdagent_x11_get_atom_name(struct vdagent_x11 *x11, Atom a)
385 if (a == None)
386 return "None";
388 return XGetAtomName(x11->display, a);
391 static int vdagent_x11_get_selection(struct vdagent_x11 *x11, XEvent *event,
392 Atom type, Atom prop, int format,
393 unsigned char **data_ret, int incr)
395 Bool del = incr ? True: False;
396 Atom type_ret;
397 int format_ret, ret_val = -1;
398 unsigned long len, remain;
399 unsigned char *data = NULL;
401 *data_ret = NULL;
403 if (incr) {
404 if (event->xproperty.atom != prop) {
405 fprintf(x11->errfile, "PropertyNotify parameters mismatch\n");
406 goto exit;
408 } else {
409 if (event->xselection.property == None) {
410 if (x11->verbose)
411 fprintf(x11->errfile,
412 "XConvertSelection refused by clipboard owner\n");
413 goto exit;
416 if (event->xselection.requestor != x11->selection_window ||
417 event->xselection.selection != x11->clipboard_atom ||
418 event->xselection.property != prop) {
419 fprintf(x11->errfile, "SelectionNotify parameters mismatch\n");
420 goto exit;
424 if (XGetWindowProperty(x11->display, x11->selection_window, prop, 0,
425 LONG_MAX, del, type, &type_ret, &format_ret, &len,
426 &remain, &data) != Success) {
427 fprintf(x11->errfile, "XGetWindowProperty failed\n");
428 goto exit;
431 if (!incr) {
432 if (type_ret == x11->incr_atom) {
433 int prop_min_size = *(uint32_t*)data;
435 if (x11->expect_property_notify) {
436 fprintf(x11->errfile,
437 "received an incr SelectionNotify while "
438 "still reading another incr property\n");
439 goto exit;
442 if (x11->clipboard_data_space < prop_min_size) {
443 free(x11->clipboard_data);
444 x11->clipboard_data = malloc(prop_min_size);
445 if (!x11->clipboard_data) {
446 fprintf(x11->errfile,
447 "out of memory allocating clipboard buffer\n");
448 x11->clipboard_data_space = 0;
449 goto exit;
451 x11->clipboard_data_space = prop_min_size;
453 x11->expect_property_notify = 1;
454 XSelectInput(x11->display, x11->selection_window,
455 PropertyChangeMask);
456 XDeleteProperty(x11->display, x11->selection_window, prop);
457 XFlush(x11->display);
458 XFree(data);
459 return 0; /* Wait for more data */
461 XDeleteProperty(x11->display, x11->selection_window, prop);
462 XFlush(x11->display);
465 if (type_ret != type) {
466 fprintf(x11->errfile, "expected property type: %s, got: %s\n",
467 vdagent_x11_get_atom_name(x11, type),
468 vdagent_x11_get_atom_name(x11, type_ret));
469 goto exit;
472 if (format_ret != format) {
473 fprintf(x11->errfile, "expected %d bit format, got %d bits\n", format,
474 format_ret);
475 goto exit;
478 /* Convert len to bytes */
479 switch(format) {
480 case 8:
481 break;
482 case 16:
483 len *= sizeof(short);
484 break;
485 case 32:
486 len *= sizeof(long);
487 break;
490 if (incr) {
491 if (len) {
492 if (x11->clipboard_data_size + len > x11->clipboard_data_space) {
493 void *old_clipboard_data = x11->clipboard_data;
495 x11->clipboard_data_space = x11->clipboard_data_size + len;
496 x11->clipboard_data = realloc(x11->clipboard_data,
497 x11->clipboard_data_space);
498 if (!x11->clipboard_data) {
499 fprintf(x11->errfile,
500 "out of memory allocating clipboard buffer\n");
501 x11->clipboard_data_space = 0;
502 free(old_clipboard_data);
503 goto exit;
506 memcpy(x11->clipboard_data + x11->clipboard_data_size, data, len);
507 x11->clipboard_data_size += len;
508 if (x11->verbose)
509 fprintf(x11->errfile, "Appended %ld bytes to buffer\n", len);
510 XFree(data);
511 return 0; /* Wait for more data */
513 len = x11->clipboard_data_size;
514 *data_ret = x11->clipboard_data;
515 } else
516 *data_ret = data;
518 if (len > 0) {
519 ret_val = len;
520 } else {
521 fprintf(x11->errfile, "property contains no data (zero length)\n");
522 *data_ret = NULL;
525 exit:
526 if ((incr || ret_val == -1) && data)
527 XFree(data);
529 if (incr) {
530 x11->clipboard_data_size = 0;
531 x11->expect_property_notify = 0;
534 return ret_val;
537 static void vdagent_x11_get_selection_free(struct vdagent_x11 *x11,
538 unsigned char *data, int incr)
540 if (incr) {
541 /* If the clipboard has grown large return the memory to the system */
542 if (x11->clipboard_data_space > 512 * 1024) {
543 free(x11->clipboard_data);
544 x11->clipboard_data = NULL;
545 x11->clipboard_data_space = 0;
547 } else if (data)
548 XFree(data);
551 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11 *x11,
552 Atom target)
554 int i, j;
556 for (i = 0; i < clipboard_format_count; i++) {
557 for (j = 0; j < x11->clipboard_formats[i].atom_count; i++) {
558 if (x11->clipboard_formats[i].atoms[j] == target) {
559 return x11->clipboard_formats[i].type;
564 fprintf(x11->errfile, "unexpected selection type %s\n",
565 vdagent_x11_get_atom_name(x11, target));
566 return VD_AGENT_CLIPBOARD_NONE;
569 static Atom vdagent_x11_type_to_target(struct vdagent_x11 *x11, uint32_t type)
571 int i;
573 for (i = 0; i < x11->clipboard_type_count; i++)
574 if (x11->clipboard_agent_types[i] == type)
575 return x11->clipboard_x11_targets[i];
577 fprintf(x11->errfile, "client requested unavailable type %u\n", type);
578 return None;
581 static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
582 XEvent *event, int incr)
584 int len = -1;
585 unsigned char *data = NULL;
586 uint32_t type;
588 if (x11->clipboard_request_target == None) {
589 fprintf(x11->errfile, "SelectionNotify received without a target\n");
590 return;
593 type = vdagent_x11_target_to_type(x11, x11->clipboard_request_target);
594 if (!incr &&
595 event->xselection.target != x11->clipboard_request_target &&
596 event->xselection.target != x11->incr_atom)
597 fprintf(x11->errfile, "Requested %s target got %s\n",
598 vdagent_x11_get_atom_name(x11, x11->clipboard_request_target),
599 vdagent_x11_get_atom_name(x11, event->xselection.target));
600 else
601 len = vdagent_x11_get_selection(x11, event, x11->clipboard_request_target,
602 x11->clipboard_atom, 8, &data, incr);
603 if (len == 0) /* waiting for more data? */
604 return;
605 if (len == -1) {
606 type = VD_AGENT_CLIPBOARD_NONE;
607 len = 0;
610 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, type, data, len);
611 x11->clipboard_request_target = None;
612 vdagent_x11_get_selection_free(x11, data, incr);
615 static Atom atom_lists_overlap(Atom *atoms1, Atom *atoms2, int l1, int l2)
617 int i, j;
619 for (i = 0; i < l1; i++)
620 for (j = 0; j < l2; j++)
621 if (atoms1[i] == atoms2[j])
622 return atoms1[i];
624 return 0;
627 static void vdagent_x11_print_targets(struct vdagent_x11 *x11,
628 const char *action, Atom *atoms, int c)
630 int i;
632 if (!x11->verbose)
633 return;
635 fprintf(x11->errfile, "%s %d targets:\n", action, c);
636 for (i = 0; i < c; i++)
637 fprintf(x11->errfile, "%s\n",
638 vdagent_x11_get_atom_name(x11, atoms[i]));
641 static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
642 XEvent *event, int incr)
644 int i, len;
645 Atom atom, *atoms = NULL;
647 if (!x11->expected_targets_notifies) {
648 fprintf(x11->errfile, "unexpected selection notify TARGETS\n");
649 return;
652 x11->expected_targets_notifies--;
654 /* If we have more targets_notifies pending, ignore this one, we
655 are only interested in the targets list of the current owner
656 (which is the last one we've requested a targets list from) */
657 if (x11->expected_targets_notifies)
658 return;
660 len = vdagent_x11_get_selection(x11, event, XA_ATOM, x11->targets_atom, 32,
661 (unsigned char **)&atoms, incr);
662 if (len == 0 || len == -1) /* waiting for more data or error? */
663 return;
665 /* bytes -> atoms */
666 len /= sizeof(Atom);
667 vdagent_x11_print_targets(x11, "received", atoms, len);
669 x11->clipboard_type_count = 0;
670 for (i = 0; i < clipboard_format_count; i++) {
671 atom = atom_lists_overlap(x11->clipboard_formats[i].atoms, atoms,
672 x11->clipboard_formats[i].atom_count, len);
673 if (atom) {
674 x11->clipboard_agent_types[x11->clipboard_type_count] =
675 x11->clipboard_formats[i].type;
676 x11->clipboard_x11_targets[x11->clipboard_type_count] = atom;
677 x11->clipboard_type_count++;
678 if (x11->clipboard_type_count ==
679 sizeof(x11->clipboard_agent_types)/sizeof(uint32_t)) {
680 fprintf(x11->errfile,
681 "handle_targets_notify: too many types\n");
682 break;
687 if (x11->clipboard_type_count) {
688 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_GRAB, 0,
689 (uint8_t *)x11->clipboard_agent_types,
690 x11->clipboard_type_count * sizeof(uint32_t));
691 vdagent_x11_set_clipboard_owner(x11, owner_guest);
694 vdagent_x11_get_selection_free(x11, (unsigned char *)atoms, incr);
697 static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
698 Atom prop, int process_next_req)
700 XEvent res, *event = &x11->selection_request->event;
701 struct vdagent_x11_selection_request *selection_request;
703 res.xselection.property = prop;
704 res.xselection.type = SelectionNotify;
705 res.xselection.display = event->xselectionrequest.display;
706 res.xselection.requestor = event->xselectionrequest.requestor;
707 res.xselection.selection = event->xselectionrequest.selection;
708 res.xselection.target = event->xselectionrequest.target;
709 res.xselection.time = event->xselectionrequest.time;
710 XSendEvent(x11->display, event->xselectionrequest.requestor, 0, 0, &res);
711 XFlush(x11->display);
713 selection_request = x11->selection_request;
714 x11->selection_request = selection_request->next;
715 free(selection_request);
716 if (process_next_req)
717 vdagent_x11_handle_selection_request(x11);
720 static void vdagent_x11_send_targets(struct vdagent_x11 *x11, XEvent *event)
722 Atom prop, targets[256] = { x11->targets_atom, };
723 int i, j, k, target_count = 1;
725 for (i = 0; i < x11->clipboard_type_count; i++) {
726 for (j = 0; j < clipboard_format_count; j++) {
727 if (x11->clipboard_formats[j].type !=
728 x11->clipboard_agent_types[i])
729 continue;
731 for (k = 0; k < x11->clipboard_formats[j].atom_count; k++) {
732 targets[target_count] = x11->clipboard_formats[j].atoms[k];
733 target_count++;
734 if (target_count == sizeof(targets)/sizeof(Atom)) {
735 fprintf(x11->errfile, "send_targets: too many targets\n");
736 goto exit_loop;
741 exit_loop:
743 prop = event->xselectionrequest.property;
744 if (prop == None)
745 prop = event->xselectionrequest.target;
747 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
748 XA_ATOM, 32, PropModeReplace, (unsigned char *)&targets,
749 target_count);
750 vdagent_x11_print_targets(x11, "sent", targets, target_count);
751 vdagent_x11_send_selection_notify(x11, prop, 1);
754 static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11)
756 XEvent *event;
757 uint32_t type = VD_AGENT_CLIPBOARD_NONE;
759 if (!x11->selection_request)
760 return;
762 event = &x11->selection_request->event;
764 if (x11->clipboard_owner != owner_client) {
765 fprintf(x11->errfile,
766 "received selection request event for target %s, "
767 "while not owning client clipboard\n",
768 vdagent_x11_get_atom_name(x11, event->xselectionrequest.target));
769 vdagent_x11_send_selection_notify(x11, None, 1);
770 return;
773 if (event->xselectionrequest.target == x11->multiple_atom) {
774 fprintf(x11->errfile, "multiple target not supported\n");
775 vdagent_x11_send_selection_notify(x11, None, 1);
776 return;
779 if (event->xselectionrequest.target == x11->targets_atom) {
780 vdagent_x11_send_targets(x11, event);
781 return;
784 type = vdagent_x11_target_to_type(x11, event->xselectionrequest.target);
785 if (type == VD_AGENT_CLIPBOARD_NONE) {
786 vdagent_x11_send_selection_notify(x11, None, 1);
787 return;
790 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_REQUEST, type, NULL, 0);
793 void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
794 VDAgentMonitorsConfig *mon_config)
796 int i, num_sizes = 0;
797 int best = -1;
798 unsigned int closest_diff = -1;
799 XRRScreenSize* sizes;
800 XRRScreenConfiguration* config;
801 Rotation rotation;
803 if (!x11->has_xrandr)
804 return;
806 if (mon_config->num_of_monitors != 1) {
807 fprintf(x11->errfile,
808 "Only 1 monitor supported, ignoring monitor config\n");
809 return;
812 sizes = XRRSizes(x11->display, x11->screen, &num_sizes);
813 if (!sizes || !num_sizes) {
814 fprintf(x11->errfile, "XRRSizes failed\n");
815 return;
818 /* Find the closest size which will fit within the monitor */
819 for (i = 0; i < num_sizes; i++) {
820 if (sizes[i].width > mon_config->monitors[0].width ||
821 sizes[i].height > mon_config->monitors[0].height)
822 continue; /* Too large for the monitor */
824 unsigned int wdiff = mon_config->monitors[0].width - sizes[i].width;
825 unsigned int hdiff = mon_config->monitors[0].height - sizes[i].height;
826 unsigned int diff = wdiff * wdiff + hdiff * hdiff;
827 if (diff < closest_diff) {
828 closest_diff = diff;
829 best = i;
833 if (best == -1) {
834 fprintf(x11->errfile, "no suitable resolution found for monitor\n");
835 return;
838 config = XRRGetScreenInfo(x11->display, x11->root_window);
839 if(!config) {
840 fprintf(x11->errfile, "get screen info failed\n");
841 return;
843 XRRConfigCurrentConfiguration(config, &rotation);
844 XRRSetScreenConfig(x11->display, config, x11->root_window, best,
845 rotation, CurrentTime);
846 XRRFreeScreenConfigInfo(config);
847 XFlush(x11->display);
848 x11->width = sizes[best].width;
849 x11->height = sizes[best].height;
850 vdagent_x11_send_daemon_guest_xorg_res(x11);
853 void vdagent_x11_clipboard_request(struct vdagent_x11 *x11, uint32_t type)
855 Atom target;
857 if (x11->clipboard_owner != owner_guest) {
858 fprintf(x11->errfile,
859 "received clipboard req while not owning guest clipboard\n");
860 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
861 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
862 return;
865 target = vdagent_x11_type_to_target(x11, type);
866 if (target == None) {
867 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
868 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
869 return;
872 if (x11->clipboard_request_target) {
873 fprintf(x11->errfile,
874 "XConvertSelection request is already pending\n");
875 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
876 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
877 return;
879 x11->clipboard_request_target = target;
880 XConvertSelection(x11->display, x11->clipboard_atom, target,
881 x11->clipboard_atom, x11->selection_window, CurrentTime);
882 XFlush(x11->display);
885 void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint32_t *types,
886 uint32_t type_count)
888 if (type_count > sizeof(x11->clipboard_agent_types)/sizeof(uint32_t)) {
889 fprintf(x11->errfile, "x11_clipboard_grab: too many types\n");
890 type_count = sizeof(x11->clipboard_agent_types)/sizeof(uint32_t);
893 memcpy(x11->clipboard_agent_types, types, type_count * sizeof(uint32_t));
894 x11->clipboard_type_count = type_count;
896 XSetSelectionOwner(x11->display, x11->clipboard_atom,
897 x11->selection_window, CurrentTime);
898 XFlush(x11->display);
899 vdagent_x11_set_clipboard_owner(x11, owner_client);
902 void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint32_t type,
903 const uint8_t *data, uint32_t size)
905 Atom prop;
906 XEvent *event;
907 uint32_t type_from_event;
909 if (!x11->selection_request) {
910 fprintf(x11->errfile, "received clipboard data without an outstanding"
911 "selection request, ignoring\n");
912 return;
915 event = &x11->selection_request->event;
916 type_from_event = vdagent_x11_target_to_type(x11,
917 event->xselectionrequest.target);
918 if (type_from_event != type) {
919 fprintf(x11->errfile, "expecting type %u clipboard data got %u\n",
920 type_from_event, type);
921 vdagent_x11_send_selection_notify(x11, None, 1);
922 return;
925 prop = event->xselectionrequest.property;
926 if (prop == None)
927 prop = event->xselectionrequest.target;
929 /* FIXME: use INCR for large data transfers */
930 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
931 event->xselectionrequest.target, 8, PropModeReplace,
932 data, size);
933 vdagent_x11_send_selection_notify(x11, prop, 1);
936 void vdagent_x11_clipboard_release(struct vdagent_x11 *x11)
938 XEvent event;
940 if (x11->clipboard_owner != owner_client) {
941 fprintf(x11->errfile,
942 "received clipboard release while not owning client clipboard\n");
943 return;
946 XSetSelectionOwner(x11->display, x11->clipboard_atom, None, CurrentTime);
947 /* Make sure we process the XFixesSetSelectionOwnerNotify event caused
948 by this, so we don't end up changing the clipboard owner to none, after
949 it has already been re-owned because this event is still pending. */
950 XSync(x11->display, False);
951 while (XCheckTypedEvent(x11->display, x11->xfixes_event_base,
952 &event))
953 vdagent_x11_handle_event(x11, event);
955 /* Note no need to do a set_clipboard_owner(owner_none) here, as that is
956 already done by processing the XFixesSetSelectionOwnerNotify event. */