1 /* vdagent-x11.c vdagent x11 code
3 Copyright 2010 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/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
{
49 struct vdagent_x11_selection_request
*next
;
52 struct clipboard_format_tmpl
{
54 const char *atom_names
[16];
57 struct clipboard_format_info
{
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]))
76 struct clipboard_format_info clipboard_formats
[clipboard_format_count
];
79 Atom clipboard_primary_atom
;
84 Window selection_window
;
85 struct udscs_connection
*vdagentd
;
94 int xfixes_event_base
;
96 int expected_targets_notifies
;
97 int expect_property_notify
;
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
,
121 static void vdagent_x11_send_selection_notify(struct vdagent_x11
*x11
,
123 int process_next_req
);
124 static void vdagent_x11_set_clipboard_owner(struct vdagent_x11
*x11
,
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
));
136 fprintf(errfile
, "out of memory allocating vdagent_x11 struct\n");
140 x11
->vdagentd
= vdagentd
;
141 x11
->errfile
= errfile
;
142 x11
->verbose
= verbose
;
144 x11
->display
= XOpenDisplay(NULL
);
146 fprintf(x11
->errfile
, "could not connect to X-server\n");
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
],
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);
174 fprintf(x11
->errfile
, "Selection window: %u\n",
175 (unsigned int)x11
->selection_window
);
177 if (XRRQueryExtension(x11
->display
, &i
, &i
))
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) {
185 XFixesSelectSelectionInput(x11
->display
, x11
->root_window
,
187 XFixesSetSelectionOwnerNotifyMask
|
188 XFixesSelectionWindowDestroyNotifyMask
|
189 XFixesSelectionClientCloseNotifyMask
);
190 XFixesSelectSelectionInput(x11
->display
, x11
->root_window
,
191 x11
->clipboard_primary_atom
,
192 XFixesSetSelectionOwnerNotifyMask
|
193 XFixesSelectionWindowDestroyNotifyMask
|
194 XFixesSelectionClientCloseNotifyMask
);
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;
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
);
224 void vdagent_x11_destroy(struct vdagent_x11
*x11
)
229 vdagent_x11_set_clipboard_owner(x11
, owner_none
);
230 XCloseDisplay(x11
->display
);
234 int vdagent_x11_get_fd(struct vdagent_x11
*x11
)
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
,
257 /* Clear pending requests and clipboard data */
258 if (x11
->selection_request
) {
259 fprintf(x11
->errfile
,
260 "selection requests pending on clipboard ownership change, "
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
;
296 fprintf(x11
->errfile
, "selection_get_grab: unknown selection\n");
303 static int vdagent_x11_get_clipboard_selection(struct vdagent_x11
*x11
,
304 XEvent
*event
, uint8_t *selection
)
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
;
316 fprintf(x11
->errfile
, "get_clipboard_selection: unknown event type\n");
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
;
325 fprintf(x11
->errfile
, "get_clipboard_selection: unknown selection\n");
332 static void vdagent_x11_handle_event(struct vdagent_x11
*x11
, XEvent event
)
337 if (event
.type
== x11
->xfixes_event_base
) {
340 XFixesSelectionNotifyEvent xfev
;
344 switch (ev
.xfev
.subtype
) {
345 case XFixesSetSelectionOwnerNotify
:
347 /* Treat ... as a SelectionOwnerNotify None */
348 case XFixesSelectionWindowDestroyNotify
:
349 case XFixesSelectionClientCloseNotify
:
350 ev
.xfev
.owner
= None
;
354 fprintf(x11
->errfile
,
355 "unexpected xfix event subtype %d window %d\n",
356 (int)ev
.xfev
.subtype
, (int)event
.xany
.window
);
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
)
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
)
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
,
378 x11
->expected_targets_notifies
++;
382 switch (event
.type
) {
383 case ConfigureNotify
:
384 if (event
.xconfigure
.window
!= x11
->root_window
)
389 if (event
.xconfigure
.width
== x11
->width
&&
390 event
.xconfigure
.height
== x11
->height
)
393 x11
->width
= event
.xconfigure
.width
;
394 x11
->height
= event
.xconfigure
.height
;
396 vdagent_x11_send_daemon_guest_xorg_res(x11
);
398 case SelectionNotify
:
399 if (event
.xselection
.target
== x11
->targets_atom
)
400 vdagent_x11_handle_targets_notify(x11
, &event
, 0);
402 vdagent_x11_handle_selection_notify(x11
, &event
, 0);
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);
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. */
424 /* Do nothing the clipboard ownership will get updated through
425 the XFixesSetSelectionOwnerNotify event */
428 case SelectionRequest
: {
429 struct vdagent_x11_selection_request
*req
, *new_req
;
431 new_req
= malloc(sizeof(*new_req
));
433 fprintf(x11
->errfile
,
434 "out of memory on SelectionRequest, ignoring.\n");
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
);
449 /* maybe we should limit the selection_request stack depth ? */
450 req
= x11
->selection_request
;
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
)
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
)
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
;
498 int format_ret
, ret_val
= -1;
499 unsigned long len
, remain
;
500 unsigned char *data
= NULL
;
505 if (event
->xproperty
.atom
!= prop
) {
506 fprintf(x11
->errfile
, "PropertyNotify parameters mismatch\n");
510 if (event
->xselection
.property
== None
) {
512 fprintf(x11
->errfile
,
513 "XConvertSelection refused by clipboard owner\n");
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");
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");
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");
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;
552 x11
->clipboard_data_space
= prop_min_size
;
554 x11
->expect_property_notify
= 1;
555 XSelectInput(x11
->display
, x11
->selection_window
,
557 XDeleteProperty(x11
->display
, x11
->selection_window
, prop
);
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
));
571 if (format_ret
!= format
) {
572 fprintf(x11
->errfile
, "expected %d bit format, got %d bits\n", format
,
577 /* Convert len to bytes */
582 len
*= sizeof(short);
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
);
605 memcpy(x11
->clipboard_data
+ x11
->clipboard_data_size
, data
, len
);
606 x11
->clipboard_data_size
+= len
;
608 fprintf(x11
->errfile
, "Appended %ld bytes to buffer\n", len
);
610 return 0; /* Wait for more data */
612 len
= x11
->clipboard_data_size
;
613 *data_ret
= x11
->clipboard_data
;
620 fprintf(x11
->errfile
, "property contains no data (zero length)\n");
625 if ((incr
|| ret_val
== -1) && data
)
629 x11
->clipboard_data_size
= 0;
630 x11
->expect_property_notify
= 0;
636 static void vdagent_x11_get_selection_free(struct vdagent_x11
*x11
,
637 unsigned char *data
, int 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;
650 static uint32_t vdagent_x11_target_to_type(struct vdagent_x11
*x11
,
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
)
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
);
680 static void vdagent_x11_handle_selection_notify(struct vdagent_x11
*x11
,
681 XEvent
*event
, int incr
)
684 unsigned char *data
= NULL
;
687 if (x11
->clipboard_request_target
== None
) {
688 fprintf(x11
->errfile
, "SelectionNotify received without a target\n");
692 type
= vdagent_x11_target_to_type(x11
, x11
->clipboard_request_target
);
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
));
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? */
705 type
= VD_AGENT_CLIPBOARD_NONE
;
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
)
718 for (i
= 0; i
< l1
; i
++)
719 for (j
= 0; j
< l2
; j
++)
720 if (atoms1
[i
] == atoms2
[j
])
726 static void vdagent_x11_print_targets(struct vdagent_x11
*x11
,
727 const char *action
, Atom
*atoms
, int c
)
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
)
744 Atom atom
, *atoms
= NULL
;
746 if (!x11
->expected_targets_notifies
) {
747 fprintf(x11
->errfile
, "unexpected selection notify TARGETS\n");
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
)
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? */
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
);
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");
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
])
827 for (k
= 0; k
< x11
->clipboard_formats
[j
].atom_count
; k
++) {
828 targets
[target_count
] = x11
->clipboard_formats
[j
].atoms
[k
];
830 if (target_count
== sizeof(targets
)/sizeof(Atom
)) {
831 fprintf(x11
->errfile
, "send_targets: too many targets\n");
839 prop
= event
->xselectionrequest
.property
;
841 prop
= event
->xselectionrequest
.target
;
843 XChangeProperty(x11
->display
, event
->xselectionrequest
.requestor
, prop
,
844 XA_ATOM
, 32, PropModeReplace
, (unsigned char *)&targets
,
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
)
853 uint32_t type
= VD_AGENT_CLIPBOARD_NONE
;
855 if (!x11
->selection_request
)
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);
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);
875 if (event
->xselectionrequest
.target
== x11
->targets_atom
) {
876 vdagent_x11_send_targets(x11
, event
);
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);
886 udscs_write(x11
->vdagentd
, VDAGENTD_CLIPBOARD_REQUEST
, type
, NULL
, 0);
889 static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11
*x11
,
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
) {
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
;
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
);
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
,
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. */
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;
938 unsigned int closest_diff
= -1;
939 XRRScreenSize
* sizes
;
940 XRRScreenConfiguration
* config
;
943 if (!x11
->has_xrandr
)
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");
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
) {
973 fprintf(x11
->errfile
, "no suitable resolution found for monitor\n");
977 config
= XRRGetScreenInfo(x11
->display
, x11
->root_window
);
979 fprintf(x11
->errfile
, "get screen info failed\n");
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
)
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);
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);
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);
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
)
1052 uint32_t type_from_event
;
1054 if (x11
->selection_req_data
) {
1056 fprintf(x11
->errfile
, "received clipboard data while still sending"
1057 " data from previous request, ignoring\n");
1063 if (!x11
->selection_request
) {
1065 fprintf(x11
->errfile
, "received clipboard data without an "
1066 "outstanding selection request, ignoring\n");
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);
1081 /* Flush output buffers and consume any pending events */
1082 vdagent_x11_do_read(x11
);
1086 prop
= event
->xselectionrequest
.property
;
1088 prop
= event
->xselectionrequest
.target
;
1090 if (size
> x11
->max_prop_size
) {
1091 unsigned long len
= size
;
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);
1105 XChangeProperty(x11
->display
, event
->xselectionrequest
.requestor
, prop
,
1106 event
->xselectionrequest
.target
, 8, PropModeReplace
,
1108 vdagent_x11_send_selection_notify(x11
, prop
, 1);
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
)
1120 if (x11
->clipboard_owner
!= owner_client
) {
1121 fprintf(x11
->errfile
,
1122 "received clipboard release while not owning client clipboard\n");
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
,
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
);