x11: add a few helper functions for multi-clipboard
[vd_agent.git] / vdagent-x11.c
blob728568c6073b3010a1fef66c091d60d2de1cfc35
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: Our event loop is only called when there is data to be read from the
23 X11 socket. If events have arrived and have already been read by libX11 from
24 the socket triggered by other libX11 calls from this file, the select for
25 read in the main loop, won't see these and our event loop won't get called!
27 Thus we must make sure that all queued events have been consumed, whenever
28 we return to the main loop. IOW all (externally callable) functions in this
29 file must end with calling XPending and consuming all queued events.
31 Calling XPending when-ever we return to the mainloop also ensures any
32 pending writes are flushed. */
34 #include <stdlib.h>
35 #include <limits.h>
36 #include <string.h>
37 #include <assert.h>
38 #include <X11/Xatom.h>
39 #include <X11/Xlib.h>
40 #include <X11/extensions/Xrandr.h>
41 #include <X11/extensions/Xfixes.h>
42 #include "vdagentd-proto.h"
43 #include "vdagent-x11.h"
45 enum { owner_none, owner_guest, owner_client };
47 struct vdagent_x11_selection_request {
48 XEvent event;
49 struct vdagent_x11_selection_request *next;
52 struct clipboard_format_tmpl {
53 uint32_t type;
54 const char *atom_names[16];
57 struct clipboard_format_info {
58 uint32_t type;
59 Atom atoms[16];
60 int atom_count;
63 static const struct clipboard_format_tmpl clipboard_format_templates[] = {
64 { VD_AGENT_CLIPBOARD_UTF8_TEXT, { "UTF8_STRING",
65 "text/plain;charset=UTF-8", "text/plain;charset=utf-8", NULL }, },
66 { VD_AGENT_CLIPBOARD_IMAGE_PNG, { "image/png", NULL }, },
67 { VD_AGENT_CLIPBOARD_IMAGE_BMP, { "image/bmp", "image/x-bmp",
68 "image/x-MS-bmp", "image/x-win-bitmap", NULL }, },
69 { VD_AGENT_CLIPBOARD_IMAGE_TIFF, { "image/tiff", NULL }, },
70 { VD_AGENT_CLIPBOARD_IMAGE_JPG, { "image/jpeg", NULL }, },
73 #define clipboard_format_count (sizeof(clipboard_format_templates)/sizeof(clipboard_format_templates[0]))
75 struct vdagent_x11 {
76 struct clipboard_format_info clipboard_formats[clipboard_format_count];
77 Display *display;
78 Atom clipboard_atom;
79 Atom clipboard_primary_atom;
80 Atom targets_atom;
81 Atom incr_atom;
82 Atom multiple_atom;
83 Window root_window;
84 Window selection_window;
85 struct udscs_connection *vdagentd;
86 FILE *errfile;
87 int verbose;
88 int fd;
89 int screen;
90 int width;
91 int height;
92 int has_xrandr;
93 int has_xfixes;
94 int xfixes_event_base;
95 int max_prop_size;
96 int expected_targets_notifies;
97 int expect_property_notify;
98 int clipboard_owner;
99 Atom clipboard_request_target;
100 int clipboard_type_count;
101 uint32_t clipboard_agent_types[256];
102 Atom clipboard_x11_targets[256];
103 uint8_t *clipboard_data;
104 uint32_t clipboard_data_size;
105 uint32_t clipboard_data_space;
106 struct vdagent_x11_selection_request *selection_request;
107 uint8_t *selection_req_data;
108 uint32_t selection_req_data_pos;
109 uint32_t selection_req_data_size;
110 Atom selection_req_atom;
113 static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11);
114 static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
115 XEvent *event, int incr);
116 static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11);
117 static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
118 XEvent *event, int incr);
119 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11,
120 XEvent *del_event);
121 static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
122 Atom prop,
123 int process_next_req);
124 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11,
125 int new_owner);
127 struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd,
128 FILE *errfile, int verbose)
130 struct vdagent_x11 *x11;
131 XWindowAttributes attrib;
132 int i, j, major, minor;
134 x11 = calloc(1, sizeof(*x11));
135 if (!x11) {
136 fprintf(errfile, "out of memory allocating vdagent_x11 struct\n");
137 return NULL;
140 x11->vdagentd = vdagentd;
141 x11->errfile = errfile;
142 x11->verbose = verbose;
144 x11->display = XOpenDisplay(NULL);
145 if (!x11->display) {
146 fprintf(x11->errfile, "could not connect to X-server\n");
147 free(x11);
148 return NULL;
151 x11->screen = DefaultScreen(x11->display);
152 x11->root_window = RootWindow(x11->display, x11->screen);
153 x11->fd = ConnectionNumber(x11->display);
154 x11->clipboard_atom = XInternAtom(x11->display, "CLIPBOARD", False);
155 x11->clipboard_primary_atom = XInternAtom(x11->display, "PRIMARY", False);
156 x11->targets_atom = XInternAtom(x11->display, "TARGETS", False);
157 x11->incr_atom = XInternAtom(x11->display, "INCR", False);
158 x11->multiple_atom = XInternAtom(x11->display, "MULTIPLE", False);
159 for(i = 0; i < clipboard_format_count; i++) {
160 x11->clipboard_formats[i].type = clipboard_format_templates[i].type;
161 for(j = 0; clipboard_format_templates[i].atom_names[j]; j++) {
162 x11->clipboard_formats[i].atoms[j] =
163 XInternAtom(x11->display,
164 clipboard_format_templates[i].atom_names[j],
165 False);
167 x11->clipboard_formats[i].atom_count = j;
170 /* We should not store properties (for selections) on the root window */
171 x11->selection_window = XCreateSimpleWindow(x11->display, x11->root_window,
172 0, 0, 1, 1, 0, 0, 0);
173 if (x11->verbose)
174 fprintf(x11->errfile, "Selection window: %u\n",
175 (unsigned int)x11->selection_window);
177 if (XRRQueryExtension(x11->display, &i, &i))
178 x11->has_xrandr = 1;
179 else
180 fprintf(x11->errfile, "no xrandr\n");
182 if (XFixesQueryExtension(x11->display, &x11->xfixes_event_base, &i) &&
183 XFixesQueryVersion(x11->display, &major, &minor) && major >= 1) {
184 x11->has_xfixes = 1;
185 XFixesSelectSelectionInput(x11->display, x11->root_window,
186 x11->clipboard_atom,
187 XFixesSetSelectionOwnerNotifyMask|
188 XFixesSelectionWindowDestroyNotifyMask|
189 XFixesSelectionClientCloseNotifyMask);
190 XFixesSelectSelectionInput(x11->display, x11->root_window,
191 x11->clipboard_primary_atom,
192 XFixesSetSelectionOwnerNotifyMask|
193 XFixesSelectionWindowDestroyNotifyMask|
194 XFixesSelectionClientCloseNotifyMask);
195 } else
196 fprintf(x11->errfile,
197 "no xfixes, no guest -> client copy paste support\n");
199 x11->max_prop_size = XExtendedMaxRequestSize(x11->display);
200 if (x11->max_prop_size) {
201 x11->max_prop_size -= 100;
202 } else {
203 x11->max_prop_size = XMaxRequestSize(x11->display) - 100;
205 /* Be a good X11 citizen and maximize the amount of data we send at once */
206 if (x11->max_prop_size > 262144)
207 x11->max_prop_size = 262144;
209 /* Catch resolution changes */
210 XSelectInput(x11->display, x11->root_window, StructureNotifyMask);
212 /* Get the current resolution */
213 XGetWindowAttributes(x11->display, x11->root_window, &attrib);
214 x11->width = attrib.width;
215 x11->height = attrib.height;
216 vdagent_x11_send_daemon_guest_xorg_res(x11);
218 /* Flush output buffers and consume any pending events */
219 vdagent_x11_do_read(x11);
221 return x11;
224 void vdagent_x11_destroy(struct vdagent_x11 *x11)
226 if (!x11)
227 return;
229 vdagent_x11_set_clipboard_owner(x11, owner_none);
230 XCloseDisplay(x11->display);
231 free(x11);
234 int vdagent_x11_get_fd(struct vdagent_x11 *x11)
236 return x11->fd;
239 static void vdagent_x11_next_selection_request(struct vdagent_x11 *x11)
241 struct vdagent_x11_selection_request *selection_request;
243 free(x11->selection_req_data);
244 x11->selection_req_data = NULL;
245 x11->selection_req_data_pos = 0;
246 x11->selection_req_data_size = 0;
247 x11->selection_req_atom = None;
249 selection_request = x11->selection_request;
250 x11->selection_request = selection_request->next;
251 free(selection_request);
254 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11,
255 int new_owner)
257 /* Clear pending requests and clipboard data */
258 if (x11->selection_request) {
259 fprintf(x11->errfile,
260 "selection requests pending on clipboard ownership change, "
261 "clearing\n");
262 while (x11->selection_request) {
263 vdagent_x11_send_selection_notify(x11, None, 0);
264 vdagent_x11_next_selection_request(x11);
267 if (x11->clipboard_request_target != None) {
268 fprintf(x11->errfile,
269 "client clipboard request pending on clipboard ownership "
270 "change, clearing\n");
271 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
272 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
273 x11->clipboard_request_target = None;
275 x11->clipboard_data_size = 0;
276 x11->expect_property_notify = 0;
278 if (new_owner == owner_none) {
279 /* When going from owner_guest to owner_none we need to send a
280 clipboard release message to the client */
281 if (x11->clipboard_owner == owner_guest)
282 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_RELEASE, 0, NULL, 0);
284 x11->clipboard_type_count = 0;
286 x11->clipboard_owner = new_owner;
289 static int vdagent_x11_get_clipboard_atom(struct vdagent_x11 *x11, uint8_t selection, Atom* clipboard)
291 if (selection == VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) {
292 *clipboard = x11->clipboard_atom;
293 } else if (selection == VD_AGENT_CLIPBOARD_SELECTION_PRIMARY) {
294 *clipboard = x11->clipboard_primary_atom;
295 } else {
296 fprintf(x11->errfile, "selection_get_grab: unknown selection\n");
297 return -1;
300 return 0;
303 static int vdagent_x11_get_clipboard_selection(struct vdagent_x11 *x11,
304 XEvent *event, uint8_t *selection)
306 Atom atom;
308 if (event->type == x11->xfixes_event_base) {
309 XFixesSelectionNotifyEvent *xfev = (XFixesSelectionNotifyEvent *)event;
310 atom = xfev->selection;
311 } else if (event->type == SelectionNotify) {
312 atom = event->xselection.selection;
313 } else if (event->type == SelectionRequest) {
314 atom = event->xselectionrequest.selection;
315 } else {
316 fprintf(x11->errfile, "get_clipboard_selection: unknown event type\n");
317 return -1;
320 if (atom == x11->clipboard_atom) {
321 *selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
322 } else if (atom == x11->clipboard_primary_atom) {
323 *selection = VD_AGENT_CLIPBOARD_SELECTION_PRIMARY;
324 } else {
325 fprintf(x11->errfile, "get_clipboard_selection: unknown selection\n");
326 return -1;
329 return 0;
332 static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
334 int handled = 0;
337 if (event.type == x11->xfixes_event_base) {
338 union {
339 XEvent ev;
340 XFixesSelectionNotifyEvent xfev;
341 } ev;
343 ev.ev = event;
344 switch (ev.xfev.subtype) {
345 case XFixesSetSelectionOwnerNotify:
346 break;
347 /* Treat ... as a SelectionOwnerNotify None */
348 case XFixesSelectionWindowDestroyNotify:
349 case XFixesSelectionClientCloseNotify:
350 ev.xfev.owner = None;
351 break;
352 default:
353 if (x11->verbose)
354 fprintf(x11->errfile,
355 "unexpected xfix event subtype %d window %d\n",
356 (int)ev.xfev.subtype, (int)event.xany.window);
357 return;
360 if (x11->verbose)
361 fprintf(x11->errfile, "New selection owner: %u\n",
362 (unsigned int)ev.xfev.owner);
364 /* Ignore becoming the owner ourselves */
365 if (ev.xfev.owner == x11->selection_window)
366 return;
368 /* If the clipboard owner is changed we no longer own it */
369 vdagent_x11_set_clipboard_owner(x11, owner_none);
371 if (ev.xfev.owner == None)
372 return;
374 /* Request the supported targets from the new owner */
375 XConvertSelection(x11->display, x11->clipboard_atom, x11->targets_atom,
376 x11->targets_atom, x11->selection_window,
377 CurrentTime);
378 x11->expected_targets_notifies++;
379 return;
382 switch (event.type) {
383 case ConfigureNotify:
384 if (event.xconfigure.window != x11->root_window)
385 break;
387 handled = 1;
389 if (event.xconfigure.width == x11->width &&
390 event.xconfigure.height == x11->height)
391 break;
393 x11->width = event.xconfigure.width;
394 x11->height = event.xconfigure.height;
396 vdagent_x11_send_daemon_guest_xorg_res(x11);
397 break;
398 case SelectionNotify:
399 if (event.xselection.target == x11->targets_atom)
400 vdagent_x11_handle_targets_notify(x11, &event, 0);
401 else
402 vdagent_x11_handle_selection_notify(x11, &event, 0);
404 handled = 1;
405 break;
406 case PropertyNotify:
407 if (x11->expect_property_notify &&
408 event.xproperty.state == PropertyNewValue) {
409 if (event.xproperty.atom == x11->targets_atom) {
410 vdagent_x11_handle_targets_notify(x11, &event, 1);
411 } else {
412 vdagent_x11_handle_selection_notify(x11, &event, 1);
415 if (x11->selection_req_data &&
416 event.xproperty.state == PropertyDelete) {
417 vdagent_x11_handle_property_delete_notify(x11, &event);
419 /* Always mark as handled, since we cannot unselect input for property
420 notifications once we are done with handling the incr transfer. */
421 handled = 1;
422 break;
423 case SelectionClear:
424 /* Do nothing the clipboard ownership will get updated through
425 the XFixesSetSelectionOwnerNotify event */
426 handled = 1;
427 break;
428 case SelectionRequest: {
429 struct vdagent_x11_selection_request *req, *new_req;
431 new_req = malloc(sizeof(*new_req));
432 if (!new_req) {
433 fprintf(x11->errfile,
434 "out of memory on SelectionRequest, ignoring.\n");
435 break;
438 handled = 1;
440 new_req->event = event;
441 new_req->next = NULL;
443 if (!x11->selection_request) {
444 x11->selection_request = new_req;
445 vdagent_x11_handle_selection_request(x11);
446 break;
449 /* maybe we should limit the selection_request stack depth ? */
450 req = x11->selection_request;
451 while (req->next)
452 req = req->next;
454 req->next = new_req;
455 break;
458 if (!handled && x11->verbose)
459 fprintf(x11->errfile, "unhandled x11 event, type %d, window %d\n",
460 (int)event.type, (int)event.xany.window);
463 void vdagent_x11_do_read(struct vdagent_x11 *x11)
465 XEvent event;
467 while (XPending(x11->display)) {
468 XNextEvent(x11->display, &event);
469 vdagent_x11_handle_event(x11, event);
473 static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11)
475 struct vdagentd_guest_xorg_resolution res;
477 res.width = x11->width;
478 res.height = x11->height;
480 udscs_write(x11->vdagentd, VDAGENTD_GUEST_XORG_RESOLUTION, 0,
481 (uint8_t *)&res, sizeof(res));
484 static const char *vdagent_x11_get_atom_name(struct vdagent_x11 *x11, Atom a)
486 if (a == None)
487 return "None";
489 return XGetAtomName(x11->display, a);
492 static int vdagent_x11_get_selection(struct vdagent_x11 *x11, XEvent *event,
493 Atom type, Atom prop, int format,
494 unsigned char **data_ret, int incr)
496 Bool del = incr ? True: False;
497 Atom type_ret;
498 int format_ret, ret_val = -1;
499 unsigned long len, remain;
500 unsigned char *data = NULL;
502 *data_ret = NULL;
504 if (incr) {
505 if (event->xproperty.atom != prop) {
506 fprintf(x11->errfile, "PropertyNotify parameters mismatch\n");
507 goto exit;
509 } else {
510 if (event->xselection.property == None) {
511 if (x11->verbose)
512 fprintf(x11->errfile,
513 "XConvertSelection refused by clipboard owner\n");
514 goto exit;
517 if (event->xselection.requestor != x11->selection_window ||
518 event->xselection.selection != x11->clipboard_atom ||
519 event->xselection.property != prop) {
520 fprintf(x11->errfile, "SelectionNotify parameters mismatch\n");
521 goto exit;
525 if (XGetWindowProperty(x11->display, x11->selection_window, prop, 0,
526 LONG_MAX, del, type, &type_ret, &format_ret, &len,
527 &remain, &data) != Success) {
528 fprintf(x11->errfile, "XGetWindowProperty failed\n");
529 goto exit;
532 if (!incr) {
533 if (type_ret == x11->incr_atom) {
534 int prop_min_size = *(uint32_t*)data;
536 if (x11->expect_property_notify) {
537 fprintf(x11->errfile,
538 "received an incr SelectionNotify while "
539 "still reading another incr property\n");
540 goto exit;
543 if (x11->clipboard_data_space < prop_min_size) {
544 free(x11->clipboard_data);
545 x11->clipboard_data = malloc(prop_min_size);
546 if (!x11->clipboard_data) {
547 fprintf(x11->errfile,
548 "out of memory allocating clipboard buffer\n");
549 x11->clipboard_data_space = 0;
550 goto exit;
552 x11->clipboard_data_space = prop_min_size;
554 x11->expect_property_notify = 1;
555 XSelectInput(x11->display, x11->selection_window,
556 PropertyChangeMask);
557 XDeleteProperty(x11->display, x11->selection_window, prop);
558 XFree(data);
559 return 0; /* Wait for more data */
561 XDeleteProperty(x11->display, x11->selection_window, prop);
564 if (type_ret != type) {
565 fprintf(x11->errfile, "expected property type: %s, got: %s\n",
566 vdagent_x11_get_atom_name(x11, type),
567 vdagent_x11_get_atom_name(x11, type_ret));
568 goto exit;
571 if (format_ret != format) {
572 fprintf(x11->errfile, "expected %d bit format, got %d bits\n", format,
573 format_ret);
574 goto exit;
577 /* Convert len to bytes */
578 switch(format) {
579 case 8:
580 break;
581 case 16:
582 len *= sizeof(short);
583 break;
584 case 32:
585 len *= sizeof(long);
586 break;
589 if (incr) {
590 if (len) {
591 if (x11->clipboard_data_size + len > x11->clipboard_data_space) {
592 void *old_clipboard_data = x11->clipboard_data;
594 x11->clipboard_data_space = x11->clipboard_data_size + len;
595 x11->clipboard_data = realloc(x11->clipboard_data,
596 x11->clipboard_data_space);
597 if (!x11->clipboard_data) {
598 fprintf(x11->errfile,
599 "out of memory allocating clipboard buffer\n");
600 x11->clipboard_data_space = 0;
601 free(old_clipboard_data);
602 goto exit;
605 memcpy(x11->clipboard_data + x11->clipboard_data_size, data, len);
606 x11->clipboard_data_size += len;
607 if (x11->verbose)
608 fprintf(x11->errfile, "Appended %ld bytes to buffer\n", len);
609 XFree(data);
610 return 0; /* Wait for more data */
612 len = x11->clipboard_data_size;
613 *data_ret = x11->clipboard_data;
614 } else
615 *data_ret = data;
617 if (len > 0) {
618 ret_val = len;
619 } else {
620 fprintf(x11->errfile, "property contains no data (zero length)\n");
621 *data_ret = NULL;
624 exit:
625 if ((incr || ret_val == -1) && data)
626 XFree(data);
628 if (incr) {
629 x11->clipboard_data_size = 0;
630 x11->expect_property_notify = 0;
633 return ret_val;
636 static void vdagent_x11_get_selection_free(struct vdagent_x11 *x11,
637 unsigned char *data, int incr)
639 if (incr) {
640 /* If the clipboard has grown large return the memory to the system */
641 if (x11->clipboard_data_space > 512 * 1024) {
642 free(x11->clipboard_data);
643 x11->clipboard_data = NULL;
644 x11->clipboard_data_space = 0;
646 } else if (data)
647 XFree(data);
650 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11 *x11,
651 Atom target)
653 int i, j;
655 for (i = 0; i < clipboard_format_count; i++) {
656 for (j = 0; j < x11->clipboard_formats[i].atom_count; i++) {
657 if (x11->clipboard_formats[i].atoms[j] == target) {
658 return x11->clipboard_formats[i].type;
663 fprintf(x11->errfile, "unexpected selection type %s\n",
664 vdagent_x11_get_atom_name(x11, target));
665 return VD_AGENT_CLIPBOARD_NONE;
668 static Atom vdagent_x11_type_to_target(struct vdagent_x11 *x11, uint32_t type)
670 int i;
672 for (i = 0; i < x11->clipboard_type_count; i++)
673 if (x11->clipboard_agent_types[i] == type)
674 return x11->clipboard_x11_targets[i];
676 fprintf(x11->errfile, "client requested unavailable type %u\n", type);
677 return None;
680 static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
681 XEvent *event, int incr)
683 int len = -1;
684 unsigned char *data = NULL;
685 uint32_t type;
687 if (x11->clipboard_request_target == None) {
688 fprintf(x11->errfile, "SelectionNotify received without a target\n");
689 return;
692 type = vdagent_x11_target_to_type(x11, x11->clipboard_request_target);
693 if (!incr &&
694 event->xselection.target != x11->clipboard_request_target &&
695 event->xselection.target != x11->incr_atom)
696 fprintf(x11->errfile, "Requested %s target got %s\n",
697 vdagent_x11_get_atom_name(x11, x11->clipboard_request_target),
698 vdagent_x11_get_atom_name(x11, event->xselection.target));
699 else
700 len = vdagent_x11_get_selection(x11, event, x11->clipboard_request_target,
701 x11->clipboard_atom, 8, &data, incr);
702 if (len == 0) /* waiting for more data? */
703 return;
704 if (len == -1) {
705 type = VD_AGENT_CLIPBOARD_NONE;
706 len = 0;
709 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, type, data, len);
710 x11->clipboard_request_target = None;
711 vdagent_x11_get_selection_free(x11, data, incr);
714 static Atom atom_lists_overlap(Atom *atoms1, Atom *atoms2, int l1, int l2)
716 int i, j;
718 for (i = 0; i < l1; i++)
719 for (j = 0; j < l2; j++)
720 if (atoms1[i] == atoms2[j])
721 return atoms1[i];
723 return 0;
726 static void vdagent_x11_print_targets(struct vdagent_x11 *x11,
727 const char *action, Atom *atoms, int c)
729 int i;
731 if (!x11->verbose)
732 return;
734 fprintf(x11->errfile, "%s %d targets:\n", action, c);
735 for (i = 0; i < c; i++)
736 fprintf(x11->errfile, "%s\n",
737 vdagent_x11_get_atom_name(x11, atoms[i]));
740 static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
741 XEvent *event, int incr)
743 int i, len;
744 Atom atom, *atoms = NULL;
746 if (!x11->expected_targets_notifies) {
747 fprintf(x11->errfile, "unexpected selection notify TARGETS\n");
748 return;
751 x11->expected_targets_notifies--;
753 /* If we have more targets_notifies pending, ignore this one, we
754 are only interested in the targets list of the current owner
755 (which is the last one we've requested a targets list from) */
756 if (x11->expected_targets_notifies)
757 return;
759 len = vdagent_x11_get_selection(x11, event, XA_ATOM, x11->targets_atom, 32,
760 (unsigned char **)&atoms, incr);
761 if (len == 0 || len == -1) /* waiting for more data or error? */
762 return;
764 /* bytes -> atoms */
765 len /= sizeof(Atom);
766 vdagent_x11_print_targets(x11, "received", atoms, len);
768 x11->clipboard_type_count = 0;
769 for (i = 0; i < clipboard_format_count; i++) {
770 atom = atom_lists_overlap(x11->clipboard_formats[i].atoms, atoms,
771 x11->clipboard_formats[i].atom_count, len);
772 if (atom) {
773 x11->clipboard_agent_types[x11->clipboard_type_count] =
774 x11->clipboard_formats[i].type;
775 x11->clipboard_x11_targets[x11->clipboard_type_count] = atom;
776 x11->clipboard_type_count++;
777 if (x11->clipboard_type_count ==
778 sizeof(x11->clipboard_agent_types)/sizeof(uint32_t)) {
779 fprintf(x11->errfile,
780 "handle_targets_notify: too many types\n");
781 break;
786 if (x11->clipboard_type_count) {
787 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_GRAB, 0,
788 (uint8_t *)x11->clipboard_agent_types,
789 x11->clipboard_type_count * sizeof(uint32_t));
790 vdagent_x11_set_clipboard_owner(x11, owner_guest);
793 vdagent_x11_get_selection_free(x11, (unsigned char *)atoms, incr);
796 static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
797 Atom prop, int process_next_req)
799 XEvent res, *event = &x11->selection_request->event;
801 res.xselection.property = prop;
802 res.xselection.type = SelectionNotify;
803 res.xselection.display = event->xselectionrequest.display;
804 res.xselection.requestor = event->xselectionrequest.requestor;
805 res.xselection.selection = event->xselectionrequest.selection;
806 res.xselection.target = event->xselectionrequest.target;
807 res.xselection.time = event->xselectionrequest.time;
808 XSendEvent(x11->display, event->xselectionrequest.requestor, 0, 0, &res);
810 if (process_next_req) {
811 vdagent_x11_next_selection_request(x11);
812 vdagent_x11_handle_selection_request(x11);
816 static void vdagent_x11_send_targets(struct vdagent_x11 *x11, XEvent *event)
818 Atom prop, targets[256] = { x11->targets_atom, };
819 int i, j, k, target_count = 1;
821 for (i = 0; i < x11->clipboard_type_count; i++) {
822 for (j = 0; j < clipboard_format_count; j++) {
823 if (x11->clipboard_formats[j].type !=
824 x11->clipboard_agent_types[i])
825 continue;
827 for (k = 0; k < x11->clipboard_formats[j].atom_count; k++) {
828 targets[target_count] = x11->clipboard_formats[j].atoms[k];
829 target_count++;
830 if (target_count == sizeof(targets)/sizeof(Atom)) {
831 fprintf(x11->errfile, "send_targets: too many targets\n");
832 goto exit_loop;
837 exit_loop:
839 prop = event->xselectionrequest.property;
840 if (prop == None)
841 prop = event->xselectionrequest.target;
843 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
844 XA_ATOM, 32, PropModeReplace, (unsigned char *)&targets,
845 target_count);
846 vdagent_x11_print_targets(x11, "sent", targets, target_count);
847 vdagent_x11_send_selection_notify(x11, prop, 1);
850 static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11)
852 XEvent *event;
853 uint32_t type = VD_AGENT_CLIPBOARD_NONE;
855 if (!x11->selection_request)
856 return;
858 event = &x11->selection_request->event;
860 if (x11->clipboard_owner != owner_client) {
861 fprintf(x11->errfile,
862 "received selection request event for target %s, "
863 "while not owning client clipboard\n",
864 vdagent_x11_get_atom_name(x11, event->xselectionrequest.target));
865 vdagent_x11_send_selection_notify(x11, None, 1);
866 return;
869 if (event->xselectionrequest.target == x11->multiple_atom) {
870 fprintf(x11->errfile, "multiple target not supported\n");
871 vdagent_x11_send_selection_notify(x11, None, 1);
872 return;
875 if (event->xselectionrequest.target == x11->targets_atom) {
876 vdagent_x11_send_targets(x11, event);
877 return;
880 type = vdagent_x11_target_to_type(x11, event->xselectionrequest.target);
881 if (type == VD_AGENT_CLIPBOARD_NONE) {
882 vdagent_x11_send_selection_notify(x11, None, 1);
883 return;
886 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_REQUEST, type, NULL, 0);
889 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11,
890 XEvent *del_event)
892 XEvent *sel_event;
893 int len;
895 assert(x11->selection_request);
896 sel_event = &x11->selection_request->event;
897 if (del_event->xproperty.window != sel_event->xselectionrequest.requestor
898 || del_event->xproperty.atom != x11->selection_req_atom) {
899 return;
902 len = x11->selection_req_data_size - x11->selection_req_data_pos;
903 if (len > x11->max_prop_size) {
904 len = x11->max_prop_size;
907 if (x11->verbose) {
908 if (len) {
909 fprintf(x11->errfile, "Sending %d-%d/%d bytes of clipboard data\n",
910 x11->selection_req_data_pos,
911 x11->selection_req_data_pos + len - 1,
912 x11->selection_req_data_size);
913 } else {
914 fprintf(x11->errfile, "Ending incr send of clipboard data\n");
917 XChangeProperty(x11->display, sel_event->xselectionrequest.requestor,
918 x11->selection_req_atom,
919 sel_event->xselectionrequest.target, 8, PropModeReplace,
920 x11->selection_req_data + x11->selection_req_data_pos,
921 len);
922 x11->selection_req_data_pos += len;
924 /* Note we must explictly send a 0 sized XChangeProperty to signal the
925 incr transfer is done. Hence we do not check if we've send all data
926 but instead check we've send the final 0 sized XChangeProperty. */
927 if (len == 0) {
928 vdagent_x11_next_selection_request(x11);
929 vdagent_x11_handle_selection_request(x11);
933 void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
934 VDAgentMonitorsConfig *mon_config)
936 int i, num_sizes = 0;
937 int best = -1;
938 unsigned int closest_diff = -1;
939 XRRScreenSize* sizes;
940 XRRScreenConfiguration* config;
941 Rotation rotation;
943 if (!x11->has_xrandr)
944 return;
946 if (mon_config->num_of_monitors != 1) {
947 fprintf(x11->errfile,
948 "Only 1 monitor supported, ignoring additional monitors\n");
951 sizes = XRRSizes(x11->display, x11->screen, &num_sizes);
952 if (!sizes || !num_sizes) {
953 fprintf(x11->errfile, "XRRSizes failed\n");
954 return;
957 /* Find the closest size which will fit within the monitor */
958 for (i = 0; i < num_sizes; i++) {
959 if (sizes[i].width > mon_config->monitors[0].width ||
960 sizes[i].height > mon_config->monitors[0].height)
961 continue; /* Too large for the monitor */
963 unsigned int wdiff = mon_config->monitors[0].width - sizes[i].width;
964 unsigned int hdiff = mon_config->monitors[0].height - sizes[i].height;
965 unsigned int diff = wdiff * wdiff + hdiff * hdiff;
966 if (diff < closest_diff) {
967 closest_diff = diff;
968 best = i;
972 if (best == -1) {
973 fprintf(x11->errfile, "no suitable resolution found for monitor\n");
974 return;
977 config = XRRGetScreenInfo(x11->display, x11->root_window);
978 if(!config) {
979 fprintf(x11->errfile, "get screen info failed\n");
980 return;
982 XRRConfigCurrentConfiguration(config, &rotation);
983 XRRSetScreenConfig(x11->display, config, x11->root_window, best,
984 rotation, CurrentTime);
985 XRRFreeScreenConfigInfo(config);
986 x11->width = sizes[best].width;
987 x11->height = sizes[best].height;
988 vdagent_x11_send_daemon_guest_xorg_res(x11);
990 /* Flush output buffers and consume any pending events */
991 vdagent_x11_do_read(x11);
994 void vdagent_x11_clipboard_request(struct vdagent_x11 *x11, uint32_t type)
996 Atom target;
998 if (x11->clipboard_owner != owner_guest) {
999 fprintf(x11->errfile,
1000 "received clipboard req while not owning guest clipboard\n");
1001 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
1002 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
1003 return;
1006 target = vdagent_x11_type_to_target(x11, type);
1007 if (target == None) {
1008 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
1009 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
1010 return;
1013 if (x11->clipboard_request_target) {
1014 fprintf(x11->errfile,
1015 "XConvertSelection request is already pending\n");
1016 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
1017 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
1018 return;
1020 x11->clipboard_request_target = target;
1021 XConvertSelection(x11->display, x11->clipboard_atom, target,
1022 x11->clipboard_atom, x11->selection_window, CurrentTime);
1024 /* Flush output buffers and consume any pending events */
1025 vdagent_x11_do_read(x11);
1028 void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint32_t *types,
1029 uint32_t type_count)
1031 if (type_count > sizeof(x11->clipboard_agent_types)/sizeof(uint32_t)) {
1032 fprintf(x11->errfile, "x11_clipboard_grab: too many types\n");
1033 type_count = sizeof(x11->clipboard_agent_types)/sizeof(uint32_t);
1036 memcpy(x11->clipboard_agent_types, types, type_count * sizeof(uint32_t));
1037 x11->clipboard_type_count = type_count;
1039 XSetSelectionOwner(x11->display, x11->clipboard_atom,
1040 x11->selection_window, CurrentTime);
1041 vdagent_x11_set_clipboard_owner(x11, owner_client);
1043 /* Flush output buffers and consume any pending events */
1044 vdagent_x11_do_read(x11);
1047 void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint32_t type,
1048 uint8_t *data, uint32_t size)
1050 Atom prop;
1051 XEvent *event;
1052 uint32_t type_from_event;
1054 if (x11->selection_req_data) {
1055 if (type || size) {
1056 fprintf(x11->errfile, "received clipboard data while still sending"
1057 " data from previous request, ignoring\n");
1059 free(data);
1060 return;
1063 if (!x11->selection_request) {
1064 if (type || size) {
1065 fprintf(x11->errfile, "received clipboard data without an "
1066 "outstanding selection request, ignoring\n");
1068 free(data);
1069 return;
1072 event = &x11->selection_request->event;
1073 type_from_event = vdagent_x11_target_to_type(x11,
1074 event->xselectionrequest.target);
1075 if (type_from_event != type) {
1076 fprintf(x11->errfile, "expecting type %u clipboard data got %u\n",
1077 type_from_event, type);
1078 vdagent_x11_send_selection_notify(x11, None, 1);
1079 free(data);
1081 /* Flush output buffers and consume any pending events */
1082 vdagent_x11_do_read(x11);
1083 return;
1086 prop = event->xselectionrequest.property;
1087 if (prop == None)
1088 prop = event->xselectionrequest.target;
1090 if (size > x11->max_prop_size) {
1091 unsigned long len = size;
1092 if (x11->verbose)
1093 fprintf(x11->errfile, "Starting incr send of clipboard data\n");
1094 x11->selection_req_data = data;
1095 x11->selection_req_data_pos = 0;
1096 x11->selection_req_data_size = size;
1097 x11->selection_req_atom = prop;
1098 XSelectInput(x11->display, event->xselectionrequest.requestor,
1099 PropertyChangeMask);
1100 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
1101 x11->incr_atom, 32, PropModeReplace,
1102 (unsigned char*)&len, 1);
1103 vdagent_x11_send_selection_notify(x11, prop, 0);
1104 } else {
1105 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
1106 event->xselectionrequest.target, 8, PropModeReplace,
1107 data, size);
1108 vdagent_x11_send_selection_notify(x11, prop, 1);
1109 free(data);
1112 /* Flush output buffers and consume any pending events */
1113 vdagent_x11_do_read(x11);
1116 void vdagent_x11_clipboard_release(struct vdagent_x11 *x11)
1118 XEvent event;
1120 if (x11->clipboard_owner != owner_client) {
1121 fprintf(x11->errfile,
1122 "received clipboard release while not owning client clipboard\n");
1123 return;
1126 XSetSelectionOwner(x11->display, x11->clipboard_atom, None, CurrentTime);
1127 /* Make sure we process the XFixesSetSelectionOwnerNotify event caused
1128 by this, so we don't end up changing the clipboard owner to none, after
1129 it has already been re-owned because this event is still pending. */
1130 XSync(x11->display, False);
1131 while (XCheckTypedEvent(x11->display, x11->xfixes_event_base,
1132 &event))
1133 vdagent_x11_handle_event(x11, event);
1135 /* Note no need to do a set_clipboard_owner(owner_none) here, as that is
1136 already done by processing the XFixesSetSelectionOwnerNotify event. */
1138 /* Flush output buffers and consume any pending events */
1139 vdagent_x11_do_read(x11);