Do not set crtc config to 0
[vd_agent/hramrach.git] / src / vdagent-x11.c
blob3af8e7595121ce988d8fb9074c6b36125501df1e
1 /* vdagent-x11.c vdagent x11 code
3 Copyright 2010-2011 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/Xfixes.h>
41 #include "vdagentd-proto.h"
42 #include "vdagent-x11.h"
43 #include "vdagent-x11-priv.h"
45 static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
46 XEvent *event, int incr);
47 static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11);
48 static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
49 XEvent *event);
50 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11,
51 XEvent *del_event);
52 static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
53 Atom prop, struct vdagent_x11_selection_request *request);
54 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11,
55 uint8_t selection, int new_owner);
57 static const char *vdagent_x11_sel_to_str(uint8_t selection) {
58 switch (selection) {
59 case VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD:
60 return "clipboard";
61 case VD_AGENT_CLIPBOARD_SELECTION_PRIMARY:
62 return "primary";
63 case VD_AGENT_CLIPBOARD_SELECTION_SECONDARY:
64 return "secondary";
65 default:
66 return "unknown";
70 static int debug_error_handler(Display *display, XErrorEvent *error)
72 abort();
75 struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd,
76 FILE *errfile, int verbose, int sync)
78 struct vdagent_x11 *x11;
79 XWindowAttributes attrib;
80 int i, j, major, minor;
82 x11 = calloc(1, sizeof(*x11));
83 if (!x11) {
84 fprintf(errfile, "out of memory allocating vdagent_x11 struct\n");
85 return NULL;
88 x11->vdagentd = vdagentd;
89 x11->errfile = errfile;
90 x11->verbose = verbose;
92 x11->display = XOpenDisplay(NULL);
93 if (!x11->display) {
94 fprintf(x11->errfile, "could not connect to X-server\n");
95 free(x11);
96 return NULL;
99 if (sync) {
100 XSetErrorHandler(debug_error_handler);
101 XSynchronize(x11->display, True);
104 x11->screen = DefaultScreen(x11->display);
105 x11->root_window = RootWindow(x11->display, x11->screen);
106 x11->fd = ConnectionNumber(x11->display);
107 x11->clipboard_atom = XInternAtom(x11->display, "CLIPBOARD", False);
108 x11->clipboard_primary_atom = XInternAtom(x11->display, "PRIMARY", False);
109 x11->targets_atom = XInternAtom(x11->display, "TARGETS", False);
110 x11->incr_atom = XInternAtom(x11->display, "INCR", False);
111 x11->multiple_atom = XInternAtom(x11->display, "MULTIPLE", False);
112 for(i = 0; i < clipboard_format_count; i++) {
113 x11->clipboard_formats[i].type = clipboard_format_templates[i].type;
114 for(j = 0; clipboard_format_templates[i].atom_names[j]; j++) {
115 x11->clipboard_formats[i].atoms[j] =
116 XInternAtom(x11->display,
117 clipboard_format_templates[i].atom_names[j],
118 False);
120 x11->clipboard_formats[i].atom_count = j;
123 /* We should not store properties (for selections) on the root window */
124 x11->selection_window = XCreateSimpleWindow(x11->display, x11->root_window,
125 0, 0, 1, 1, 0, 0, 0);
126 if (x11->verbose)
127 fprintf(x11->errfile, "Selection window: %u\n",
128 (unsigned int)x11->selection_window);
130 vdagent_x11_randr_init(x11);
132 if (XFixesQueryExtension(x11->display, &x11->xfixes_event_base, &i) &&
133 XFixesQueryVersion(x11->display, &major, &minor) && major >= 1) {
134 x11->has_xfixes = 1;
135 XFixesSelectSelectionInput(x11->display, x11->root_window,
136 x11->clipboard_atom,
137 XFixesSetSelectionOwnerNotifyMask|
138 XFixesSelectionWindowDestroyNotifyMask|
139 XFixesSelectionClientCloseNotifyMask);
140 XFixesSelectSelectionInput(x11->display, x11->root_window,
141 x11->clipboard_primary_atom,
142 XFixesSetSelectionOwnerNotifyMask|
143 XFixesSelectionWindowDestroyNotifyMask|
144 XFixesSelectionClientCloseNotifyMask);
145 } else
146 fprintf(x11->errfile,
147 "no xfixes, no guest -> client copy paste support\n");
149 x11->max_prop_size = XExtendedMaxRequestSize(x11->display);
150 if (x11->max_prop_size) {
151 x11->max_prop_size -= 100;
152 } else {
153 x11->max_prop_size = XMaxRequestSize(x11->display) - 100;
155 /* Be a good X11 citizen and maximize the amount of data we send at once */
156 if (x11->max_prop_size > 262144)
157 x11->max_prop_size = 262144;
159 /* Catch resolution changes */
160 XSelectInput(x11->display, x11->root_window, StructureNotifyMask);
162 /* Get the current resolution */
163 XGetWindowAttributes(x11->display, x11->root_window, &attrib);
164 x11->width = attrib.width;
165 x11->height = attrib.height;
166 vdagent_x11_send_daemon_guest_xorg_res(x11);
168 /* Flush output buffers and consume any pending events */
169 vdagent_x11_do_read(x11);
171 return x11;
174 void vdagent_x11_destroy(struct vdagent_x11 *x11)
176 uint8_t sel;
178 if (!x11)
179 return;
181 for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) {
182 vdagent_x11_set_clipboard_owner(x11, sel, owner_none);
185 XCloseDisplay(x11->display);
186 free(x11);
189 int vdagent_x11_get_fd(struct vdagent_x11 *x11)
191 return x11->fd;
194 static void vdagent_x11_next_selection_request(struct vdagent_x11 *x11)
196 struct vdagent_x11_selection_request *selection_request;
197 selection_request = x11->selection_req;
198 x11->selection_req = selection_request->next;
199 free(selection_request);
202 static void vdagent_x11_next_conversion_request(struct vdagent_x11 *x11)
204 struct vdagent_x11_conversion_request *conversion_req;
205 conversion_req = x11->conversion_req;
206 x11->conversion_req = conversion_req->next;
207 free(conversion_req);
210 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11,
211 uint8_t selection, int new_owner)
213 struct vdagent_x11_selection_request *prev_sel, *curr_sel, *next_sel;
214 struct vdagent_x11_conversion_request *prev_conv, *curr_conv, *next_conv;
215 int once;
217 /* Clear pending requests and clipboard data */
218 once = 1;
219 prev_sel = NULL;
220 next_sel = x11->selection_req;
221 while (next_sel) {
222 curr_sel = next_sel;
223 next_sel = curr_sel->next;
224 if (curr_sel->selection == selection) {
225 if (once) {
226 SELPRINTF("selection requests pending on clipboard ownership "
227 "change, clearing\n");
228 once = 0;
230 vdagent_x11_send_selection_notify(x11, None, curr_sel);
231 if (curr_sel == x11->selection_req) {
232 x11->selection_req = next_sel;
233 free(x11->selection_req_data);
234 x11->selection_req_data = NULL;
235 x11->selection_req_data_pos = 0;
236 x11->selection_req_data_size = 0;
237 x11->selection_req_atom = None;
238 } else {
239 prev_sel->next = next_sel;
241 free(curr_sel);
242 } else {
243 prev_sel = curr_sel;
247 once = 1;
248 prev_conv = NULL;
249 next_conv = x11->conversion_req;
250 while (next_conv) {
251 curr_conv = next_conv;
252 next_conv = curr_conv->next;
253 if (curr_conv->selection == selection) {
254 if (once) {
255 SELPRINTF("client clipboard request pending on clipboard "
256 "ownership change, clearing\n");
257 once = 0;
259 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, selection,
260 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
261 if (curr_conv == x11->conversion_req) {
262 x11->conversion_req = next_conv;
263 x11->clipboard_data_size = 0;
264 x11->expect_property_notify = 0;
265 } else {
266 prev_conv->next = next_conv;
268 free(curr_conv);
269 } else {
270 prev_conv = curr_conv;
274 if (new_owner == owner_none) {
275 /* When going from owner_guest to owner_none we need to send a
276 clipboard release message to the client */
277 if (x11->clipboard_owner[selection] == owner_guest) {
278 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_RELEASE, selection,
279 0, NULL, 0);
281 x11->clipboard_type_count[selection] = 0;
283 x11->clipboard_owner[selection] = new_owner;
286 static int vdagent_x11_get_clipboard_atom(struct vdagent_x11 *x11, uint8_t selection, Atom* clipboard)
288 if (selection == VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) {
289 *clipboard = x11->clipboard_atom;
290 } else if (selection == VD_AGENT_CLIPBOARD_SELECTION_PRIMARY) {
291 *clipboard = x11->clipboard_primary_atom;
292 } else {
293 fprintf(x11->errfile, "get_clipboard_atom: unknown selection\n");
294 return -1;
297 return 0;
300 static int vdagent_x11_get_clipboard_selection(struct vdagent_x11 *x11,
301 XEvent *event, uint8_t *selection)
303 Atom atom;
305 if (event->type == x11->xfixes_event_base) {
306 XFixesSelectionNotifyEvent *xfev = (XFixesSelectionNotifyEvent *)event;
307 atom = xfev->selection;
308 } else if (event->type == SelectionNotify) {
309 atom = event->xselection.selection;
310 } else if (event->type == SelectionRequest) {
311 atom = event->xselectionrequest.selection;
312 } else {
313 fprintf(x11->errfile, "get_clipboard_selection: unknown event type\n");
314 return -1;
317 if (atom == x11->clipboard_atom) {
318 *selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
319 } else if (atom == x11->clipboard_primary_atom) {
320 *selection = VD_AGENT_CLIPBOARD_SELECTION_PRIMARY;
321 } else {
322 fprintf(x11->errfile, "get_clipboard_selection: unknown selection\n");
323 return -1;
326 return 0;
329 static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
331 int handled = 0;
332 uint8_t selection;
334 if (event.type == x11->xfixes_event_base) {
335 union {
336 XEvent ev;
337 XFixesSelectionNotifyEvent xfev;
338 } ev;
340 if (vdagent_x11_get_clipboard_selection(x11, &event, &selection)) {
341 return;
344 ev.ev = event;
345 switch (ev.xfev.subtype) {
346 case XFixesSetSelectionOwnerNotify:
347 break;
348 /* Treat ... as a SelectionOwnerNotify None */
349 case XFixesSelectionWindowDestroyNotify:
350 case XFixesSelectionClientCloseNotify:
351 ev.xfev.owner = None;
352 break;
353 default:
354 VSELPRINTF("unexpected xfix event subtype %d window %d\n",
355 (int)ev.xfev.subtype, (int)event.xany.window);
356 return;
358 VSELPRINTF("New selection owner: %u\n", (unsigned int)ev.xfev.owner);
360 /* Ignore becoming the owner ourselves */
361 if (ev.xfev.owner == x11->selection_window)
362 return;
364 /* If the clipboard owner is changed we no longer own it */
365 vdagent_x11_set_clipboard_owner(x11, selection, owner_none);
367 if (ev.xfev.owner == None)
368 return;
370 /* Request the supported targets from the new owner */
371 XConvertSelection(x11->display, ev.xfev.selection, x11->targets_atom,
372 x11->targets_atom, x11->selection_window,
373 CurrentTime);
374 x11->expected_targets_notifies[selection]++;
375 return;
378 switch (event.type) {
379 case ConfigureNotify:
380 // TODO: handle CrtcConfigureNotify, OutputConfigureNotify can be ignored.
381 if (event.xconfigure.window != x11->root_window)
382 break;
384 handled = 1;
385 vdagent_x11_randr_handle_root_size_change(x11,
386 event.xconfigure.width, event.xconfigure.height);
387 break;
388 case MappingNotify:
389 /* These are uninteresting */
390 handled = 1;
391 break;
392 case SelectionNotify:
393 if (event.xselection.target == x11->targets_atom)
394 vdagent_x11_handle_targets_notify(x11, &event);
395 else
396 vdagent_x11_handle_selection_notify(x11, &event, 0);
398 handled = 1;
399 break;
400 case PropertyNotify:
401 if (x11->expect_property_notify &&
402 event.xproperty.state == PropertyNewValue) {
403 vdagent_x11_handle_selection_notify(x11, &event, 1);
405 if (x11->selection_req_data &&
406 event.xproperty.state == PropertyDelete) {
407 vdagent_x11_handle_property_delete_notify(x11, &event);
409 /* Always mark as handled, since we cannot unselect input for property
410 notifications once we are done with handling the incr transfer. */
411 handled = 1;
412 break;
413 case SelectionClear:
414 /* Do nothing the clipboard ownership will get updated through
415 the XFixesSetSelectionOwnerNotify event */
416 handled = 1;
417 break;
418 case SelectionRequest: {
419 struct vdagent_x11_selection_request *req, *new_req;
421 if (vdagent_x11_get_clipboard_selection(x11, &event, &selection)) {
422 return;
425 new_req = malloc(sizeof(*new_req));
426 if (!new_req) {
427 SELPRINTF("out of memory on SelectionRequest, ignoring.\n");
428 break;
431 handled = 1;
433 new_req->event = event;
434 new_req->selection = selection;
435 new_req->next = NULL;
437 if (!x11->selection_req) {
438 x11->selection_req = new_req;
439 vdagent_x11_handle_selection_request(x11);
440 break;
443 /* maybe we should limit the selection_request stack depth ? */
444 req = x11->selection_req;
445 while (req->next)
446 req = req->next;
448 req->next = new_req;
449 break;
452 if (!handled && x11->verbose)
453 fprintf(x11->errfile, "unhandled x11 event, type %d, window %d\n",
454 (int)event.type, (int)event.xany.window);
457 void vdagent_x11_do_read(struct vdagent_x11 *x11)
459 XEvent event;
461 while (XPending(x11->display)) {
462 XNextEvent(x11->display, &event);
463 vdagent_x11_handle_event(x11, event);
467 static const char *vdagent_x11_get_atom_name(struct vdagent_x11 *x11, Atom a)
469 if (a == None)
470 return "None";
472 return XGetAtomName(x11->display, a);
475 static int vdagent_x11_get_selection(struct vdagent_x11 *x11, XEvent *event,
476 uint8_t selection, Atom type, Atom prop, int format,
477 unsigned char **data_ret, int incr)
479 Bool del = incr ? True: False;
480 Atom type_ret;
481 int format_ret, ret_val = -1;
482 unsigned long len, remain;
483 unsigned char *data = NULL;
485 *data_ret = NULL;
487 if (!incr) {
488 if (event->xselection.property == None) {
489 VSELPRINTF("XConvertSelection refused by clipboard owner\n");
490 goto exit;
493 if (event->xselection.requestor != x11->selection_window ||
494 event->xselection.property != prop) {
495 SELPRINTF("SelectionNotify parameters mismatch\n");
496 goto exit;
500 if (XGetWindowProperty(x11->display, x11->selection_window, prop, 0,
501 LONG_MAX, del, type, &type_ret, &format_ret, &len,
502 &remain, &data) != Success) {
503 SELPRINTF("XGetWindowProperty failed\n");
504 goto exit;
507 if (!incr && prop != x11->targets_atom) {
508 if (type_ret == x11->incr_atom) {
509 int prop_min_size = *(uint32_t*)data;
511 if (x11->expect_property_notify) {
512 SELPRINTF("received an incr SelectionNotify while "
513 "still reading another incr property\n");
514 goto exit;
517 if (x11->clipboard_data_space < prop_min_size) {
518 free(x11->clipboard_data);
519 x11->clipboard_data = malloc(prop_min_size);
520 if (!x11->clipboard_data) {
521 SELPRINTF("out of memory allocating clipboard buffer\n");
522 x11->clipboard_data_space = 0;
523 goto exit;
525 x11->clipboard_data_space = prop_min_size;
527 x11->expect_property_notify = 1;
528 XSelectInput(x11->display, x11->selection_window,
529 PropertyChangeMask);
530 XDeleteProperty(x11->display, x11->selection_window, prop);
531 XFree(data);
532 return 0; /* Wait for more data */
534 XDeleteProperty(x11->display, x11->selection_window, prop);
537 if (type_ret != type) {
538 SELPRINTF("expected property type: %s, got: %s\n",
539 vdagent_x11_get_atom_name(x11, type),
540 vdagent_x11_get_atom_name(x11, type_ret));
541 goto exit;
544 if (format_ret != format) {
545 SELPRINTF("expected %d bit format, got %d bits\n", format, format_ret);
546 goto exit;
549 /* Convert len to bytes */
550 switch(format) {
551 case 8:
552 break;
553 case 16:
554 len *= sizeof(short);
555 break;
556 case 32:
557 len *= sizeof(long);
558 break;
561 if (incr) {
562 if (len) {
563 if (x11->clipboard_data_size + len > x11->clipboard_data_space) {
564 void *old_clipboard_data = x11->clipboard_data;
566 x11->clipboard_data_space = x11->clipboard_data_size + len;
567 x11->clipboard_data = realloc(x11->clipboard_data,
568 x11->clipboard_data_space);
569 if (!x11->clipboard_data) {
570 SELPRINTF("out of memory allocating clipboard buffer\n");
571 x11->clipboard_data_space = 0;
572 free(old_clipboard_data);
573 goto exit;
576 memcpy(x11->clipboard_data + x11->clipboard_data_size, data, len);
577 x11->clipboard_data_size += len;
578 VSELPRINTF("Appended %ld bytes to buffer\n", len);
579 XFree(data);
580 return 0; /* Wait for more data */
582 len = x11->clipboard_data_size;
583 *data_ret = x11->clipboard_data;
584 } else
585 *data_ret = data;
587 if (len > 0) {
588 ret_val = len;
589 } else {
590 SELPRINTF("property contains no data (zero length)\n");
591 *data_ret = NULL;
594 exit:
595 if ((incr || ret_val == -1) && data)
596 XFree(data);
598 if (incr) {
599 x11->clipboard_data_size = 0;
600 x11->expect_property_notify = 0;
603 return ret_val;
606 static void vdagent_x11_get_selection_free(struct vdagent_x11 *x11,
607 unsigned char *data, int incr)
609 if (incr) {
610 /* If the clipboard has grown large return the memory to the system */
611 if (x11->clipboard_data_space > 512 * 1024) {
612 free(x11->clipboard_data);
613 x11->clipboard_data = NULL;
614 x11->clipboard_data_space = 0;
616 } else if (data)
617 XFree(data);
620 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11 *x11,
621 uint8_t selection, Atom target)
623 int i, j;
625 for (i = 0; i < clipboard_format_count; i++) {
626 for (j = 0; j < x11->clipboard_formats[i].atom_count; i++) {
627 if (x11->clipboard_formats[i].atoms[j] == target) {
628 return x11->clipboard_formats[i].type;
633 SELPRINTF("unexpected selection type %s\n",
634 vdagent_x11_get_atom_name(x11, target));
635 return VD_AGENT_CLIPBOARD_NONE;
638 static Atom vdagent_x11_type_to_target(struct vdagent_x11 *x11,
639 uint8_t selection, uint32_t type)
641 int i;
643 for (i = 0; i < x11->clipboard_type_count[selection]; i++) {
644 if (x11->clipboard_agent_types[selection][i] == type) {
645 return x11->clipboard_x11_targets[selection][i];
648 SELPRINTF("client requested unavailable type %u\n", type);
649 return None;
652 static void vdagent_x11_handle_conversion_request(struct vdagent_x11 *x11)
654 Atom clip;
656 if (!x11->conversion_req) {
657 return;
660 vdagent_x11_get_clipboard_atom(x11, x11->conversion_req->selection, &clip);
661 XConvertSelection(x11->display, clip, x11->conversion_req->target,
662 clip, x11->selection_window, CurrentTime);
665 static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
666 XEvent *event, int incr)
668 int len = 0;
669 unsigned char *data = NULL;
670 uint32_t type;
671 uint8_t selection = -1;
672 Atom clip;
674 if (!x11->conversion_req) {
675 fprintf(x11->errfile, "SelectionNotify received without a target\n");
676 return;
678 vdagent_x11_get_clipboard_atom(x11, x11->conversion_req->selection, &clip);
680 if (incr) {
681 if (event->xproperty.atom != clip ||
682 event->xproperty.window != x11->selection_window) {
683 return;
685 } else {
686 if (vdagent_x11_get_clipboard_selection(x11, event, &selection)) {
687 len = -1;
688 } else if (selection != x11->conversion_req->selection) {
689 SELPRINTF("Requested data for selection %d got %d\n",
690 (int)x11->conversion_req->selection, (int)selection);
691 len = -1;
693 if (event->xselection.target != x11->conversion_req->target &&
694 event->xselection.target != x11->incr_atom) {
695 SELPRINTF("Requested %s target got %s\n",
696 vdagent_x11_get_atom_name(x11, x11->conversion_req->target),
697 vdagent_x11_get_atom_name(x11, event->xselection.target));
698 len = -1;
702 selection = x11->conversion_req->selection;
703 type = vdagent_x11_target_to_type(x11, selection,
704 x11->conversion_req->target);
705 if (len == 0) { /* No errors so far */
706 len = vdagent_x11_get_selection(x11, event, selection,
707 x11->conversion_req->target,
708 clip, 8, &data, incr);
709 if (len == 0) { /* waiting for more data? */
710 return;
713 if (len == -1) {
714 type = VD_AGENT_CLIPBOARD_NONE;
715 len = 0;
718 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, selection, type,
719 data, len);
720 vdagent_x11_get_selection_free(x11, data, incr);
722 vdagent_x11_next_conversion_request(x11);
723 vdagent_x11_handle_conversion_request(x11);
726 static Atom atom_lists_overlap(Atom *atoms1, Atom *atoms2, int l1, int l2)
728 int i, j;
730 for (i = 0; i < l1; i++)
731 for (j = 0; j < l2; j++)
732 if (atoms1[i] == atoms2[j])
733 return atoms1[i];
735 return 0;
738 static void vdagent_x11_print_targets(struct vdagent_x11 *x11,
739 uint8_t selection, const char *action, Atom *atoms, int c)
741 int i;
742 VSELPRINTF("%s %d targets:\n", action, c);
743 for (i = 0; i < c; i++)
744 VSELPRINTF("%s\n", vdagent_x11_get_atom_name(x11, atoms[i]));
747 static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
748 XEvent *event)
750 int i, len;
751 Atom atom, *atoms = NULL;
752 uint8_t selection;
753 int *type_count;
755 if (vdagent_x11_get_clipboard_selection(x11, event, &selection)) {
756 return;
759 if (!x11->expected_targets_notifies[selection]) {
760 SELPRINTF("unexpected selection notify TARGETS\n");
761 return;
764 x11->expected_targets_notifies[selection]--;
766 /* If we have more targets_notifies pending, ignore this one, we
767 are only interested in the targets list of the current owner
768 (which is the last one we've requested a targets list from) */
769 if (x11->expected_targets_notifies[selection]) {
770 return;
773 len = vdagent_x11_get_selection(x11, event, selection,
774 XA_ATOM, x11->targets_atom, 32,
775 (unsigned char **)&atoms, 0);
776 if (len == 0 || len == -1) /* waiting for more data or error? */
777 return;
779 /* bytes -> atoms */
780 len /= sizeof(Atom);
781 vdagent_x11_print_targets(x11, selection, "received", atoms, len);
783 type_count = &x11->clipboard_type_count[selection];
784 *type_count = 0;
785 for (i = 0; i < clipboard_format_count; i++) {
786 atom = atom_lists_overlap(x11->clipboard_formats[i].atoms, atoms,
787 x11->clipboard_formats[i].atom_count, len);
788 if (atom) {
789 x11->clipboard_agent_types[selection][*type_count] =
790 x11->clipboard_formats[i].type;
791 x11->clipboard_x11_targets[selection][*type_count] = atom;
792 (*type_count)++;
793 if (*type_count ==
794 sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t)) {
795 SELPRINTF("handle_targets_notify: too many types\n");
796 break;
801 if (*type_count) {
802 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_GRAB, selection, 0,
803 (uint8_t *)x11->clipboard_agent_types[selection],
804 *type_count * sizeof(uint32_t));
805 vdagent_x11_set_clipboard_owner(x11, selection, owner_guest);
808 vdagent_x11_get_selection_free(x11, (unsigned char *)atoms, 0);
811 static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
812 Atom prop, struct vdagent_x11_selection_request *request)
814 XEvent res, *event;
816 if (request) {
817 event = &request->event;
818 } else {
819 event = &x11->selection_req->event;
822 res.xselection.property = prop;
823 res.xselection.type = SelectionNotify;
824 res.xselection.display = event->xselectionrequest.display;
825 res.xselection.requestor = event->xselectionrequest.requestor;
826 res.xselection.selection = event->xselectionrequest.selection;
827 res.xselection.target = event->xselectionrequest.target;
828 res.xselection.time = event->xselectionrequest.time;
829 XSendEvent(x11->display, event->xselectionrequest.requestor, 0, 0, &res);
831 if (!request) {
832 vdagent_x11_next_selection_request(x11);
833 vdagent_x11_handle_selection_request(x11);
837 static void vdagent_x11_send_targets(struct vdagent_x11 *x11,
838 uint8_t selection, XEvent *event)
840 Atom prop, targets[256] = { x11->targets_atom, };
841 int i, j, k, target_count = 1;
843 for (i = 0; i < x11->clipboard_type_count[selection]; i++) {
844 for (j = 0; j < clipboard_format_count; j++) {
845 if (x11->clipboard_formats[j].type !=
846 x11->clipboard_agent_types[selection][i])
847 continue;
849 for (k = 0; k < x11->clipboard_formats[j].atom_count; k++) {
850 targets[target_count] = x11->clipboard_formats[j].atoms[k];
851 target_count++;
852 if (target_count == sizeof(targets)/sizeof(Atom)) {
853 SELPRINTF("send_targets: too many targets\n");
854 goto exit_loop;
859 exit_loop:
861 prop = event->xselectionrequest.property;
862 if (prop == None)
863 prop = event->xselectionrequest.target;
865 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
866 XA_ATOM, 32, PropModeReplace, (unsigned char *)&targets,
867 target_count);
868 vdagent_x11_print_targets(x11, selection, "sent", targets, target_count);
869 vdagent_x11_send_selection_notify(x11, prop, NULL);
872 static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11)
874 XEvent *event;
875 uint32_t type = VD_AGENT_CLIPBOARD_NONE;
876 uint8_t selection;
878 if (!x11->selection_req)
879 return;
881 event = &x11->selection_req->event;
882 selection = x11->selection_req->selection;
884 if (x11->clipboard_owner[selection] != owner_client) {
885 SELPRINTF("received selection request event for target %s, "
886 "while not owning client clipboard\n",
887 vdagent_x11_get_atom_name(x11, event->xselectionrequest.target));
888 vdagent_x11_send_selection_notify(x11, None, NULL);
889 return;
892 if (event->xselectionrequest.target == x11->multiple_atom) {
893 SELPRINTF("multiple target not supported\n");
894 vdagent_x11_send_selection_notify(x11, None, NULL);
895 return;
898 if (event->xselectionrequest.target == x11->targets_atom) {
899 vdagent_x11_send_targets(x11, selection, event);
900 return;
903 type = vdagent_x11_target_to_type(x11, selection,
904 event->xselectionrequest.target);
905 if (type == VD_AGENT_CLIPBOARD_NONE) {
906 vdagent_x11_send_selection_notify(x11, None, NULL);
907 return;
910 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_REQUEST, selection, type,
911 NULL, 0);
914 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11,
915 XEvent *del_event)
917 XEvent *sel_event;
918 int len;
919 uint8_t selection;
921 assert(x11->selection_req);
922 sel_event = &x11->selection_req->event;
923 selection = x11->selection_req->selection;
924 if (del_event->xproperty.window != sel_event->xselectionrequest.requestor
925 || del_event->xproperty.atom != x11->selection_req_atom) {
926 return;
929 len = x11->selection_req_data_size - x11->selection_req_data_pos;
930 if (len > x11->max_prop_size) {
931 len = x11->max_prop_size;
934 if (len) {
935 VSELPRINTF("Sending %d-%d/%d bytes of clipboard data\n",
936 x11->selection_req_data_pos,
937 x11->selection_req_data_pos + len - 1,
938 x11->selection_req_data_size);
939 } else {
940 VSELPRINTF("Ending incr send of clipboard data\n");
942 XChangeProperty(x11->display, sel_event->xselectionrequest.requestor,
943 x11->selection_req_atom,
944 sel_event->xselectionrequest.target, 8, PropModeReplace,
945 x11->selection_req_data + x11->selection_req_data_pos,
946 len);
947 x11->selection_req_data_pos += len;
949 /* Note we must explictly send a 0 sized XChangeProperty to signal the
950 incr transfer is done. Hence we do not check if we've send all data
951 but instead check we've send the final 0 sized XChangeProperty. */
952 if (len == 0) {
953 free(x11->selection_req_data);
954 x11->selection_req_data = NULL;
955 x11->selection_req_data_pos = 0;
956 x11->selection_req_data_size = 0;
957 x11->selection_req_atom = None;
958 vdagent_x11_next_selection_request(x11);
959 vdagent_x11_handle_selection_request(x11);
963 void vdagent_x11_clipboard_request(struct vdagent_x11 *x11,
964 uint8_t selection, uint32_t type)
966 Atom target, clip;
967 struct vdagent_x11_conversion_request *req, *new_req;
969 /* We don't use clip here, but we call get_clipboard_atom to verify
970 selection is valid */
971 if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) {
972 goto none;
975 if (x11->clipboard_owner[selection] != owner_guest) {
976 SELPRINTF("received clipboard req while not owning guest clipboard\n");
977 goto none;
980 target = vdagent_x11_type_to_target(x11, selection, type);
981 if (target == None) {
982 goto none;
985 new_req = malloc(sizeof(*new_req));
986 if (!new_req) {
987 SELPRINTF("out of memory on client clipboard request, ignoring.\n");
988 return;
991 new_req->target = target;
992 new_req->selection = selection;
993 new_req->next = NULL;
995 if (!x11->conversion_req) {
996 x11->conversion_req = new_req;
997 vdagent_x11_handle_conversion_request(x11);
998 /* Flush output buffers and consume any pending events */
999 vdagent_x11_do_read(x11);
1000 return;
1003 /* maybe we should limit the conversion_request stack depth ? */
1004 req = x11->conversion_req;
1005 while (req->next)
1006 req = req->next;
1008 req->next = new_req;
1009 return;
1011 none:
1012 udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
1013 selection, VD_AGENT_CLIPBOARD_NONE, NULL, 0);
1016 void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint8_t selection,
1017 uint32_t *types, uint32_t type_count)
1019 Atom clip;
1021 if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) {
1022 return;
1025 if (type_count > sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t)) {
1026 SELPRINTF("x11_clipboard_grab: too many types\n");
1027 type_count = sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t);
1030 memcpy(x11->clipboard_agent_types[selection], types,
1031 type_count * sizeof(uint32_t));
1032 x11->clipboard_type_count[selection] = type_count;
1034 XSetSelectionOwner(x11->display, clip,
1035 x11->selection_window, CurrentTime);
1036 vdagent_x11_set_clipboard_owner(x11, selection, owner_client);
1038 /* Flush output buffers and consume any pending events */
1039 vdagent_x11_do_read(x11);
1042 void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint8_t selection,
1043 uint32_t type, uint8_t *data, uint32_t size)
1045 Atom prop;
1046 XEvent *event;
1047 uint32_t type_from_event;
1049 if (x11->selection_req_data) {
1050 if (type || size) {
1051 SELPRINTF("received clipboard data while still sending"
1052 " data from previous request, ignoring\n");
1054 free(data);
1055 return;
1058 if (!x11->selection_req) {
1059 if (type || size) {
1060 SELPRINTF("received clipboard data without an "
1061 "outstanding selection request, ignoring\n");
1063 free(data);
1064 return;
1067 event = &x11->selection_req->event;
1068 type_from_event = vdagent_x11_target_to_type(x11,
1069 x11->selection_req->selection,
1070 event->xselectionrequest.target);
1071 if (type_from_event != type ||
1072 selection != x11->selection_req->selection) {
1073 if (selection != x11->selection_req->selection) {
1074 SELPRINTF("expecting data for selection %d got %d\n",
1075 (int)x11->selection_req->selection, (int)selection);
1077 if (type_from_event != type) {
1078 SELPRINTF("expecting type %u clipboard data got %u\n",
1079 type_from_event, type);
1081 vdagent_x11_send_selection_notify(x11, None, NULL);
1082 free(data);
1084 /* Flush output buffers and consume any pending events */
1085 vdagent_x11_do_read(x11);
1086 return;
1089 prop = event->xselectionrequest.property;
1090 if (prop == None)
1091 prop = event->xselectionrequest.target;
1093 if (size > x11->max_prop_size) {
1094 unsigned long len = size;
1095 VSELPRINTF("Starting incr send of clipboard data\n");
1096 x11->selection_req_data = data;
1097 x11->selection_req_data_pos = 0;
1098 x11->selection_req_data_size = size;
1099 x11->selection_req_atom = prop;
1100 XSelectInput(x11->display, event->xselectionrequest.requestor,
1101 PropertyChangeMask);
1102 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
1103 x11->incr_atom, 32, PropModeReplace,
1104 (unsigned char*)&len, 1);
1105 vdagent_x11_send_selection_notify(x11, prop, x11->selection_req);
1106 } else {
1107 XChangeProperty(x11->display, event->xselectionrequest.requestor, prop,
1108 event->xselectionrequest.target, 8, PropModeReplace,
1109 data, size);
1110 vdagent_x11_send_selection_notify(x11, prop, NULL);
1111 free(data);
1114 /* Flush output buffers and consume any pending events */
1115 vdagent_x11_do_read(x11);
1118 void vdagent_x11_clipboard_release(struct vdagent_x11 *x11, uint8_t selection)
1120 XEvent event;
1121 Atom clip;
1123 if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) {
1124 return;
1127 if (x11->clipboard_owner[selection] != owner_client) {
1128 SELPRINTF("received release while not owning client clipboard\n");
1129 return;
1132 XSetSelectionOwner(x11->display, clip, None, CurrentTime);
1133 /* Make sure we process the XFixesSetSelectionOwnerNotify event caused
1134 by this, so we don't end up changing the clipboard owner to none, after
1135 it has already been re-owned because this event is still pending. */
1136 XSync(x11->display, False);
1137 while (XCheckTypedEvent(x11->display, x11->xfixes_event_base,
1138 &event))
1139 vdagent_x11_handle_event(x11, event);
1141 /* Note no need to do a set_clipboard_owner(owner_none) here, as that is
1142 already done by processing the XFixesSetSelectionOwnerNotify event. */
1144 /* Flush output buffers and consume any pending events */
1145 vdagent_x11_do_read(x11);