Add support for receiving incr. properties
[vd_agent/hramrach.git] / vdagent-x11.c
blob00d1886e7cd3c45d906c40258429167d9cde2c77
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 const char *utf8_atom_names[] = {
41 "UTF8_STRING",
42 "text/plain;charset=UTF-8",
43 "text/plain;charset=utf-8",
46 #define utf8_atom_count (sizeof(utf8_atom_names)/sizeof(utf8_atom_names[0]))
48 struct vdagent_x11_selection_request {
49 XEvent event;
50 struct vdagent_x11_selection_request *next;
53 struct vdagent_x11 {
54 Display *display;
55 Atom clipboard_atom;
56 Atom targets_atom;
57 Atom incr_atom;
58 Atom multiple_atom;
59 Atom utf8_atoms[utf8_atom_count];
60 Window root_window;
61 Window selection_window;
62 struct udscs_connection *vdagentd;
63 int verbose;
64 int fd;
65 int screen;
66 int width;
67 int height;
68 int has_xrandr;
69 int has_xfixes;
70 int xfixes_event_base;
71 int expected_targets_notifies;
72 int expect_property_notify;
73 int clipboard_owner;
74 Atom clipboard_request_target;
75 int clipboard_type_count;
76 /* TODO Add support for more types here */
77 /* Warning the size of these needs to be increased each time we add
78 support for a new type!! */
79 uint32_t clipboard_agent_types[1];
80 Atom clipboard_x11_targets[1];
81 uint8_t *clipboard_data;
82 uint32_t clipboard_data_size;
83 uint32_t clipboard_data_space;
84 struct vdagent_x11_selection_request *selection_request;
87 static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11);
88 static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
89 XEvent *event, int incr);
90 static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11);
91 static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
92 XEvent *event, int incr);
93 static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
94 Atom prop,
95 int process_next_req);
97 struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd,
98 int verbose)
100 struct vdagent_x11 *x11;
101 XWindowAttributes attrib;
102 int i, major, minor;
104 x11 = calloc(1, sizeof(*x11));
105 if (!x11) {
106 fprintf(stderr, "out of memory allocating vdagent_x11 struct\n");
107 return NULL;
110 x11->vdagentd = vdagentd;
111 x11->verbose = verbose;
113 x11->display = XOpenDisplay(NULL);
114 if (!x11->display) {
115 fprintf(stderr, "could not connect to X-server\n");
116 free(x11);
117 return NULL;
120 x11->screen = DefaultScreen(x11->display);
121 x11->root_window = RootWindow(x11->display, x11->screen);
122 x11->fd = ConnectionNumber(x11->display);
123 x11->clipboard_atom = XInternAtom(x11->display, "CLIPBOARD", False);
124 x11->targets_atom = XInternAtom(x11->display, "TARGETS", False);
125 x11->incr_atom = XInternAtom(x11->display, "INCR", False);
126 x11->multiple_atom = XInternAtom(x11->display, "MULTIPLE", False);
127 for(i = 0; i < utf8_atom_count; i++)
128 x11->utf8_atoms[i] = XInternAtom(x11->display, utf8_atom_names[i],
129 False);
131 /* We should not store properties (for selections) on the root window */
132 x11->selection_window = XCreateSimpleWindow(x11->display, x11->root_window,
133 0, 0, 1, 1, 0, 0, 0);
134 if (x11->verbose)
135 fprintf(stderr, "Selection window: %u\n",
136 (unsigned int)x11->selection_window);
138 if (XRRQueryExtension(x11->display, &i, &i))
139 x11->has_xrandr = 1;
140 else
141 fprintf(stderr, "no xrandr\n");
143 if (XFixesQueryExtension(x11->display, &x11->xfixes_event_base, &i) &&
144 XFixesQueryVersion(x11->display, &major, &minor) && major >= 1) {
145 x11->has_xfixes = 1;
146 XFixesSelectSelectionInput(x11->display, x11->root_window,
147 x11->clipboard_atom,
148 XFixesSetSelectionOwnerNotifyMask);
149 } else
150 fprintf(stderr, "no xfixes, no guest -> client copy paste support\n");
152 /* Catch resolution changes */
153 XSelectInput(x11->display, x11->root_window, StructureNotifyMask);
155 /* Get the current resolution */
156 XGetWindowAttributes(x11->display, x11->root_window, &attrib);
157 x11->width = attrib.width;
158 x11->height = attrib.height;
159 vdagent_x11_send_daemon_guest_xorg_res(x11);
161 /* No need for XFlush as XGetWindowAttributes does an implicit Xflush */
163 return x11;
166 void vdagent_x11_destroy(struct vdagent_x11 *x11)
168 if (!x11)
169 return;
171 XCloseDisplay(x11->display);
172 free(x11);
175 int vdagent_x11_get_fd(struct vdagent_x11 *x11)
177 return x11->fd;
180 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11,
181 int new_owner)
183 /* Clear pending requests and clipboard data */
184 if (x11->selection_request) {
185 fprintf(stderr,
186 "selection requests pending on clipboard ownership change, "
187 "clearing");
188 while (x11->selection_request)
189 vdagent_x11_send_selection_notify(x11, None, 0);
191 x11->clipboard_data_size = 0;
192 x11->clipboard_request_target = None;
193 x11->expect_property_notify = 0;
195 if (new_owner == owner_none) {
196 /* When going from owner_guest to owner_none we need to send a
197 clipboard release message to the client */
198 if (x11->clipboard_owner == owner_guest)
199 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_RELEASE, 0, NULL, 0);
201 x11->clipboard_type_count = 0;
203 x11->clipboard_owner = new_owner;
206 static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
208 int handled = 0;
211 if (event.type == x11->xfixes_event_base) {
212 union {
213 XEvent ev;
214 XFixesSelectionNotifyEvent xfev;
215 } ev;
217 ev.ev = event;
218 if (ev.xfev.subtype != XFixesSetSelectionOwnerNotify) {
219 if (x11->verbose)
220 fprintf(stderr, "unexpected xfix event subtype %d window %d\n",
221 (int)ev.xfev.subtype, (int)event.xany.window);
222 return;
225 if (x11->verbose)
226 fprintf(stderr, "New selection owner: %u\n",
227 (unsigned int)ev.xfev.owner);
229 /* Ignore becoming the owner ourselves */
230 if (ev.xfev.owner == x11->selection_window)
231 return;
233 /* If the clipboard owner is changed we no longer own it */
234 vdagent_x11_set_clipboard_owner(x11, owner_none);
236 if (ev.xfev.owner == None)
237 return;
239 /* Request the supported targets from the new owner */
240 XConvertSelection(x11->display, x11->clipboard_atom, x11->targets_atom,
241 x11->targets_atom, x11->selection_window,
242 CurrentTime);
243 XFlush(x11->display);
244 x11->expected_targets_notifies++;
245 return;
248 switch (event.type) {
249 case ConfigureNotify:
250 if (event.xconfigure.window != x11->root_window)
251 break;
253 handled = 1;
255 if (event.xconfigure.width == x11->width &&
256 event.xconfigure.height == x11->height)
257 break;
259 x11->width = event.xconfigure.width;
260 x11->height = event.xconfigure.height;
262 vdagent_x11_send_daemon_guest_xorg_res(x11);
263 break;
264 case SelectionNotify:
265 if (event.xselection.target == x11->targets_atom)
266 vdagent_x11_handle_targets_notify(x11, &event, 0);
267 else
268 vdagent_x11_handle_selection_notify(x11, &event, 0);
270 handled = 1;
271 break;
272 case PropertyNotify:
273 handled = 1;
274 if (!x11->expect_property_notify ||
275 event.xproperty.state != PropertyNewValue)
276 break;
278 if (event.xproperty.atom == x11->targets_atom)
279 vdagent_x11_handle_targets_notify(x11, &event, 1);
280 else
281 vdagent_x11_handle_selection_notify(x11, &event, 1);
282 break;
283 case SelectionClear:
284 /* Do nothing the clipboard ownership will get updated through
285 the XFixesSetSelectionOwnerNotify event */
286 handled = 1;
287 break;
288 case SelectionRequest: {
289 struct vdagent_x11_selection_request *req, *new_req;
291 new_req = malloc(sizeof(*new_req));
292 if (!new_req) {
293 fprintf(stderr, "out of memory on SelectionRequest, ignoring.\n");
294 break;
297 handled = 1;
299 new_req->event = event;
300 new_req->next = NULL;
302 if (!x11->selection_request) {
303 x11->selection_request = new_req;
304 vdagent_x11_handle_selection_request(x11);
305 break;
308 /* maybe we should limit the selection_request stack depth ? */
309 req = x11->selection_request;
310 while (req->next)
311 req = req->next;
313 req->next = new_req;
314 break;
317 if (!handled && x11->verbose)
318 fprintf(stderr, "unhandled x11 event, type %d, window %d\n",
319 (int)event.type, (int)event.xany.window);
322 void vdagent_x11_do_read(struct vdagent_x11 *x11)
324 XEvent event;
326 while (XPending(x11->display)) {
327 XNextEvent(x11->display, &event);
328 vdagent_x11_handle_event(x11, event);
332 static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11)
334 struct vdagentd_guest_xorg_resolution res;
336 res.width = x11->width;
337 res.height = x11->height;
339 udscs_write(x11->vdagentd, VDAGENTD_GUEST_XORG_RESOLUTION, 0,
340 (uint8_t *)&res, sizeof(res));
343 static const char *vdagent_x11_get_atom_name(struct vdagent_x11 *x11, Atom a)
345 if (a == None)
346 return "None";
348 return XGetAtomName(x11->display, a);
351 static int vdagent_x11_get_selection(struct vdagent_x11 *x11, XEvent *event,
352 Atom type, Atom prop, int format,
353 unsigned char **data_ret, int incr)
355 Bool del = incr ? True: False;
356 Atom type_ret;
357 int format_ret, ret_val = -1;
358 unsigned long len, remain;
359 unsigned char *data = NULL;
361 if (incr) {
362 if (event->xproperty.atom != prop) {
363 fprintf(stderr, "PropertyNotify parameters mismatch\n");
364 goto exit;
366 } else {
367 if (event->xselection.property == None) {
368 if (x11->verbose)
369 fprintf(stderr, "XConvertSelection refused by clipboard owner\n");
370 goto exit;
373 if (event->xselection.requestor != x11->selection_window ||
374 event->xselection.selection != x11->clipboard_atom ||
375 event->xselection.property != prop) {
376 fprintf(stderr, "SelectionNotify parameters mismatch\n");
377 goto exit;
381 /* FIXME when we've incr support we should not immediately
382 delete the property (as we need to first register for
383 property change events) */
384 if (XGetWindowProperty(x11->display, x11->selection_window, prop, 0,
385 LONG_MAX, del, type, &type_ret, &format_ret, &len,
386 &remain, &data) != Success) {
387 fprintf(stderr, "XGetWindowProperty failed\n");
388 goto exit;
391 if (!incr) {
392 if (type_ret == x11->incr_atom) {
393 int prop_min_size = *(uint32_t*)data;
395 if (x11->expect_property_notify) {
396 fprintf(stderr,
397 "received an incr property notify while "
398 "still reading another incr property\n");
399 goto exit;
402 if (x11->clipboard_data_space < prop_min_size) {
403 free(x11->clipboard_data);
404 x11->clipboard_data = malloc(prop_min_size);
405 if (!x11->clipboard_data) {
406 fprintf(stderr,
407 "out of memory allocating clipboard buffer\n");
408 x11->clipboard_data_space = 0;
409 goto exit;
411 x11->clipboard_data_space = prop_min_size;
413 x11->expect_property_notify = 1;
414 XSelectInput(x11->display, x11->selection_window,
415 PropertyChangeMask);
416 XDeleteProperty(x11->display, x11->selection_window, prop);
417 XFlush(x11->display);
418 XFree(data);
419 return 0; /* Wait for more data */
421 XDeleteProperty(x11->display, x11->selection_window, prop);
422 XFlush(x11->display);
425 if (type_ret != type) {
426 fprintf(stderr, "expected property type: %s, got: %s\n",
427 vdagent_x11_get_atom_name(x11, type),
428 vdagent_x11_get_atom_name(x11, type_ret));
429 goto exit;
432 if (format_ret != format) {
433 fprintf(stderr, "expected %d bit format, got %d bits\n", format,
434 format_ret);
435 goto exit;
438 /* Convert len to bytes */
439 switch(format) {
440 case 8:
441 break;
442 case 16:
443 len *= sizeof(short);
444 break;
445 case 32:
446 len *= sizeof(long);
447 break;
450 if (incr) {
451 if (len) {
452 if (x11->clipboard_data_size + len > x11->clipboard_data_space) {
453 void *old_clipboard_data = x11->clipboard_data;
455 x11->clipboard_data_space = x11->clipboard_data_size + len;
456 x11->clipboard_data = realloc(x11->clipboard_data,
457 x11->clipboard_data_space);
458 if (!x11->clipboard_data) {
459 fprintf(stderr,
460 "out of memory allocating clipboard buffer\n");
461 x11->clipboard_data_space = 0;
462 free(old_clipboard_data);
463 goto exit;
466 memcpy(x11->clipboard_data + x11->clipboard_data_size, data, len);
467 x11->clipboard_data_size += len;
468 if (x11->verbose)
469 fprintf(stderr, "Appended %ld bytes to buffer\n", len);
470 XFree(data);
471 return 0; /* Wait for more data */
473 len = x11->clipboard_data_size;
474 *data_ret = x11->clipboard_data;
475 } else
476 *data_ret = data;
478 if (len > 0)
479 ret_val = len;
480 else
481 fprintf(stderr, "property contains no data (zero length)\n");
483 exit:
484 if ((incr || ret_val == -1) && data)
485 XFree(data);
487 if (incr) {
488 x11->clipboard_data_size = 0;
489 x11->expect_property_notify = 0;
492 return ret_val;
495 static void vdagent_x11_get_selection_free(struct vdagent_x11 *x11,
496 unsigned char *data, int incr)
498 if (incr) {
499 /* If the clipboard has grown large return the memory to the system */
500 if (x11->clipboard_data_space > 512 * 1024) {
501 free(x11->clipboard_data);
502 x11->clipboard_data = NULL;
503 x11->clipboard_data_space = 0;
505 } else if (data)
506 XFree(data);
509 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11 *x11,
510 Atom target)
512 int i;
514 if (target == None)
515 return VD_AGENT_CLIPBOARD_NONE;
517 for (i = 0; i < utf8_atom_count; i++)
518 if (x11->utf8_atoms[i] == target)
519 return VD_AGENT_CLIPBOARD_UTF8_TEXT;
521 /* TODO Add support for more types here */
523 fprintf(stderr, "unexpected selection type %s\n",
524 vdagent_x11_get_atom_name(x11, target));
525 return VD_AGENT_CLIPBOARD_NONE;
528 static Atom vdagent_x11_type_to_target(struct vdagent_x11 *x11, uint32_t type)
530 int i;
532 for (i = 0; i < x11->clipboard_type_count; i++)
533 if (x11->clipboard_agent_types[i] == type)
534 return x11->clipboard_x11_targets[i];
536 fprintf(stderr, "client requested unavailable type %u\n", type);
537 return None;
540 static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
541 XEvent *event, int incr)
543 int len = -1;
544 unsigned char *data = NULL;
545 uint32_t type;
547 type = vdagent_x11_target_to_type(x11, x11->clipboard_request_target);
549 if (x11->clipboard_request_target == None)
550 fprintf(stderr, "SelectionNotify received without a target\n");
551 else if (!incr &&
552 event->xselection.target != x11->clipboard_request_target)
553 fprintf(stderr, "Requested %s target got %s\n",
554 vdagent_x11_get_atom_name(x11, x11->clipboard_request_target),
555 vdagent_x11_get_atom_name(x11, event->xselection.target));
556 else
557 len = vdagent_x11_get_selection(x11, event, event->xselection.target,
558 x11->clipboard_atom, 8, &data, incr);
559 if (len == 0) /* waiting for more data? */
560 return;
561 if (len == -1) {
562 type = VD_AGENT_CLIPBOARD_NONE;
563 len = 0;
566 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, type, data, len);
567 x11->clipboard_request_target = None;
568 vdagent_x11_get_selection_free(x11, data, incr);
571 static Atom atom_lists_overlap(Atom *atoms1, Atom *atoms2, int l1, int l2)
573 int i, j;
575 for (i = 0; i < l1; i++)
576 for (j = 0; j < l2; j++)
577 if (atoms1[i] == atoms2[j])
578 return atoms1[i];
580 return 0;
583 static void vdagent_x11_print_targets(struct vdagent_x11 *x11,
584 const char *action, Atom *atoms, int c)
586 int i;
588 if (!x11->verbose)
589 return;
591 fprintf(stderr, "%s %d targets:\n", action, c);
592 for (i = 0; i < c; i++)
593 fprintf(stderr, "%s\n", vdagent_x11_get_atom_name(x11, atoms[i]));
596 static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
597 XEvent *event, int incr)
599 int len;
600 Atom atom, *atoms = NULL;
602 if (!x11->expected_targets_notifies) {
603 fprintf(stderr, "unexpected selection notify TARGETS\n");
604 return;
607 x11->expected_targets_notifies--;
609 /* If we have more targets_notifies pending, ignore this one, we
610 are only interested in the targets list of the current owner
611 (which is the last one we've requested a targets list from) */
612 if (x11->expected_targets_notifies)
613 return;
615 len = vdagent_x11_get_selection(x11, event, XA_ATOM, x11->targets_atom, 32,
616 (unsigned char **)&atoms, incr);
617 if (len == 0 || len == -1) /* waiting for more data or error? */
618 return;
620 /* bytes -> atoms */
621 len /= sizeof(Atom);
622 vdagent_x11_print_targets(x11, "received", atoms, len);
624 x11->clipboard_type_count = 0;
625 atom = atom_lists_overlap(x11->utf8_atoms, atoms, utf8_atom_count, len);
626 if (atom) {
627 x11->clipboard_agent_types[x11->clipboard_type_count] =
628 VD_AGENT_CLIPBOARD_UTF8_TEXT;
629 x11->clipboard_x11_targets[x11->clipboard_type_count] = atom;
630 x11->clipboard_type_count++;
633 /* TODO Add support for more types here */
635 if (x11->clipboard_type_count) {
636 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_GRAB, 0,
637 (uint8_t *)x11->clipboard_agent_types,
638 x11->clipboard_type_count * sizeof(uint32_t));
639 vdagent_x11_set_clipboard_owner(x11, owner_guest);
642 vdagent_x11_get_selection_free(x11, (unsigned char *)atoms, incr);
645 static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
646 Atom prop, int process_next_req)
648 XEvent res, *event = &x11->selection_request->event;
649 struct vdagent_x11_selection_request *selection_request;
651 res.xselection.property = prop;
652 res.xselection.type = SelectionNotify;
653 res.xselection.display = event->xselectionrequest.display;
654 res.xselection.requestor = event->xselectionrequest.requestor;
655 res.xselection.selection = event->xselectionrequest.selection;
656 res.xselection.target = event->xselectionrequest.target;
657 res.xselection.time = event->xselectionrequest.time;
658 XSendEvent(x11->display, event->xselectionrequest.requestor, 0, 0, &res);
659 XFlush(x11->display);
661 selection_request = x11->selection_request;
662 x11->selection_request = selection_request->next;
663 free(selection_request);
664 if (process_next_req)
665 vdagent_x11_handle_selection_request(x11);
668 static void vdagent_x11_send_targets(struct vdagent_x11 *x11, XEvent *event)
670 /* TODO Add support for more types here */
671 /* Warning the size of this needs to be increased each time we add support
672 for a new type, or the atom count of an existing type changes */
673 Atom prop, targets[4] = { x11->targets_atom, };
674 int i, j, target_count = 1;
676 for (i = 0; i < x11->clipboard_type_count; i++) {
677 switch (x11->clipboard_agent_types[i]) {
678 case VD_AGENT_CLIPBOARD_UTF8_TEXT:
679 for (j = 0; j < utf8_atom_count; j++) {
680 targets[target_count] = x11->utf8_atoms[j];
681 target_count++;
683 break;
684 /* TODO Add support for more types here */
688 prop = event->xselectionrequest.property;
689 if (prop == None)
690 prop = event->xselectionrequest.target;
692 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
693 XA_ATOM, 32, PropModeReplace, (unsigned char *)&targets,
694 target_count);
695 vdagent_x11_print_targets(x11, "sent", targets, target_count);
696 vdagent_x11_send_selection_notify(x11, prop, 1);
699 static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11)
701 XEvent *event;
702 uint32_t type = VD_AGENT_CLIPBOARD_NONE;
704 if (!x11->selection_request)
705 return;
707 event = &x11->selection_request->event;
709 if (x11->clipboard_owner != owner_client) {
710 fprintf(stderr,
711 "received selection request event for target %s, "
712 "while not owning client clipboard\n",
713 vdagent_x11_get_atom_name(x11, event->xselectionrequest.target));
714 vdagent_x11_send_selection_notify(x11, None, 1);
715 return;
718 if (event->xselectionrequest.target == x11->multiple_atom) {
719 fprintf(stderr, "multiple target not supported\n");
720 vdagent_x11_send_selection_notify(x11, None, 1);
721 return;
724 if (event->xselectionrequest.target == x11->targets_atom) {
725 vdagent_x11_send_targets(x11, event);
726 return;
729 type = vdagent_x11_target_to_type(x11, event->xselectionrequest.target);
730 if (type == VD_AGENT_CLIPBOARD_NONE) {
731 vdagent_x11_send_selection_notify(x11, None, 1);
732 return;
735 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_REQUEST, type, NULL, 0);
738 void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
739 VDAgentMonitorsConfig *mon_config)
741 int i, num_sizes = 0;
742 int best = -1;
743 unsigned int closest_diff = -1;
744 XRRScreenSize* sizes;
745 XRRScreenConfiguration* config;
746 Rotation rotation;
748 if (!x11->has_xrandr)
749 return;
751 if (mon_config->num_of_monitors != 1) {
752 fprintf(stderr, "Only 1 monitor supported, ignoring monitor config\n");
753 return;
756 sizes = XRRSizes(x11->display, x11->screen, &num_sizes);
757 if (!sizes || !num_sizes) {
758 fprintf(stderr, "XRRSizes failed\n");
759 return;
762 /* Find the closest size which will fit within the monitor */
763 for (i = 0; i < num_sizes; i++) {
764 if (sizes[i].width > mon_config->monitors[0].width ||
765 sizes[i].height > mon_config->monitors[0].height)
766 continue; /* Too large for the monitor */
768 unsigned int wdiff = mon_config->monitors[0].width - sizes[i].width;
769 unsigned int hdiff = mon_config->monitors[0].height - sizes[i].height;
770 unsigned int diff = wdiff * wdiff + hdiff * hdiff;
771 if (diff < closest_diff) {
772 closest_diff = diff;
773 best = i;
777 if (best == -1) {
778 fprintf(stderr, "no suitable resolution found for monitor\n");
779 return;
782 config = XRRGetScreenInfo(x11->display, x11->root_window);
783 if(!config) {
784 fprintf(stderr, "get screen info failed\n");
785 return;
787 XRRConfigCurrentConfiguration(config, &rotation);
788 XRRSetScreenConfig(x11->display, config, x11->root_window, best,
789 rotation, CurrentTime);
790 XRRFreeScreenConfigInfo(config);
791 XFlush(x11->display);
794 void vdagent_x11_clipboard_request(struct vdagent_x11 *x11, uint32_t type)
796 Atom target;
798 if (x11->clipboard_owner != owner_guest) {
799 fprintf(stderr,
800 "received clipboard req while not owning guest clipboard\n");
801 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
802 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
803 return;
806 target = vdagent_x11_type_to_target(x11, type);
807 if (target == None) {
808 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
809 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
810 return;
813 if (x11->clipboard_request_target) {
814 fprintf(stderr, "XConvertSelection request is already pending\n");
815 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
816 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
817 return;
819 x11->clipboard_request_target = target;
820 XConvertSelection(x11->display, x11->clipboard_atom, target,
821 x11->clipboard_atom, x11->selection_window, CurrentTime);
822 XFlush(x11->display);
825 void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint32_t *types,
826 uint32_t type_count)
828 int i;
830 x11->clipboard_type_count = 0;
831 for (i = 0; i < type_count; i++) {
832 /* TODO Add support for more types here */
833 /* Check if we support the type */
834 if (types[i] != VD_AGENT_CLIPBOARD_UTF8_TEXT)
835 continue;
837 x11->clipboard_agent_types[x11->clipboard_type_count] = types[i];
838 x11->clipboard_type_count++;
841 if (!x11->clipboard_type_count)
842 return;
844 XSetSelectionOwner(x11->display, x11->clipboard_atom,
845 x11->selection_window, CurrentTime);
846 XFlush(x11->display);
847 vdagent_x11_set_clipboard_owner(x11, owner_client);
850 void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint32_t type,
851 const uint8_t *data, uint32_t size)
853 Atom prop;
854 XEvent *event;
855 uint32_t type_from_event;
857 if (!x11->selection_request) {
858 fprintf(stderr, "received clipboard data without an outstanding"
859 "selection request, ignoring\n");
860 return;
863 event = &x11->selection_request->event;
864 type_from_event = vdagent_x11_target_to_type(x11,
865 event->xselectionrequest.target);
866 if (type_from_event != type) {
867 fprintf(stderr, "expecting type %u clipboard data got %u\n",
868 type_from_event, type);
869 vdagent_x11_send_selection_notify(x11, None, 1);
870 return;
873 prop = event->xselectionrequest.property;
874 if (prop == None)
875 prop = event->xselectionrequest.target;
877 /* FIXME: use INCR for large data transfers */
878 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
879 event->xselectionrequest.target, 8, PropModeReplace,
880 data, size);
881 vdagent_x11_send_selection_notify(x11, prop, 1);
884 void vdagent_x11_clipboard_release(struct vdagent_x11 *x11)
886 XEvent event;
888 if (x11->clipboard_owner != owner_client) {
889 fprintf(stderr,
890 "received clipboard release while not owning client clipboard\n");
891 return;
894 XSetSelectionOwner(x11->display, x11->clipboard_atom, None, CurrentTime);
895 /* Make sure we process the XFixesSetSelectionOwnerNotify event caused
896 by this, so we don't end up changing the clipboard owner to none, after
897 it has already been re-owned because this event is still pending. */
898 XSync(x11->display, False);
899 while (XCheckTypedEvent(x11->display, x11->xfixes_event_base,
900 &event))
901 vdagent_x11_handle_event(x11, event);
903 /* Note no need to do a set_clipboard_owner(owner_none) here, as that is
904 already done by processing the XFixesSetSelectionOwnerNotify event. */