udscs: sync up callback disconnect handling
[vd_agent/hramrach.git] / vdagent-x11.c
blobfd4a72c83d07f8ec38748cce5f8bb28be3b484ba
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 <stdio.h>
28 #include <stdlib.h>
29 #include <limits.h>
30 #include <string.h>
31 #include <X11/Xatom.h>
32 #include <X11/Xlib.h>
33 #include <X11/extensions/Xrandr.h>
34 #include <X11/extensions/Xfixes.h>
35 #include "vdagentd-proto.h"
36 #include "vdagent-x11.h"
38 enum { owner_none, owner_guest, owner_client };
40 struct vdagent_x11_selection_request {
41 XEvent event;
42 struct vdagent_x11_selection_request *next;
45 struct clipboard_format_tmpl {
46 uint32_t type;
47 const char *atom_names[16];
50 struct clipboard_format_info {
51 uint32_t type;
52 Atom atoms[16];
53 int atom_count;
56 static const struct clipboard_format_tmpl clipboard_format_templates[] = {
57 { VD_AGENT_CLIPBOARD_UTF8_TEXT, { "UTF8_STRING",
58 "text/plain;charset=UTF-8", "text/plain;charset=utf-8", NULL }, },
59 { VD_AGENT_CLIPBOARD_IMAGE_PNG, { "image/png", NULL }, },
60 { VD_AGENT_CLIPBOARD_IMAGE_BMP, { "image/bmp", "image/x-bmp",
61 "image/x-MS-bmp", "image/x-win-bitmap", NULL }, },
62 { VD_AGENT_CLIPBOARD_IMAGE_TIFF, { "image/tiff", NULL }, },
63 { VD_AGENT_CLIPBOARD_IMAGE_JPG, { "image/jpeg", NULL }, },
66 #define clipboard_format_count (sizeof(clipboard_format_templates)/sizeof(clipboard_format_templates[0]))
68 struct vdagent_x11 {
69 struct clipboard_format_info clipboard_formats[clipboard_format_count];
70 Display *display;
71 Atom clipboard_atom;
72 Atom targets_atom;
73 Atom incr_atom;
74 Atom multiple_atom;
75 Window root_window;
76 Window selection_window;
77 struct udscs_connection *vdagentd;
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 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(stderr, "out of memory allocating vdagent_x11 struct\n");
119 return NULL;
122 x11->vdagentd = vdagentd;
123 x11->verbose = verbose;
125 x11->display = XOpenDisplay(NULL);
126 if (!x11->display) {
127 fprintf(stderr, "could not connect to X-server\n");
128 free(x11);
129 return NULL;
132 x11->screen = DefaultScreen(x11->display);
133 x11->root_window = RootWindow(x11->display, x11->screen);
134 x11->fd = ConnectionNumber(x11->display);
135 x11->clipboard_atom = XInternAtom(x11->display, "CLIPBOARD", False);
136 x11->targets_atom = XInternAtom(x11->display, "TARGETS", False);
137 x11->incr_atom = XInternAtom(x11->display, "INCR", False);
138 x11->multiple_atom = XInternAtom(x11->display, "MULTIPLE", False);
139 for(i = 0; i < clipboard_format_count; i++) {
140 x11->clipboard_formats[i].type = clipboard_format_templates[i].type;
141 for(j = 0; clipboard_format_templates[i].atom_names[j]; j++) {
142 x11->clipboard_formats[i].atoms[j] =
143 XInternAtom(x11->display,
144 clipboard_format_templates[i].atom_names[j],
145 False);
147 x11->clipboard_formats[i].atom_count = j;
150 /* We should not store properties (for selections) on the root window */
151 x11->selection_window = XCreateSimpleWindow(x11->display, x11->root_window,
152 0, 0, 1, 1, 0, 0, 0);
153 if (x11->verbose)
154 fprintf(stderr, "Selection window: %u\n",
155 (unsigned int)x11->selection_window);
157 if (XRRQueryExtension(x11->display, &i, &i))
158 x11->has_xrandr = 1;
159 else
160 fprintf(stderr, "no xrandr\n");
162 if (XFixesQueryExtension(x11->display, &x11->xfixes_event_base, &i) &&
163 XFixesQueryVersion(x11->display, &major, &minor) && major >= 1) {
164 x11->has_xfixes = 1;
165 XFixesSelectSelectionInput(x11->display, x11->root_window,
166 x11->clipboard_atom,
167 XFixesSetSelectionOwnerNotifyMask);
168 } else
169 fprintf(stderr, "no xfixes, no guest -> client copy paste support\n");
171 /* Catch resolution changes */
172 XSelectInput(x11->display, x11->root_window, StructureNotifyMask);
174 /* Get the current resolution */
175 XGetWindowAttributes(x11->display, x11->root_window, &attrib);
176 x11->width = attrib.width;
177 x11->height = attrib.height;
178 vdagent_x11_send_daemon_guest_xorg_res(x11);
180 /* No need for XFlush as XGetWindowAttributes does an implicit Xflush */
182 return x11;
185 void vdagent_x11_destroy(struct vdagent_x11 *x11)
187 if (!x11)
188 return;
190 XCloseDisplay(x11->display);
191 free(x11);
194 int vdagent_x11_get_fd(struct vdagent_x11 *x11)
196 return x11->fd;
199 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11,
200 int new_owner)
202 /* Clear pending requests and clipboard data */
203 if (x11->selection_request) {
204 fprintf(stderr,
205 "selection requests pending on clipboard ownership change, "
206 "clearing");
207 while (x11->selection_request)
208 vdagent_x11_send_selection_notify(x11, None, 0);
210 x11->clipboard_data_size = 0;
211 x11->clipboard_request_target = None;
212 x11->expect_property_notify = 0;
214 if (new_owner == owner_none) {
215 /* When going from owner_guest to owner_none we need to send a
216 clipboard release message to the client */
217 if (x11->clipboard_owner == owner_guest)
218 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_RELEASE, 0, NULL, 0);
220 x11->clipboard_type_count = 0;
222 x11->clipboard_owner = new_owner;
225 static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
227 int handled = 0;
230 if (event.type == x11->xfixes_event_base) {
231 union {
232 XEvent ev;
233 XFixesSelectionNotifyEvent xfev;
234 } ev;
236 ev.ev = event;
237 if (ev.xfev.subtype != XFixesSetSelectionOwnerNotify) {
238 if (x11->verbose)
239 fprintf(stderr, "unexpected xfix event subtype %d window %d\n",
240 (int)ev.xfev.subtype, (int)event.xany.window);
241 return;
244 if (x11->verbose)
245 fprintf(stderr, "New selection owner: %u\n",
246 (unsigned int)ev.xfev.owner);
248 /* Ignore becoming the owner ourselves */
249 if (ev.xfev.owner == x11->selection_window)
250 return;
252 /* If the clipboard owner is changed we no longer own it */
253 vdagent_x11_set_clipboard_owner(x11, owner_none);
255 if (ev.xfev.owner == None)
256 return;
258 /* Request the supported targets from the new owner */
259 XConvertSelection(x11->display, x11->clipboard_atom, x11->targets_atom,
260 x11->targets_atom, x11->selection_window,
261 CurrentTime);
262 XFlush(x11->display);
263 x11->expected_targets_notifies++;
264 return;
267 switch (event.type) {
268 case ConfigureNotify:
269 if (event.xconfigure.window != x11->root_window)
270 break;
272 handled = 1;
274 if (event.xconfigure.width == x11->width &&
275 event.xconfigure.height == x11->height)
276 break;
278 x11->width = event.xconfigure.width;
279 x11->height = event.xconfigure.height;
281 vdagent_x11_send_daemon_guest_xorg_res(x11);
282 break;
283 case SelectionNotify:
284 if (event.xselection.target == x11->targets_atom)
285 vdagent_x11_handle_targets_notify(x11, &event, 0);
286 else
287 vdagent_x11_handle_selection_notify(x11, &event, 0);
289 handled = 1;
290 break;
291 case PropertyNotify:
292 handled = 1;
293 if (!x11->expect_property_notify ||
294 event.xproperty.state != PropertyNewValue)
295 break;
297 if (event.xproperty.atom == x11->targets_atom)
298 vdagent_x11_handle_targets_notify(x11, &event, 1);
299 else
300 vdagent_x11_handle_selection_notify(x11, &event, 1);
301 break;
302 case SelectionClear:
303 /* Do nothing the clipboard ownership will get updated through
304 the XFixesSetSelectionOwnerNotify event */
305 handled = 1;
306 break;
307 case SelectionRequest: {
308 struct vdagent_x11_selection_request *req, *new_req;
310 new_req = malloc(sizeof(*new_req));
311 if (!new_req) {
312 fprintf(stderr, "out of memory on SelectionRequest, ignoring.\n");
313 break;
316 handled = 1;
318 new_req->event = event;
319 new_req->next = NULL;
321 if (!x11->selection_request) {
322 x11->selection_request = new_req;
323 vdagent_x11_handle_selection_request(x11);
324 break;
327 /* maybe we should limit the selection_request stack depth ? */
328 req = x11->selection_request;
329 while (req->next)
330 req = req->next;
332 req->next = new_req;
333 break;
336 if (!handled && x11->verbose)
337 fprintf(stderr, "unhandled x11 event, type %d, window %d\n",
338 (int)event.type, (int)event.xany.window);
341 void vdagent_x11_do_read(struct vdagent_x11 *x11)
343 XEvent event;
345 while (XPending(x11->display)) {
346 XNextEvent(x11->display, &event);
347 vdagent_x11_handle_event(x11, event);
351 static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11)
353 struct vdagentd_guest_xorg_resolution res;
355 res.width = x11->width;
356 res.height = x11->height;
358 udscs_write(x11->vdagentd, VDAGENTD_GUEST_XORG_RESOLUTION, 0,
359 (uint8_t *)&res, sizeof(res));
362 static const char *vdagent_x11_get_atom_name(struct vdagent_x11 *x11, Atom a)
364 if (a == None)
365 return "None";
367 return XGetAtomName(x11->display, a);
370 static int vdagent_x11_get_selection(struct vdagent_x11 *x11, XEvent *event,
371 Atom type, Atom prop, int format,
372 unsigned char **data_ret, int incr)
374 Bool del = incr ? True: False;
375 Atom type_ret;
376 int format_ret, ret_val = -1;
377 unsigned long len, remain;
378 unsigned char *data = NULL;
380 if (incr) {
381 if (event->xproperty.atom != prop) {
382 fprintf(stderr, "PropertyNotify parameters mismatch\n");
383 goto exit;
385 } else {
386 if (event->xselection.property == None) {
387 if (x11->verbose)
388 fprintf(stderr, "XConvertSelection refused by clipboard owner\n");
389 goto exit;
392 if (event->xselection.requestor != x11->selection_window ||
393 event->xselection.selection != x11->clipboard_atom ||
394 event->xselection.property != prop) {
395 fprintf(stderr, "SelectionNotify parameters mismatch\n");
396 goto exit;
400 if (XGetWindowProperty(x11->display, x11->selection_window, prop, 0,
401 LONG_MAX, del, type, &type_ret, &format_ret, &len,
402 &remain, &data) != Success) {
403 fprintf(stderr, "XGetWindowProperty failed\n");
404 goto exit;
407 if (!incr) {
408 if (type_ret == x11->incr_atom) {
409 int prop_min_size = *(uint32_t*)data;
411 if (x11->expect_property_notify) {
412 fprintf(stderr,
413 "received an incr property notify while "
414 "still reading another incr property\n");
415 goto exit;
418 if (x11->clipboard_data_space < prop_min_size) {
419 free(x11->clipboard_data);
420 x11->clipboard_data = malloc(prop_min_size);
421 if (!x11->clipboard_data) {
422 fprintf(stderr,
423 "out of memory allocating clipboard buffer\n");
424 x11->clipboard_data_space = 0;
425 goto exit;
427 x11->clipboard_data_space = prop_min_size;
429 x11->expect_property_notify = 1;
430 XSelectInput(x11->display, x11->selection_window,
431 PropertyChangeMask);
432 XDeleteProperty(x11->display, x11->selection_window, prop);
433 XFlush(x11->display);
434 XFree(data);
435 return 0; /* Wait for more data */
437 XDeleteProperty(x11->display, x11->selection_window, prop);
438 XFlush(x11->display);
441 if (type_ret != type) {
442 fprintf(stderr, "expected property type: %s, got: %s\n",
443 vdagent_x11_get_atom_name(x11, type),
444 vdagent_x11_get_atom_name(x11, type_ret));
445 goto exit;
448 if (format_ret != format) {
449 fprintf(stderr, "expected %d bit format, got %d bits\n", format,
450 format_ret);
451 goto exit;
454 /* Convert len to bytes */
455 switch(format) {
456 case 8:
457 break;
458 case 16:
459 len *= sizeof(short);
460 break;
461 case 32:
462 len *= sizeof(long);
463 break;
466 if (incr) {
467 if (len) {
468 if (x11->clipboard_data_size + len > x11->clipboard_data_space) {
469 void *old_clipboard_data = x11->clipboard_data;
471 x11->clipboard_data_space = x11->clipboard_data_size + len;
472 x11->clipboard_data = realloc(x11->clipboard_data,
473 x11->clipboard_data_space);
474 if (!x11->clipboard_data) {
475 fprintf(stderr,
476 "out of memory allocating clipboard buffer\n");
477 x11->clipboard_data_space = 0;
478 free(old_clipboard_data);
479 goto exit;
482 memcpy(x11->clipboard_data + x11->clipboard_data_size, data, len);
483 x11->clipboard_data_size += len;
484 if (x11->verbose)
485 fprintf(stderr, "Appended %ld bytes to buffer\n", len);
486 XFree(data);
487 return 0; /* Wait for more data */
489 len = x11->clipboard_data_size;
490 *data_ret = x11->clipboard_data;
491 } else
492 *data_ret = data;
494 if (len > 0)
495 ret_val = len;
496 else
497 fprintf(stderr, "property contains no data (zero length)\n");
499 exit:
500 if ((incr || ret_val == -1) && data)
501 XFree(data);
503 if (incr) {
504 x11->clipboard_data_size = 0;
505 x11->expect_property_notify = 0;
508 return ret_val;
511 static void vdagent_x11_get_selection_free(struct vdagent_x11 *x11,
512 unsigned char *data, int incr)
514 if (incr) {
515 /* If the clipboard has grown large return the memory to the system */
516 if (x11->clipboard_data_space > 512 * 1024) {
517 free(x11->clipboard_data);
518 x11->clipboard_data = NULL;
519 x11->clipboard_data_space = 0;
521 } else if (data)
522 XFree(data);
525 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11 *x11,
526 Atom target)
528 int i, j;
530 if (target == None)
531 return VD_AGENT_CLIPBOARD_NONE;
533 for (i = 0; i < clipboard_format_count; i++) {
534 for (j = 0; j < x11->clipboard_formats[i].atom_count; i++) {
535 if (x11->clipboard_formats[i].atoms[j] == target) {
536 return x11->clipboard_formats[i].type;
541 fprintf(stderr, "unexpected selection type %s\n",
542 vdagent_x11_get_atom_name(x11, target));
543 return VD_AGENT_CLIPBOARD_NONE;
546 static Atom vdagent_x11_type_to_target(struct vdagent_x11 *x11, uint32_t type)
548 int i;
550 for (i = 0; i < x11->clipboard_type_count; i++)
551 if (x11->clipboard_agent_types[i] == type)
552 return x11->clipboard_x11_targets[i];
554 fprintf(stderr, "client requested unavailable type %u\n", type);
555 return None;
558 static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
559 XEvent *event, int incr)
561 int len = -1;
562 unsigned char *data = NULL;
563 uint32_t type;
565 type = vdagent_x11_target_to_type(x11, x11->clipboard_request_target);
567 if (x11->clipboard_request_target == None)
568 fprintf(stderr, "SelectionNotify received without a target\n");
569 else if (!incr &&
570 event->xselection.target != x11->clipboard_request_target)
571 fprintf(stderr, "Requested %s target got %s\n",
572 vdagent_x11_get_atom_name(x11, x11->clipboard_request_target),
573 vdagent_x11_get_atom_name(x11, event->xselection.target));
574 else
575 len = vdagent_x11_get_selection(x11, event, x11->clipboard_request_target,
576 x11->clipboard_atom, 8, &data, incr);
577 if (len == 0) /* waiting for more data? */
578 return;
579 if (len == -1) {
580 type = VD_AGENT_CLIPBOARD_NONE;
581 len = 0;
584 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, type, data, len);
585 x11->clipboard_request_target = None;
586 vdagent_x11_get_selection_free(x11, data, incr);
589 static Atom atom_lists_overlap(Atom *atoms1, Atom *atoms2, int l1, int l2)
591 int i, j;
593 for (i = 0; i < l1; i++)
594 for (j = 0; j < l2; j++)
595 if (atoms1[i] == atoms2[j])
596 return atoms1[i];
598 return 0;
601 static void vdagent_x11_print_targets(struct vdagent_x11 *x11,
602 const char *action, Atom *atoms, int c)
604 int i;
606 if (!x11->verbose)
607 return;
609 fprintf(stderr, "%s %d targets:\n", action, c);
610 for (i = 0; i < c; i++)
611 fprintf(stderr, "%s\n", vdagent_x11_get_atom_name(x11, atoms[i]));
614 static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
615 XEvent *event, int incr)
617 int i, len;
618 Atom atom, *atoms = NULL;
620 if (!x11->expected_targets_notifies) {
621 fprintf(stderr, "unexpected selection notify TARGETS\n");
622 return;
625 x11->expected_targets_notifies--;
627 /* If we have more targets_notifies pending, ignore this one, we
628 are only interested in the targets list of the current owner
629 (which is the last one we've requested a targets list from) */
630 if (x11->expected_targets_notifies)
631 return;
633 len = vdagent_x11_get_selection(x11, event, XA_ATOM, x11->targets_atom, 32,
634 (unsigned char **)&atoms, incr);
635 if (len == 0 || len == -1) /* waiting for more data or error? */
636 return;
638 /* bytes -> atoms */
639 len /= sizeof(Atom);
640 vdagent_x11_print_targets(x11, "received", atoms, len);
642 x11->clipboard_type_count = 0;
643 for (i = 0; i < clipboard_format_count; i++) {
644 atom = atom_lists_overlap(x11->clipboard_formats[i].atoms, atoms,
645 x11->clipboard_formats[i].atom_count, len);
646 if (atom) {
647 x11->clipboard_agent_types[x11->clipboard_type_count] =
648 x11->clipboard_formats[i].type;
649 x11->clipboard_x11_targets[x11->clipboard_type_count] = atom;
650 x11->clipboard_type_count++;
651 if (x11->clipboard_type_count ==
652 sizeof(x11->clipboard_agent_types)/sizeof(uint32_t)) {
653 fprintf(stderr, "handle_targets_notify: too many types\n");
654 break;
659 if (x11->clipboard_type_count) {
660 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_GRAB, 0,
661 (uint8_t *)x11->clipboard_agent_types,
662 x11->clipboard_type_count * sizeof(uint32_t));
663 vdagent_x11_set_clipboard_owner(x11, owner_guest);
666 vdagent_x11_get_selection_free(x11, (unsigned char *)atoms, incr);
669 static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
670 Atom prop, int process_next_req)
672 XEvent res, *event = &x11->selection_request->event;
673 struct vdagent_x11_selection_request *selection_request;
675 res.xselection.property = prop;
676 res.xselection.type = SelectionNotify;
677 res.xselection.display = event->xselectionrequest.display;
678 res.xselection.requestor = event->xselectionrequest.requestor;
679 res.xselection.selection = event->xselectionrequest.selection;
680 res.xselection.target = event->xselectionrequest.target;
681 res.xselection.time = event->xselectionrequest.time;
682 XSendEvent(x11->display, event->xselectionrequest.requestor, 0, 0, &res);
683 XFlush(x11->display);
685 selection_request = x11->selection_request;
686 x11->selection_request = selection_request->next;
687 free(selection_request);
688 if (process_next_req)
689 vdagent_x11_handle_selection_request(x11);
692 static void vdagent_x11_send_targets(struct vdagent_x11 *x11, XEvent *event)
694 Atom prop, targets[256] = { x11->targets_atom, };
695 int i, j, k, target_count = 1;
697 for (i = 0; i < x11->clipboard_type_count; i++) {
698 for (j = 0; j < clipboard_format_count; j++) {
699 if (x11->clipboard_formats[j].type !=
700 x11->clipboard_agent_types[i])
701 continue;
703 for (k = 0; k < x11->clipboard_formats[j].atom_count; k++) {
704 targets[target_count] = x11->clipboard_formats[j].atoms[k];
705 target_count++;
706 if (target_count == sizeof(targets)/sizeof(Atom)) {
707 fprintf(stderr, "send_targets: too many targets\n");
708 goto exit_loop;
713 exit_loop:
715 prop = event->xselectionrequest.property;
716 if (prop == None)
717 prop = event->xselectionrequest.target;
719 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
720 XA_ATOM, 32, PropModeReplace, (unsigned char *)&targets,
721 target_count);
722 vdagent_x11_print_targets(x11, "sent", targets, target_count);
723 vdagent_x11_send_selection_notify(x11, prop, 1);
726 static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11)
728 XEvent *event;
729 uint32_t type = VD_AGENT_CLIPBOARD_NONE;
731 if (!x11->selection_request)
732 return;
734 event = &x11->selection_request->event;
736 if (x11->clipboard_owner != owner_client) {
737 fprintf(stderr,
738 "received selection request event for target %s, "
739 "while not owning client clipboard\n",
740 vdagent_x11_get_atom_name(x11, event->xselectionrequest.target));
741 vdagent_x11_send_selection_notify(x11, None, 1);
742 return;
745 if (event->xselectionrequest.target == x11->multiple_atom) {
746 fprintf(stderr, "multiple target not supported\n");
747 vdagent_x11_send_selection_notify(x11, None, 1);
748 return;
751 if (event->xselectionrequest.target == x11->targets_atom) {
752 vdagent_x11_send_targets(x11, event);
753 return;
756 type = vdagent_x11_target_to_type(x11, event->xselectionrequest.target);
757 if (type == VD_AGENT_CLIPBOARD_NONE) {
758 vdagent_x11_send_selection_notify(x11, None, 1);
759 return;
762 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_REQUEST, type, NULL, 0);
765 void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
766 VDAgentMonitorsConfig *mon_config)
768 int i, num_sizes = 0;
769 int best = -1;
770 unsigned int closest_diff = -1;
771 XRRScreenSize* sizes;
772 XRRScreenConfiguration* config;
773 Rotation rotation;
775 if (!x11->has_xrandr)
776 return;
778 if (mon_config->num_of_monitors != 1) {
779 fprintf(stderr, "Only 1 monitor supported, ignoring monitor config\n");
780 return;
783 sizes = XRRSizes(x11->display, x11->screen, &num_sizes);
784 if (!sizes || !num_sizes) {
785 fprintf(stderr, "XRRSizes failed\n");
786 return;
789 /* Find the closest size which will fit within the monitor */
790 for (i = 0; i < num_sizes; i++) {
791 if (sizes[i].width > mon_config->monitors[0].width ||
792 sizes[i].height > mon_config->monitors[0].height)
793 continue; /* Too large for the monitor */
795 unsigned int wdiff = mon_config->monitors[0].width - sizes[i].width;
796 unsigned int hdiff = mon_config->monitors[0].height - sizes[i].height;
797 unsigned int diff = wdiff * wdiff + hdiff * hdiff;
798 if (diff < closest_diff) {
799 closest_diff = diff;
800 best = i;
804 if (best == -1) {
805 fprintf(stderr, "no suitable resolution found for monitor\n");
806 return;
809 config = XRRGetScreenInfo(x11->display, x11->root_window);
810 if(!config) {
811 fprintf(stderr, "get screen info failed\n");
812 return;
814 XRRConfigCurrentConfiguration(config, &rotation);
815 XRRSetScreenConfig(x11->display, config, x11->root_window, best,
816 rotation, CurrentTime);
817 XRRFreeScreenConfigInfo(config);
818 XFlush(x11->display);
819 x11->width = sizes[best].width;
820 x11->height = sizes[best].height;
821 vdagent_x11_send_daemon_guest_xorg_res(x11);
824 void vdagent_x11_clipboard_request(struct vdagent_x11 *x11, uint32_t type)
826 Atom target;
828 if (x11->clipboard_owner != owner_guest) {
829 fprintf(stderr,
830 "received clipboard req while not owning guest clipboard\n");
831 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
832 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
833 return;
836 target = vdagent_x11_type_to_target(x11, type);
837 if (target == None) {
838 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
839 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
840 return;
843 if (x11->clipboard_request_target) {
844 fprintf(stderr, "XConvertSelection request is already pending\n");
845 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
846 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
847 return;
849 x11->clipboard_request_target = target;
850 XConvertSelection(x11->display, x11->clipboard_atom, target,
851 x11->clipboard_atom, x11->selection_window, CurrentTime);
852 XFlush(x11->display);
855 void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint32_t *types,
856 uint32_t type_count)
858 if (type_count > sizeof(x11->clipboard_agent_types)/sizeof(uint32_t)) {
859 fprintf(stderr, "x11_clipboard_grab: too many types\n");
860 type_count = sizeof(x11->clipboard_agent_types)/sizeof(uint32_t);
863 memcpy(x11->clipboard_agent_types, types, type_count * sizeof(uint32_t));
864 x11->clipboard_type_count = type_count;
866 XSetSelectionOwner(x11->display, x11->clipboard_atom,
867 x11->selection_window, CurrentTime);
868 XFlush(x11->display);
869 vdagent_x11_set_clipboard_owner(x11, owner_client);
872 void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint32_t type,
873 const uint8_t *data, uint32_t size)
875 Atom prop;
876 XEvent *event;
877 uint32_t type_from_event;
879 if (!x11->selection_request) {
880 fprintf(stderr, "received clipboard data without an outstanding"
881 "selection request, ignoring\n");
882 return;
885 event = &x11->selection_request->event;
886 type_from_event = vdagent_x11_target_to_type(x11,
887 event->xselectionrequest.target);
888 if (type_from_event != type) {
889 fprintf(stderr, "expecting type %u clipboard data got %u\n",
890 type_from_event, type);
891 vdagent_x11_send_selection_notify(x11, None, 1);
892 return;
895 prop = event->xselectionrequest.property;
896 if (prop == None)
897 prop = event->xselectionrequest.target;
899 /* FIXME: use INCR for large data transfers */
900 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
901 event->xselectionrequest.target, 8, PropModeReplace,
902 data, size);
903 vdagent_x11_send_selection_notify(x11, prop, 1);
906 void vdagent_x11_clipboard_release(struct vdagent_x11 *x11)
908 XEvent event;
910 if (x11->clipboard_owner != owner_client) {
911 fprintf(stderr,
912 "received clipboard release while not owning client clipboard\n");
913 return;
916 XSetSelectionOwner(x11->display, x11->clipboard_atom, None, CurrentTime);
917 /* Make sure we process the XFixesSetSelectionOwnerNotify event caused
918 by this, so we don't end up changing the clipboard owner to none, after
919 it has already been re-owned because this event is still pending. */
920 XSync(x11->display, False);
921 while (XCheckTypedEvent(x11->display, x11->xfixes_event_base,
922 &event))
923 vdagent_x11_handle_event(x11, event);
925 /* Note no need to do a set_clipboard_owner(owner_none) here, as that is
926 already done by processing the XFixesSetSelectionOwnerNotify event. */