Allow overriding CFLAGS
[vd_agent/hramrach.git] / vdagent-x11.c
blob7c3acb80b1364e7bb94a6af5db304c72f52127f5
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 x11->clipboard_data_size = 0;
215 x11->clipboard_request_target = None;
216 x11->expect_property_notify = 0;
218 if (new_owner == owner_none) {
219 /* When going from owner_guest to owner_none we need to send a
220 clipboard release message to the client */
221 if (x11->clipboard_owner == owner_guest)
222 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_RELEASE, 0, NULL, 0);
224 x11->clipboard_type_count = 0;
226 x11->clipboard_owner = new_owner;
229 static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
231 int handled = 0;
234 if (event.type == x11->xfixes_event_base) {
235 union {
236 XEvent ev;
237 XFixesSelectionNotifyEvent xfev;
238 } ev;
240 ev.ev = event;
241 switch (ev.xfev.subtype) {
242 case XFixesSetSelectionOwnerNotify:
243 break;
244 /* Treat ... as a SelectionOwnerNotify None */
245 case XFixesSelectionWindowDestroyNotify:
246 case XFixesSelectionClientCloseNotify:
247 ev.xfev.owner = None;
248 break;
249 default:
250 if (x11->verbose)
251 fprintf(x11->errfile,
252 "unexpected xfix event subtype %d window %d\n",
253 (int)ev.xfev.subtype, (int)event.xany.window);
254 return;
257 if (x11->verbose)
258 fprintf(x11->errfile, "New selection owner: %u\n",
259 (unsigned int)ev.xfev.owner);
261 /* Ignore becoming the owner ourselves */
262 if (ev.xfev.owner == x11->selection_window)
263 return;
265 /* If the clipboard owner is changed we no longer own it */
266 vdagent_x11_set_clipboard_owner(x11, owner_none);
268 if (ev.xfev.owner == None)
269 return;
271 /* Request the supported targets from the new owner */
272 XConvertSelection(x11->display, x11->clipboard_atom, x11->targets_atom,
273 x11->targets_atom, x11->selection_window,
274 CurrentTime);
275 XFlush(x11->display);
276 x11->expected_targets_notifies++;
277 return;
280 switch (event.type) {
281 case ConfigureNotify:
282 if (event.xconfigure.window != x11->root_window)
283 break;
285 handled = 1;
287 if (event.xconfigure.width == x11->width &&
288 event.xconfigure.height == x11->height)
289 break;
291 x11->width = event.xconfigure.width;
292 x11->height = event.xconfigure.height;
294 vdagent_x11_send_daemon_guest_xorg_res(x11);
295 break;
296 case SelectionNotify:
297 if (event.xselection.target == x11->targets_atom)
298 vdagent_x11_handle_targets_notify(x11, &event, 0);
299 else
300 vdagent_x11_handle_selection_notify(x11, &event, 0);
302 handled = 1;
303 break;
304 case PropertyNotify:
305 handled = 1;
306 if (!x11->expect_property_notify ||
307 event.xproperty.state != PropertyNewValue)
308 break;
310 if (event.xproperty.atom == x11->targets_atom)
311 vdagent_x11_handle_targets_notify(x11, &event, 1);
312 else
313 vdagent_x11_handle_selection_notify(x11, &event, 1);
314 break;
315 case SelectionClear:
316 /* Do nothing the clipboard ownership will get updated through
317 the XFixesSetSelectionOwnerNotify event */
318 handled = 1;
319 break;
320 case SelectionRequest: {
321 struct vdagent_x11_selection_request *req, *new_req;
323 new_req = malloc(sizeof(*new_req));
324 if (!new_req) {
325 fprintf(x11->errfile,
326 "out of memory on SelectionRequest, ignoring.\n");
327 break;
330 handled = 1;
332 new_req->event = event;
333 new_req->next = NULL;
335 if (!x11->selection_request) {
336 x11->selection_request = new_req;
337 vdagent_x11_handle_selection_request(x11);
338 break;
341 /* maybe we should limit the selection_request stack depth ? */
342 req = x11->selection_request;
343 while (req->next)
344 req = req->next;
346 req->next = new_req;
347 break;
350 if (!handled && x11->verbose)
351 fprintf(x11->errfile, "unhandled x11 event, type %d, window %d\n",
352 (int)event.type, (int)event.xany.window);
355 void vdagent_x11_do_read(struct vdagent_x11 *x11)
357 XEvent event;
359 while (XPending(x11->display)) {
360 XNextEvent(x11->display, &event);
361 vdagent_x11_handle_event(x11, event);
365 static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11)
367 struct vdagentd_guest_xorg_resolution res;
369 res.width = x11->width;
370 res.height = x11->height;
372 udscs_write(x11->vdagentd, VDAGENTD_GUEST_XORG_RESOLUTION, 0,
373 (uint8_t *)&res, sizeof(res));
376 static const char *vdagent_x11_get_atom_name(struct vdagent_x11 *x11, Atom a)
378 if (a == None)
379 return "None";
381 return XGetAtomName(x11->display, a);
384 static int vdagent_x11_get_selection(struct vdagent_x11 *x11, XEvent *event,
385 Atom type, Atom prop, int format,
386 unsigned char **data_ret, int incr)
388 Bool del = incr ? True: False;
389 Atom type_ret;
390 int format_ret, ret_val = -1;
391 unsigned long len, remain;
392 unsigned char *data = NULL;
394 *data_ret = NULL;
396 if (incr) {
397 if (event->xproperty.atom != prop) {
398 fprintf(x11->errfile, "PropertyNotify parameters mismatch\n");
399 goto exit;
401 } else {
402 if (event->xselection.property == None) {
403 if (x11->verbose)
404 fprintf(x11->errfile,
405 "XConvertSelection refused by clipboard owner\n");
406 goto exit;
409 if (event->xselection.requestor != x11->selection_window ||
410 event->xselection.selection != x11->clipboard_atom ||
411 event->xselection.property != prop) {
412 fprintf(x11->errfile, "SelectionNotify parameters mismatch\n");
413 goto exit;
417 if (XGetWindowProperty(x11->display, x11->selection_window, prop, 0,
418 LONG_MAX, del, type, &type_ret, &format_ret, &len,
419 &remain, &data) != Success) {
420 fprintf(x11->errfile, "XGetWindowProperty failed\n");
421 goto exit;
424 if (!incr) {
425 if (type_ret == x11->incr_atom) {
426 int prop_min_size = *(uint32_t*)data;
428 if (x11->expect_property_notify) {
429 fprintf(x11->errfile,
430 "received an incr property notify while "
431 "still reading another incr property\n");
432 goto exit;
435 if (x11->clipboard_data_space < prop_min_size) {
436 free(x11->clipboard_data);
437 x11->clipboard_data = malloc(prop_min_size);
438 if (!x11->clipboard_data) {
439 fprintf(x11->errfile,
440 "out of memory allocating clipboard buffer\n");
441 x11->clipboard_data_space = 0;
442 goto exit;
444 x11->clipboard_data_space = prop_min_size;
446 x11->expect_property_notify = 1;
447 XSelectInput(x11->display, x11->selection_window,
448 PropertyChangeMask);
449 XDeleteProperty(x11->display, x11->selection_window, prop);
450 XFlush(x11->display);
451 XFree(data);
452 return 0; /* Wait for more data */
454 XDeleteProperty(x11->display, x11->selection_window, prop);
455 XFlush(x11->display);
458 if (type_ret != type) {
459 fprintf(x11->errfile, "expected property type: %s, got: %s\n",
460 vdagent_x11_get_atom_name(x11, type),
461 vdagent_x11_get_atom_name(x11, type_ret));
462 goto exit;
465 if (format_ret != format) {
466 fprintf(x11->errfile, "expected %d bit format, got %d bits\n", format,
467 format_ret);
468 goto exit;
471 /* Convert len to bytes */
472 switch(format) {
473 case 8:
474 break;
475 case 16:
476 len *= sizeof(short);
477 break;
478 case 32:
479 len *= sizeof(long);
480 break;
483 if (incr) {
484 if (len) {
485 if (x11->clipboard_data_size + len > x11->clipboard_data_space) {
486 void *old_clipboard_data = x11->clipboard_data;
488 x11->clipboard_data_space = x11->clipboard_data_size + len;
489 x11->clipboard_data = realloc(x11->clipboard_data,
490 x11->clipboard_data_space);
491 if (!x11->clipboard_data) {
492 fprintf(x11->errfile,
493 "out of memory allocating clipboard buffer\n");
494 x11->clipboard_data_space = 0;
495 free(old_clipboard_data);
496 goto exit;
499 memcpy(x11->clipboard_data + x11->clipboard_data_size, data, len);
500 x11->clipboard_data_size += len;
501 if (x11->verbose)
502 fprintf(x11->errfile, "Appended %ld bytes to buffer\n", len);
503 XFree(data);
504 return 0; /* Wait for more data */
506 len = x11->clipboard_data_size;
507 *data_ret = x11->clipboard_data;
508 } else
509 *data_ret = data;
511 if (len > 0) {
512 ret_val = len;
513 } else {
514 fprintf(x11->errfile, "property contains no data (zero length)\n");
515 *data_ret = NULL;
518 exit:
519 if ((incr || ret_val == -1) && data)
520 XFree(data);
522 if (incr) {
523 x11->clipboard_data_size = 0;
524 x11->expect_property_notify = 0;
527 return ret_val;
530 static void vdagent_x11_get_selection_free(struct vdagent_x11 *x11,
531 unsigned char *data, int incr)
533 if (incr) {
534 /* If the clipboard has grown large return the memory to the system */
535 if (x11->clipboard_data_space > 512 * 1024) {
536 free(x11->clipboard_data);
537 x11->clipboard_data = NULL;
538 x11->clipboard_data_space = 0;
540 } else if (data)
541 XFree(data);
544 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11 *x11,
545 Atom target)
547 int i, j;
549 if (target == None)
550 return VD_AGENT_CLIPBOARD_NONE;
552 for (i = 0; i < clipboard_format_count; i++) {
553 for (j = 0; j < x11->clipboard_formats[i].atom_count; i++) {
554 if (x11->clipboard_formats[i].atoms[j] == target) {
555 return x11->clipboard_formats[i].type;
560 fprintf(x11->errfile, "unexpected selection type %s\n",
561 vdagent_x11_get_atom_name(x11, target));
562 return VD_AGENT_CLIPBOARD_NONE;
565 static Atom vdagent_x11_type_to_target(struct vdagent_x11 *x11, uint32_t type)
567 int i;
569 for (i = 0; i < x11->clipboard_type_count; i++)
570 if (x11->clipboard_agent_types[i] == type)
571 return x11->clipboard_x11_targets[i];
573 fprintf(x11->errfile, "client requested unavailable type %u\n", type);
574 return None;
577 static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
578 XEvent *event, int incr)
580 int len = -1;
581 unsigned char *data = NULL;
582 uint32_t type;
584 type = vdagent_x11_target_to_type(x11, x11->clipboard_request_target);
586 if (x11->clipboard_request_target == None)
587 fprintf(x11->errfile, "SelectionNotify received without a target\n");
588 else if (!incr &&
589 event->xselection.target != x11->clipboard_request_target)
590 fprintf(x11->errfile, "Requested %s target got %s\n",
591 vdagent_x11_get_atom_name(x11, x11->clipboard_request_target),
592 vdagent_x11_get_atom_name(x11, event->xselection.target));
593 else
594 len = vdagent_x11_get_selection(x11, event, x11->clipboard_request_target,
595 x11->clipboard_atom, 8, &data, incr);
596 if (len == 0) /* waiting for more data? */
597 return;
598 if (len == -1) {
599 type = VD_AGENT_CLIPBOARD_NONE;
600 len = 0;
603 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, type, data, len);
604 x11->clipboard_request_target = None;
605 vdagent_x11_get_selection_free(x11, data, incr);
608 static Atom atom_lists_overlap(Atom *atoms1, Atom *atoms2, int l1, int l2)
610 int i, j;
612 for (i = 0; i < l1; i++)
613 for (j = 0; j < l2; j++)
614 if (atoms1[i] == atoms2[j])
615 return atoms1[i];
617 return 0;
620 static void vdagent_x11_print_targets(struct vdagent_x11 *x11,
621 const char *action, Atom *atoms, int c)
623 int i;
625 if (!x11->verbose)
626 return;
628 fprintf(x11->errfile, "%s %d targets:\n", action, c);
629 for (i = 0; i < c; i++)
630 fprintf(x11->errfile, "%s\n",
631 vdagent_x11_get_atom_name(x11, atoms[i]));
634 static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
635 XEvent *event, int incr)
637 int i, len;
638 Atom atom, *atoms = NULL;
640 if (!x11->expected_targets_notifies) {
641 fprintf(x11->errfile, "unexpected selection notify TARGETS\n");
642 return;
645 x11->expected_targets_notifies--;
647 /* If we have more targets_notifies pending, ignore this one, we
648 are only interested in the targets list of the current owner
649 (which is the last one we've requested a targets list from) */
650 if (x11->expected_targets_notifies)
651 return;
653 len = vdagent_x11_get_selection(x11, event, XA_ATOM, x11->targets_atom, 32,
654 (unsigned char **)&atoms, incr);
655 if (len == 0 || len == -1) /* waiting for more data or error? */
656 return;
658 /* bytes -> atoms */
659 len /= sizeof(Atom);
660 vdagent_x11_print_targets(x11, "received", atoms, len);
662 x11->clipboard_type_count = 0;
663 for (i = 0; i < clipboard_format_count; i++) {
664 atom = atom_lists_overlap(x11->clipboard_formats[i].atoms, atoms,
665 x11->clipboard_formats[i].atom_count, len);
666 if (atom) {
667 x11->clipboard_agent_types[x11->clipboard_type_count] =
668 x11->clipboard_formats[i].type;
669 x11->clipboard_x11_targets[x11->clipboard_type_count] = atom;
670 x11->clipboard_type_count++;
671 if (x11->clipboard_type_count ==
672 sizeof(x11->clipboard_agent_types)/sizeof(uint32_t)) {
673 fprintf(x11->errfile,
674 "handle_targets_notify: too many types\n");
675 break;
680 if (x11->clipboard_type_count) {
681 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_GRAB, 0,
682 (uint8_t *)x11->clipboard_agent_types,
683 x11->clipboard_type_count * sizeof(uint32_t));
684 vdagent_x11_set_clipboard_owner(x11, owner_guest);
687 vdagent_x11_get_selection_free(x11, (unsigned char *)atoms, incr);
690 static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
691 Atom prop, int process_next_req)
693 XEvent res, *event = &x11->selection_request->event;
694 struct vdagent_x11_selection_request *selection_request;
696 res.xselection.property = prop;
697 res.xselection.type = SelectionNotify;
698 res.xselection.display = event->xselectionrequest.display;
699 res.xselection.requestor = event->xselectionrequest.requestor;
700 res.xselection.selection = event->xselectionrequest.selection;
701 res.xselection.target = event->xselectionrequest.target;
702 res.xselection.time = event->xselectionrequest.time;
703 XSendEvent(x11->display, event->xselectionrequest.requestor, 0, 0, &res);
704 XFlush(x11->display);
706 selection_request = x11->selection_request;
707 x11->selection_request = selection_request->next;
708 free(selection_request);
709 if (process_next_req)
710 vdagent_x11_handle_selection_request(x11);
713 static void vdagent_x11_send_targets(struct vdagent_x11 *x11, XEvent *event)
715 Atom prop, targets[256] = { x11->targets_atom, };
716 int i, j, k, target_count = 1;
718 for (i = 0; i < x11->clipboard_type_count; i++) {
719 for (j = 0; j < clipboard_format_count; j++) {
720 if (x11->clipboard_formats[j].type !=
721 x11->clipboard_agent_types[i])
722 continue;
724 for (k = 0; k < x11->clipboard_formats[j].atom_count; k++) {
725 targets[target_count] = x11->clipboard_formats[j].atoms[k];
726 target_count++;
727 if (target_count == sizeof(targets)/sizeof(Atom)) {
728 fprintf(x11->errfile, "send_targets: too many targets\n");
729 goto exit_loop;
734 exit_loop:
736 prop = event->xselectionrequest.property;
737 if (prop == None)
738 prop = event->xselectionrequest.target;
740 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
741 XA_ATOM, 32, PropModeReplace, (unsigned char *)&targets,
742 target_count);
743 vdagent_x11_print_targets(x11, "sent", targets, target_count);
744 vdagent_x11_send_selection_notify(x11, prop, 1);
747 static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11)
749 XEvent *event;
750 uint32_t type = VD_AGENT_CLIPBOARD_NONE;
752 if (!x11->selection_request)
753 return;
755 event = &x11->selection_request->event;
757 if (x11->clipboard_owner != owner_client) {
758 fprintf(x11->errfile,
759 "received selection request event for target %s, "
760 "while not owning client clipboard\n",
761 vdagent_x11_get_atom_name(x11, event->xselectionrequest.target));
762 vdagent_x11_send_selection_notify(x11, None, 1);
763 return;
766 if (event->xselectionrequest.target == x11->multiple_atom) {
767 fprintf(x11->errfile, "multiple target not supported\n");
768 vdagent_x11_send_selection_notify(x11, None, 1);
769 return;
772 if (event->xselectionrequest.target == x11->targets_atom) {
773 vdagent_x11_send_targets(x11, event);
774 return;
777 type = vdagent_x11_target_to_type(x11, event->xselectionrequest.target);
778 if (type == VD_AGENT_CLIPBOARD_NONE) {
779 vdagent_x11_send_selection_notify(x11, None, 1);
780 return;
783 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_REQUEST, type, NULL, 0);
786 void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
787 VDAgentMonitorsConfig *mon_config)
789 int i, num_sizes = 0;
790 int best = -1;
791 unsigned int closest_diff = -1;
792 XRRScreenSize* sizes;
793 XRRScreenConfiguration* config;
794 Rotation rotation;
796 if (!x11->has_xrandr)
797 return;
799 if (mon_config->num_of_monitors != 1) {
800 fprintf(x11->errfile,
801 "Only 1 monitor supported, ignoring monitor config\n");
802 return;
805 sizes = XRRSizes(x11->display, x11->screen, &num_sizes);
806 if (!sizes || !num_sizes) {
807 fprintf(x11->errfile, "XRRSizes failed\n");
808 return;
811 /* Find the closest size which will fit within the monitor */
812 for (i = 0; i < num_sizes; i++) {
813 if (sizes[i].width > mon_config->monitors[0].width ||
814 sizes[i].height > mon_config->monitors[0].height)
815 continue; /* Too large for the monitor */
817 unsigned int wdiff = mon_config->monitors[0].width - sizes[i].width;
818 unsigned int hdiff = mon_config->monitors[0].height - sizes[i].height;
819 unsigned int diff = wdiff * wdiff + hdiff * hdiff;
820 if (diff < closest_diff) {
821 closest_diff = diff;
822 best = i;
826 if (best == -1) {
827 fprintf(x11->errfile, "no suitable resolution found for monitor\n");
828 return;
831 config = XRRGetScreenInfo(x11->display, x11->root_window);
832 if(!config) {
833 fprintf(x11->errfile, "get screen info failed\n");
834 return;
836 XRRConfigCurrentConfiguration(config, &rotation);
837 XRRSetScreenConfig(x11->display, config, x11->root_window, best,
838 rotation, CurrentTime);
839 XRRFreeScreenConfigInfo(config);
840 XFlush(x11->display);
841 x11->width = sizes[best].width;
842 x11->height = sizes[best].height;
843 vdagent_x11_send_daemon_guest_xorg_res(x11);
846 void vdagent_x11_clipboard_request(struct vdagent_x11 *x11, uint32_t type)
848 Atom target;
850 if (x11->clipboard_owner != owner_guest) {
851 fprintf(x11->errfile,
852 "received clipboard req while not owning guest clipboard\n");
853 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
854 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
855 return;
858 target = vdagent_x11_type_to_target(x11, type);
859 if (target == None) {
860 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
861 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
862 return;
865 if (x11->clipboard_request_target) {
866 fprintf(x11->errfile,
867 "XConvertSelection request is already pending\n");
868 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
869 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
870 return;
872 x11->clipboard_request_target = target;
873 XConvertSelection(x11->display, x11->clipboard_atom, target,
874 x11->clipboard_atom, x11->selection_window, CurrentTime);
875 XFlush(x11->display);
878 void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint32_t *types,
879 uint32_t type_count)
881 if (type_count > sizeof(x11->clipboard_agent_types)/sizeof(uint32_t)) {
882 fprintf(x11->errfile, "x11_clipboard_grab: too many types\n");
883 type_count = sizeof(x11->clipboard_agent_types)/sizeof(uint32_t);
886 memcpy(x11->clipboard_agent_types, types, type_count * sizeof(uint32_t));
887 x11->clipboard_type_count = type_count;
889 XSetSelectionOwner(x11->display, x11->clipboard_atom,
890 x11->selection_window, CurrentTime);
891 XFlush(x11->display);
892 vdagent_x11_set_clipboard_owner(x11, owner_client);
895 void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint32_t type,
896 const uint8_t *data, uint32_t size)
898 Atom prop;
899 XEvent *event;
900 uint32_t type_from_event;
902 if (!x11->selection_request) {
903 fprintf(x11->errfile, "received clipboard data without an outstanding"
904 "selection request, ignoring\n");
905 return;
908 event = &x11->selection_request->event;
909 type_from_event = vdagent_x11_target_to_type(x11,
910 event->xselectionrequest.target);
911 if (type_from_event != type) {
912 fprintf(x11->errfile, "expecting type %u clipboard data got %u\n",
913 type_from_event, type);
914 vdagent_x11_send_selection_notify(x11, None, 1);
915 return;
918 prop = event->xselectionrequest.property;
919 if (prop == None)
920 prop = event->xselectionrequest.target;
922 /* FIXME: use INCR for large data transfers */
923 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
924 event->xselectionrequest.target, 8, PropModeReplace,
925 data, size);
926 vdagent_x11_send_selection_notify(x11, prop, 1);
929 void vdagent_x11_clipboard_release(struct vdagent_x11 *x11)
931 XEvent event;
933 if (x11->clipboard_owner != owner_client) {
934 fprintf(x11->errfile,
935 "received clipboard release while not owning client clipboard\n");
936 return;
939 XSetSelectionOwner(x11->display, x11->clipboard_atom, None, CurrentTime);
940 /* Make sure we process the XFixesSetSelectionOwnerNotify event caused
941 by this, so we don't end up changing the clipboard owner to none, after
942 it has already been re-owned because this event is still pending. */
943 XSync(x11->display, False);
944 while (XCheckTypedEvent(x11->display, x11->xfixes_event_base,
945 &event))
946 vdagent_x11_handle_event(x11, event);
948 /* Note no need to do a set_clipboard_owner(owner_none) here, as that is
949 already done by processing the XFixesSetSelectionOwnerNotify event. */