1 /* vdagent-x11.c vdagent x11 code
3 Copyright 2010-2011 Red Hat, Inc.
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. */
38 #include <X11/Xatom.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
,
50 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11
*x11
,
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
) {
59 case VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD
:
61 case VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
:
63 case VD_AGENT_CLIPBOARD_SELECTION_SECONDARY
:
70 static int debug_error_handler(Display
*display
, XErrorEvent
*error
)
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
));
84 fprintf(errfile
, "out of memory allocating vdagent_x11 struct\n");
88 x11
->vdagentd
= vdagentd
;
89 x11
->errfile
= errfile
;
90 x11
->verbose
= verbose
;
92 x11
->display
= XOpenDisplay(NULL
);
94 fprintf(x11
->errfile
, "could not connect to X-server\n");
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
],
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);
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) {
135 XFixesSelectSelectionInput(x11
->display
, x11
->root_window
,
137 XFixesSetSelectionOwnerNotifyMask
|
138 XFixesSelectionWindowDestroyNotifyMask
|
139 XFixesSelectionClientCloseNotifyMask
);
140 XFixesSelectSelectionInput(x11
->display
, x11
->root_window
,
141 x11
->clipboard_primary_atom
,
142 XFixesSetSelectionOwnerNotifyMask
|
143 XFixesSelectionWindowDestroyNotifyMask
|
144 XFixesSelectionClientCloseNotifyMask
);
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;
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
);
174 void vdagent_x11_destroy(struct vdagent_x11
*x11
)
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
);
189 int vdagent_x11_get_fd(struct vdagent_x11
*x11
)
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
;
217 /* Clear pending requests and clipboard data */
220 next_sel
= x11
->selection_req
;
223 next_sel
= curr_sel
->next
;
224 if (curr_sel
->selection
== selection
) {
226 SELPRINTF("selection requests pending on clipboard ownership "
227 "change, clearing\n");
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
;
239 prev_sel
->next
= next_sel
;
249 next_conv
= x11
->conversion_req
;
251 curr_conv
= next_conv
;
252 next_conv
= curr_conv
->next
;
253 if (curr_conv
->selection
== selection
) {
255 SELPRINTF("client clipboard request pending on clipboard "
256 "ownership change, clearing\n");
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;
266 prev_conv
->next
= next_conv
;
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
,
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
;
293 fprintf(x11
->errfile
, "get_clipboard_atom: unknown selection\n");
300 static int vdagent_x11_get_clipboard_selection(struct vdagent_x11
*x11
,
301 XEvent
*event
, uint8_t *selection
)
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
;
313 fprintf(x11
->errfile
, "get_clipboard_selection: unknown event type\n");
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
;
322 fprintf(x11
->errfile
, "get_clipboard_selection: unknown selection\n");
329 static void vdagent_x11_handle_event(struct vdagent_x11
*x11
, XEvent event
)
334 if (event
.type
== x11
->xfixes_event_base
) {
337 XFixesSelectionNotifyEvent xfev
;
340 if (vdagent_x11_get_clipboard_selection(x11
, &event
, &selection
)) {
345 switch (ev
.xfev
.subtype
) {
346 case XFixesSetSelectionOwnerNotify
:
348 /* Treat ... as a SelectionOwnerNotify None */
349 case XFixesSelectionWindowDestroyNotify
:
350 case XFixesSelectionClientCloseNotify
:
351 ev
.xfev
.owner
= None
;
354 VSELPRINTF("unexpected xfix event subtype %d window %d\n",
355 (int)ev
.xfev
.subtype
, (int)event
.xany
.window
);
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
)
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
)
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
,
374 x11
->expected_targets_notifies
[selection
]++;
378 switch (event
.type
) {
379 case ConfigureNotify
:
380 // TODO: handle CrtcConfigureNotify, OutputConfigureNotify can be ignored.
381 if (event
.xconfigure
.window
!= x11
->root_window
)
385 vdagent_x11_randr_handle_root_size_change(x11
,
386 event
.xconfigure
.width
, event
.xconfigure
.height
);
389 /* These are uninteresting */
392 case SelectionNotify
:
393 if (event
.xselection
.target
== x11
->targets_atom
)
394 vdagent_x11_handle_targets_notify(x11
, &event
);
396 vdagent_x11_handle_selection_notify(x11
, &event
, 0);
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. */
414 /* Do nothing the clipboard ownership will get updated through
415 the XFixesSetSelectionOwnerNotify event */
418 case SelectionRequest
: {
419 struct vdagent_x11_selection_request
*req
, *new_req
;
421 if (vdagent_x11_get_clipboard_selection(x11
, &event
, &selection
)) {
425 new_req
= malloc(sizeof(*new_req
));
427 SELPRINTF("out of memory on SelectionRequest, ignoring.\n");
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
);
443 /* maybe we should limit the selection_request stack depth ? */
444 req
= x11
->selection_req
;
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
)
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
)
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
;
481 int format_ret
, ret_val
= -1;
482 unsigned long len
, remain
;
483 unsigned char *data
= NULL
;
488 if (event
->xselection
.property
== None
) {
489 VSELPRINTF("XConvertSelection refused by clipboard owner\n");
493 if (event
->xselection
.requestor
!= x11
->selection_window
||
494 event
->xselection
.property
!= prop
) {
495 SELPRINTF("SelectionNotify parameters mismatch\n");
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");
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");
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;
525 x11
->clipboard_data_space
= prop_min_size
;
527 x11
->expect_property_notify
= 1;
528 XSelectInput(x11
->display
, x11
->selection_window
,
530 XDeleteProperty(x11
->display
, x11
->selection_window
, prop
);
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
));
544 if (format_ret
!= format
) {
545 SELPRINTF("expected %d bit format, got %d bits\n", format
, format_ret
);
549 /* Convert len to bytes */
554 len
*= sizeof(short);
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
);
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
);
580 return 0; /* Wait for more data */
582 len
= x11
->clipboard_data_size
;
583 *data_ret
= x11
->clipboard_data
;
590 SELPRINTF("property contains no data (zero length)\n");
595 if ((incr
|| ret_val
== -1) && data
)
599 x11
->clipboard_data_size
= 0;
600 x11
->expect_property_notify
= 0;
606 static void vdagent_x11_get_selection_free(struct vdagent_x11
*x11
,
607 unsigned char *data
, int 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;
620 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11
*x11
,
621 uint8_t selection
, Atom target
)
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
)
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
);
652 static void vdagent_x11_handle_conversion_request(struct vdagent_x11
*x11
)
656 if (!x11
->conversion_req
) {
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
)
669 unsigned char *data
= NULL
;
671 uint8_t selection
= -1;
674 if (!x11
->conversion_req
) {
675 fprintf(x11
->errfile
, "SelectionNotify received without a target\n");
678 vdagent_x11_get_clipboard_atom(x11
, x11
->conversion_req
->selection
, &clip
);
681 if (event
->xproperty
.atom
!= clip
||
682 event
->xproperty
.window
!= x11
->selection_window
) {
686 if (vdagent_x11_get_clipboard_selection(x11
, event
, &selection
)) {
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
);
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
));
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? */
714 type
= VD_AGENT_CLIPBOARD_NONE
;
718 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_DATA
, selection
, type
,
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
)
730 for (i
= 0; i
< l1
; i
++)
731 for (j
= 0; j
< l2
; j
++)
732 if (atoms1
[i
] == atoms2
[j
])
738 static void vdagent_x11_print_targets(struct vdagent_x11
*x11
,
739 uint8_t selection
, const char *action
, Atom
*atoms
, int c
)
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
,
751 Atom atom
, *atoms
= NULL
;
755 if (vdagent_x11_get_clipboard_selection(x11
, event
, &selection
)) {
759 if (!x11
->expected_targets_notifies
[selection
]) {
760 SELPRINTF("unexpected selection notify TARGETS\n");
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
]) {
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? */
781 vdagent_x11_print_targets(x11
, selection
, "received", atoms
, len
);
783 type_count
= &x11
->clipboard_type_count
[selection
];
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
);
789 x11
->clipboard_agent_types
[selection
][*type_count
] =
790 x11
->clipboard_formats
[i
].type
;
791 x11
->clipboard_x11_targets
[selection
][*type_count
] = atom
;
794 sizeof(x11
->clipboard_agent_types
[0])/sizeof(uint32_t)) {
795 SELPRINTF("handle_targets_notify: too many types\n");
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
)
817 event
= &request
->event
;
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
);
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
])
849 for (k
= 0; k
< x11
->clipboard_formats
[j
].atom_count
; k
++) {
850 targets
[target_count
] = x11
->clipboard_formats
[j
].atoms
[k
];
852 if (target_count
== sizeof(targets
)/sizeof(Atom
)) {
853 SELPRINTF("send_targets: too many targets\n");
861 prop
= event
->xselectionrequest
.property
;
863 prop
= event
->xselectionrequest
.target
;
865 XChangeProperty(x11
->display
, event
->xselectionrequest
.requestor
, prop
,
866 XA_ATOM
, 32, PropModeReplace
, (unsigned char *)&targets
,
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
)
875 uint32_t type
= VD_AGENT_CLIPBOARD_NONE
;
878 if (!x11
->selection_req
)
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
);
892 if (event
->xselectionrequest
.target
== x11
->multiple_atom
) {
893 SELPRINTF("multiple target not supported\n");
894 vdagent_x11_send_selection_notify(x11
, None
, NULL
);
898 if (event
->xselectionrequest
.target
== x11
->targets_atom
) {
899 vdagent_x11_send_targets(x11
, selection
, event
);
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
);
910 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_REQUEST
, selection
, type
,
914 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11
*x11
,
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
) {
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
;
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
);
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
,
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. */
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
)
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
)) {
975 if (x11
->clipboard_owner
[selection
] != owner_guest
) {
976 SELPRINTF("received clipboard req while not owning guest clipboard\n");
980 target
= vdagent_x11_type_to_target(x11
, selection
, type
);
981 if (target
== None
) {
985 new_req
= malloc(sizeof(*new_req
));
987 SELPRINTF("out of memory on client clipboard request, ignoring.\n");
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
);
1003 /* maybe we should limit the conversion_request stack depth ? */
1004 req
= x11
->conversion_req
;
1008 req
->next
= new_req
;
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
)
1021 if (vdagent_x11_get_clipboard_atom(x11
, selection
, &clip
)) {
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
)
1047 uint32_t type_from_event
;
1049 if (x11
->selection_req_data
) {
1051 SELPRINTF("received clipboard data while still sending"
1052 " data from previous request, ignoring\n");
1058 if (!x11
->selection_req
) {
1060 SELPRINTF("received clipboard data without an "
1061 "outstanding selection request, ignoring\n");
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
);
1084 /* Flush output buffers and consume any pending events */
1085 vdagent_x11_do_read(x11
);
1089 prop
= event
->xselectionrequest
.property
;
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
);
1107 XChangeProperty(x11
->display
, event
->xselectionrequest
.requestor
, prop
,
1108 event
->xselectionrequest
.target
, 8, PropModeReplace
,
1110 vdagent_x11_send_selection_notify(x11
, prop
, NULL
);
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
)
1123 if (vdagent_x11_get_clipboard_atom(x11
, selection
, &clip
)) {
1127 if (x11
->clipboard_owner
[selection
] != owner_client
) {
1128 SELPRINTF("received release while not owning client clipboard\n");
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
,
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
);