/[rdesktop]/sourceforge.net/trunk/rdesktop/xclip.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /sourceforge.net/trunk/rdesktop/xclip.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1203 - (show annotations)
Mon Mar 27 08:39:20 2006 UTC (18 years, 1 month ago) by ossman_
File MIME type: text/plain
File size: 27374 byte(s)
Use the helper functions since they make sure the server end flushes its
cache.

1 /* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
3 Protocol services - Clipboard functions
4 Copyright (C) Erik Forsberg <forsberg@cendio.se> 2003
5 Copyright (C) Matthew Chapman 2003
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include <X11/Xlib.h>
23 #include <X11/Xatom.h>
24 #include "rdesktop.h"
25
26 /*
27 To gain better understanding of this code, one could be assisted by the following documents:
28 - Inter-Client Communication Conventions Manual (ICCCM)
29 HTML: http://tronche.com/gui/x/icccm/
30 PDF: http://ftp.xfree86.org/pub/XFree86/4.5.0/doc/PDF/icccm.pdf
31 - MSDN: Clipboard Formats
32 http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/dataexchange/clipboard/clipboardformats.asp
33 */
34
35 #ifdef HAVE_ICONV
36 #ifdef HAVE_LANGINFO_H
37 #ifdef HAVE_ICONV_H
38 #include <langinfo.h>
39 #include <iconv.h>
40 #define USE_UNICODE_CLIPBOARD
41 #endif
42 #endif
43 #endif
44
45 #ifdef USE_UNICODE_CLIPBOARD
46 #define RDP_CF_TEXT CF_UNICODETEXT
47 #else
48 #define RDP_CF_TEXT CF_TEXT
49 #endif
50
51 #define MAX_TARGETS 7
52
53 extern Display *g_display;
54 extern Window g_wnd;
55 extern Time g_last_gesturetime;
56
57 /* Atoms of the two X selections we're dealing with: CLIPBOARD (explicit-copy) and PRIMARY (selection-copy) */
58 static Atom clipboard_atom, primary_atom;
59 /* Atom of the TARGETS clipboard target */
60 static Atom targets_atom;
61 /* Atom of the TIMESTAMP clipboard target */
62 static Atom timestamp_atom;
63 /* Atom _RDESKTOP_CLIPBOARD_TARGET which has multiple uses:
64 - The 'property' argument in XConvertSelection calls: This is the property of our
65 window into which XConvertSelection will store the received clipboard data.
66 - In a clipboard request of target _RDESKTOP_CLIPBOARD_FORMATS, an XA_INTEGER-typed
67 property carrying the Windows native (CF_...) format desired by the requestor.
68 Requestor set this property (e.g. requestor_wnd[_RDESKTOP_CLIPBOARD_TARGET] = CF_TEXT)
69 before requesting clipboard data from a fellow rdesktop using
70 the _RDESKTOP_CLIPBOARD_FORMATS target. */
71 static Atom rdesktop_clipboard_target_atom;
72 /* Atom _RDESKTOP_CLIPBOARD_FORMATS which has multiple uses:
73 - The clipboard target (X jargon for "clipboard format") for rdesktop-to-rdesktop interchange
74 of Windows native clipboard data.
75 This target cannot be used standalone; the requestor must keep the
76 _RDESKTOP_CLIPBOARD_TARGET property on his window denoting
77 the Windows native clipboard format being requested.
78 - The root window property set by rdesktop when it owns the clipboard,
79 denoting all Windows native clipboard formats it offers via
80 requests of the _RDESKTOP_CLIPBOARD_FORMATS target. */
81 static Atom rdesktop_clipboard_formats_atom;
82 static Atom format_string_atom, format_utf8_string_atom, format_unicode_atom;
83 /* Atom of the INCR clipboard type (see ICCCM on "INCR Properties") */
84 static Atom incr_atom;
85 /* Stores the last "selection request" (= another X client requesting clipboard data from us).
86 To satisfy such a request, we request the clipboard data from the RDP server.
87 When we receive the response from the RDP server (asynchronously), this variable gives us
88 the context to proceed. */
89 static XSelectionRequestEvent selection_request;
90 /* Denotes we have a pending selection request. */
91 static Bool has_selection_request;
92 /* Stores the clipboard format (CF_TEXT, CF_UNICODETEXT etc.) requested in the last
93 CLIPDR_DATA_REQUEST (= the RDP server requesting clipboard data from us).
94 When we receive this data from whatever X client offering it, this variable gives us
95 the context to proceed.
96 */
97 static int rdp_clipboard_request_format;
98 /* Array of offered clipboard targets that will be sent to fellow X clients upon a TARGETS request. */
99 static Atom targets[MAX_TARGETS];
100 static int num_targets;
101 /* Denotes that this client currently holds the PRIMARY selection. */
102 static int have_primary = 0;
103 /* Denotes that an rdesktop (not this rdesktop) is owning the selection,
104 allowing us to interchange Windows native clipboard data directly. */
105 static int rdesktop_is_selection_owner = 0;
106
107 /* Denotes that an INCR ("chunked") transfer is in progress. */
108 static int g_waiting_for_INCR = 0;
109 /* Denotes the target format of the ongoing INCR ("chunked") transfer. */
110 static Atom g_incr_target = 0;
111 /* Buffers an INCR transfer. */
112 static uint8 *g_clip_buffer = 0;
113 /* Denotes the size of g_clip_buffer. */
114 static uint32 g_clip_buflen = 0;
115
116 /* Translate LF to CR-LF. To do this, we must allocate more memory.
117 The returned string is null-terminated, as required by CF_TEXT.
118 Does not stop on embedded nulls.
119 The length is updated. */
120 static void
121 crlf2lf(uint8 * data, uint32 * length)
122 {
123 uint8 *dst, *src;
124 src = dst = data;
125 while (src < data + *length)
126 {
127 if (*src != '\x0d')
128 *dst++ = *src;
129 src++;
130 }
131 *length = dst - data;
132 }
133
134 #ifdef USE_UNICODE_CLIPBOARD
135 /* Translate LF to CR-LF. To do this, we must allocate more memory.
136 The returned string is null-terminated, as required by CF_UNICODETEXT.
137 The size is updated. */
138 static uint8 *
139 utf16_lf2crlf(uint8 * data, uint32 * size)
140 {
141 uint8 *result;
142 uint16 *inptr, *outptr;
143
144 /* Worst case: Every char is LF */
145 result = xmalloc((*size * 2) + 2);
146 if (result == NULL)
147 return NULL;
148
149 inptr = (uint16 *) data;
150 outptr = (uint16 *) result;
151
152 /* Check for a reversed BOM */
153 Bool swap_endianess = (*inptr == 0xfffe);
154
155 while ((uint8 *) inptr < data + *size)
156 {
157 uint16 uvalue = *inptr;
158 if (swap_endianess)
159 uvalue = ((uvalue << 8) & 0xff00) + (uvalue >> 8);
160 if (uvalue == 0x0a)
161 *outptr++ = swap_endianess ? 0x0d00 : 0x0d;
162 *outptr++ = *inptr++;
163 }
164 *outptr++ = 0; /* null termination */
165 *size = (uint8 *) outptr - result;
166
167 return result;
168 }
169 #else
170 /* Translate LF to CR-LF. To do this, we must allocate more memory.
171 The length is updated. */
172 static uint8 *
173 lf2crlf(uint8 * data, uint32 * length)
174 {
175 uint8 *result, *p, *o;
176
177 /* Worst case: Every char is LF */
178 result = xmalloc(*length * 2);
179
180 p = data;
181 o = result;
182
183 while (p < data + *length)
184 {
185 if (*p == '\x0a')
186 *o++ = '\x0d';
187 *o++ = *p++;
188 }
189 *length = o - result;
190
191 /* Convenience */
192 *o++ = '\0';
193
194 return result;
195 }
196 #endif
197
198 static void
199 xclip_provide_selection(XSelectionRequestEvent * req, Atom type, unsigned int format, uint8 * data,
200 uint32 length)
201 {
202 XEvent xev;
203
204 XChangeProperty(g_display, req->requestor, req->property,
205 type, format, PropModeReplace, data, length);
206
207 xev.xselection.type = SelectionNotify;
208 xev.xselection.serial = 0;
209 xev.xselection.send_event = True;
210 xev.xselection.requestor = req->requestor;
211 xev.xselection.selection = req->selection;
212 xev.xselection.target = req->target;
213 xev.xselection.property = req->property;
214 xev.xselection.time = req->time;
215 XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);
216 }
217
218 /* Replies a clipboard requestor, telling that we're unable to satisfy his request for whatever reason.
219 This has the benefit of finalizing the clipboard negotiation and thus not leaving our requestor
220 lingering (and, potentially, stuck). */
221 static void
222 xclip_refuse_selection(XSelectionRequestEvent * req)
223 {
224 XEvent xev;
225
226 xev.xselection.type = SelectionNotify;
227 xev.xselection.serial = 0;
228 xev.xselection.send_event = True;
229 xev.xselection.requestor = req->requestor;
230 xev.xselection.selection = req->selection;
231 xev.xselection.target = req->target;
232 xev.xselection.property = None;
233 xev.xselection.time = req->time;
234 XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);
235 }
236
237 /* Wrapper for cliprdr_send_data which also cleans the request state. */
238 static void
239 helper_cliprdr_send_response(uint8 * data, uint32 length)
240 {
241 if (rdp_clipboard_request_format != 0)
242 {
243 cliprdr_send_data(data, length);
244 rdp_clipboard_request_format = 0;
245 if (!rdesktop_is_selection_owner)
246 cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
247 }
248 }
249
250 /* Last resort, when we have to provide clipboard data but for whatever
251 reason couldn't get any.
252 */
253 static void
254 helper_cliprdr_send_empty_response()
255 {
256 helper_cliprdr_send_response(NULL, 0);
257 }
258
259 /* Replies with clipboard data to RDP, converting it from the target format
260 to the expected RDP format as necessary. Returns true if data was sent.
261 */
262 static Bool
263 xclip_send_data_with_convert(uint8 * source, size_t source_size, Atom target)
264 {
265 #ifdef USE_UNICODE_CLIPBOARD
266 if (target == format_string_atom ||
267 target == format_unicode_atom || target == format_utf8_string_atom)
268 {
269 if (rdp_clipboard_request_format != RDP_CF_TEXT)
270 return False;
271
272 /* Make an attempt to convert any string we send to Unicode.
273 We don't know what the RDP server's ANSI Codepage is, or how to convert
274 to it, so using CF_TEXT is not safe (and is unnecessary, since all
275 WinNT versions are Unicode-minded).
276 */
277 size_t unicode_buffer_size;
278 char *unicode_buffer;
279 iconv_t cd;
280
281 if (target == format_string_atom)
282 {
283 char *locale_charset = nl_langinfo(CODESET);
284 cd = iconv_open(WINDOWS_CODEPAGE, locale_charset);
285 if (cd == (iconv_t) - 1)
286 {
287 DEBUG_CLIPBOARD(("Locale charset %s not found in iconv. Unable to convert clipboard text.\n", locale_charset));
288 return False;
289 }
290 unicode_buffer_size = source_size * 4;
291 }
292 else if (target == format_unicode_atom)
293 {
294 cd = iconv_open(WINDOWS_CODEPAGE, "UCS-2");
295 if (cd == (iconv_t) - 1)
296 {
297 return False;
298 }
299 unicode_buffer_size = source_size;
300 }
301 else if (target == format_utf8_string_atom)
302 {
303 cd = iconv_open(WINDOWS_CODEPAGE, "UTF-8");
304 if (cd == (iconv_t) - 1)
305 {
306 return False;
307 }
308 /* UTF-8 is guaranteed to be less or equally compact
309 as UTF-16 for all Unicode chars >=2 bytes.
310 */
311 unicode_buffer_size = source_size * 2;
312 }
313 else
314 {
315 return False;
316 }
317
318 unicode_buffer = xmalloc(unicode_buffer_size);
319 size_t unicode_buffer_size_remaining = unicode_buffer_size;
320 char *unicode_buffer_remaining = unicode_buffer;
321 char *data_remaining = (char *) source;
322 size_t data_size_remaining = source_size;
323 iconv(cd, (ICONV_CONST char **) &data_remaining, &data_size_remaining,
324 &unicode_buffer_remaining, &unicode_buffer_size_remaining);
325 iconv_close(cd);
326
327 /* translate linebreaks */
328 uint32 translated_data_size = unicode_buffer_size - unicode_buffer_size_remaining;
329 uint8 *translated_data =
330 utf16_lf2crlf((uint8 *) unicode_buffer, &translated_data_size);
331 if (translated_data != NULL)
332 {
333 DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n",
334 translated_data_size));
335 helper_cliprdr_send_response(translated_data, translated_data_size);
336 xfree(translated_data); /* Not the same thing as XFree! */
337 }
338
339 xfree(unicode_buffer);
340
341 return True;
342 }
343 #else
344 if (target == format_string_atom)
345 {
346 uint8 *translated_data;
347 uint32 length = source_size;
348
349 if (rdp_clipboard_request_format != RDP_CF_TEXT)
350 return False;
351
352 DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
353 translated_data = lf2crlf(source, &length);
354 if (translated_data != NULL)
355 {
356 helper_cliprdr_send_response(translated_data, length);
357 xfree(translated_data); /* Not the same thing as XFree! */
358 }
359
360 return True;
361 }
362 #endif
363 else if (target == rdesktop_clipboard_formats_atom)
364 {
365 helper_cliprdr_send_response(source, source_size + 1);
366
367 return True;
368 }
369 else
370 {
371 return False;
372 }
373 }
374
375 /* This function is called for SelectionNotify events.
376 The SelectionNotify event is sent from the clipboard owner to the requestor
377 after his request was satisfied.
378 If this function is called, we're the requestor side. */
379 #ifndef MAKE_PROTO
380 void
381 xclip_handle_SelectionNotify(XSelectionEvent * event)
382 {
383 unsigned long nitems, bytes_left;
384 XWindowAttributes wa;
385 Atom type;
386 Atom *supported_targets;
387 int res, i, format;
388 uint8 *data = NULL;
389
390 if (event->property == None)
391 goto fail;
392
393 DEBUG_CLIPBOARD(("xclip_handle_SelectionNotify: selection=%s, target=%s, property=%s\n",
394 XGetAtomName(g_display, event->selection),
395 XGetAtomName(g_display, event->target),
396 XGetAtomName(g_display, event->property)));
397
398 if (event->property == None)
399 goto fail;
400
401 res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
402 0, XMaxRequestSize(g_display), False, AnyPropertyType,
403 &type, &format, &nitems, &bytes_left, &data);
404
405 if (res != Success)
406 {
407 DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
408 goto fail;
409 }
410
411 if (type == incr_atom)
412 {
413 DEBUG_CLIPBOARD(("Received INCR.\n"));
414
415 XGetWindowAttributes(g_display, g_wnd, &wa);
416 if ((wa.your_event_mask | PropertyChangeMask) != wa.your_event_mask)
417 {
418 XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));
419 }
420 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
421 XFree(data);
422 g_incr_target = event->target;
423 g_waiting_for_INCR = 1;
424 return;
425 }
426
427 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
428
429 /* Negotiate target format */
430 if (event->target == targets_atom)
431 {
432 /* Determine the best of text targets that we have available:
433 Prefer UTF8_STRING > text/unicode (unspecified encoding) > STRING
434 (ignore TEXT and COMPOUND_TEXT because we don't have code to handle them)
435 */
436 int text_target_satisfaction = 0;
437 Atom best_text_target = 0; /* measures how much we're satisfied with what we found */
438 if (type != None)
439 {
440 supported_targets = (Atom *) data;
441 for (i = 0; i < nitems; i++)
442 {
443 DEBUG_CLIPBOARD(("Target %d: %s\n", i,
444 XGetAtomName(g_display, supported_targets[i])));
445 if (supported_targets[i] == format_string_atom)
446 {
447 if (text_target_satisfaction < 1)
448 {
449 DEBUG_CLIPBOARD(("Other party supports STRING, choosing that as best_target\n"));
450 best_text_target = supported_targets[i];
451 text_target_satisfaction = 1;
452 }
453 }
454 #ifdef USE_UNICODE_CLIPBOARD
455 else if (supported_targets[i] == format_unicode_atom)
456 {
457 if (text_target_satisfaction < 2)
458 {
459 DEBUG_CLIPBOARD(("Other party supports text/unicode, choosing that as best_target\n"));
460 best_text_target = supported_targets[i];
461 text_target_satisfaction = 2;
462 }
463 }
464 else if (supported_targets[i] == format_utf8_string_atom)
465 {
466 if (text_target_satisfaction < 3)
467 {
468 DEBUG_CLIPBOARD(("Other party supports UTF8_STRING, choosing that as best_target\n"));
469 best_text_target = supported_targets[i];
470 text_target_satisfaction = 3;
471 }
472 }
473 #endif
474 }
475 }
476
477 /* Kickstarting the next step in the process of satisfying RDP's
478 clipboard request -- specifically, requesting the actual clipboard data.
479 */
480 if (best_text_target != 0)
481 {
482 XConvertSelection(g_display, clipboard_atom, best_text_target,
483 rdesktop_clipboard_target_atom, g_wnd, event->time);
484 return;
485 }
486 else
487 {
488 DEBUG_CLIPBOARD(("Unable to find a textual target to satisfy RDP clipboard text request\n"));
489 goto fail;
490 }
491 }
492 else
493 {
494 if (!xclip_send_data_with_convert(data, nitems, event->target))
495 {
496 goto fail;
497 }
498 }
499
500 XFree(data);
501
502 return;
503
504 fail:
505 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
506 if (data)
507 XFree(data);
508 helper_cliprdr_send_empty_response();
509 }
510
511 /* This function is called for SelectionRequest events.
512 The SelectionRequest event is sent from the requestor to the clipboard owner
513 to request clipboard data.
514 */
515 void
516 xclip_handle_SelectionRequest(XSelectionRequestEvent * event)
517 {
518 unsigned long nitems, bytes_left;
519 unsigned char *prop_return;
520 uint32 *wanted_format;
521 int format, res;
522 Atom type;
523
524 DEBUG_CLIPBOARD(("xclip_handle_SelectionRequest: selection=%s, target=%s, property=%s\n",
525 XGetAtomName(g_display, event->selection),
526 XGetAtomName(g_display, event->target),
527 XGetAtomName(g_display, event->property)));
528
529 if (event->target == targets_atom)
530 {
531 xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, num_targets);
532 return;
533 }
534 else if (event->target == timestamp_atom)
535 {
536 xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);
537 return;
538 }
539 else
540 {
541 /* All the following targets require an async operation with the RDP server
542 and currently we don't do X clipboard request queueing so we can only
543 handle one such request at a time. */
544 if (has_selection_request)
545 {
546 DEBUG_CLIPBOARD(("Error: Another clipboard request was already sent to the RDP server and not yet responded. Refusing this request.\n"));
547 xclip_refuse_selection(event);
548 return;
549 }
550 if (event->target == rdesktop_clipboard_formats_atom)
551 {
552 /* Before the requestor makes a request for the _RDESKTOP_CLIPBOARD_FORMATS target,
553 he should declare requestor[_RDESKTOP_CLIPBOARD_TARGET] = CF_SOMETHING.
554 Otherwise, we default to RDP_CF_TEXT.
555 */
556 res = XGetWindowProperty(g_display, event->requestor,
557 rdesktop_clipboard_target_atom, 0, 1, True,
558 XA_INTEGER, &type, &format, &nitems, &bytes_left,
559 &prop_return);
560 wanted_format = (uint32 *) prop_return;
561 format = (res == Success) ? *wanted_format : RDP_CF_TEXT;
562 XFree(prop_return);
563 }
564 else if (event->target == format_string_atom || event->target == XA_STRING)
565 {
566 /* STRING and XA_STRING are defined to be ISO8859-1 */
567 format = CF_TEXT;
568 }
569 else if (event->target == format_utf8_string_atom)
570 {
571 #ifdef USE_UNICODE_CLIPBOARD
572 format = CF_UNICODETEXT;
573 #else
574 DEBUG_CLIPBOARD(("Requested target unavailable due to lack of Unicode support. (It was not in TARGETS, so why did you ask for it?!)\n"));
575 xclip_refuse_selection(event);
576 return;
577 #endif
578 }
579 else if (event->target == format_unicode_atom)
580 {
581 /* Assuming text/unicode to be UTF-16 */
582 format = CF_UNICODETEXT;
583 }
584 else
585 {
586 DEBUG_CLIPBOARD(("Requested target unavailable. (It was not in TARGETS, so why did you ask for it?!)\n"));
587 xclip_refuse_selection(event);
588 return;
589 }
590
591 cliprdr_send_data_request(format);
592 selection_request = *event;
593 has_selection_request = True;
594 return; /* wait for data */
595 }
596 }
597
598 /* While this rdesktop holds ownership over the clipboard, it means the clipboard data
599 is offered by the RDP server (and when it is pasted inside RDP, there's no network
600 roundtrip).
601
602 This event (SelectionClear) symbolizes this rdesktop lost onwership of the clipboard
603 to some other X client. We should find out what clipboard formats this other
604 client offers and announce that to RDP. */
605 void
606 xclip_handle_SelectionClear(void)
607 {
608 DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
609 have_primary = 0;
610 XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);
611 /* FIXME:
612 Without XFIXES, we cannot reliably know the formats offered by the
613 new owner of the X11 clipboard, so we just lie about him
614 offering RDP_CF_TEXT. */
615 cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
616 }
617
618 /* Called when any property changes in our window or the root window. */
619 void
620 xclip_handle_PropertyNotify(XPropertyEvent * event)
621 {
622 unsigned long nitems;
623 unsigned long offset = 0;
624 unsigned long bytes_left = 1;
625 int format, res;
626 XWindowAttributes wa;
627 uint8 *data;
628 Atom type;
629
630 if (event->state == PropertyNewValue && g_waiting_for_INCR)
631 {
632 DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: g_waiting_for_INCR != 0\n"));
633
634 while (bytes_left > 0)
635 {
636 /* Unlike the specification, we don't set the 'delete' arugment to True
637 since we slurp the INCR's chunks in even-smaller chunks of 4096 bytes. */
638 if ((XGetWindowProperty
639 (g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,
640 False, AnyPropertyType, &type, &format, &nitems, &bytes_left,
641 &data) != Success))
642 {
643 XFree(data);
644 return;
645 }
646
647 if (nitems == 0)
648 {
649 /* INCR transfer finished */
650 XGetWindowAttributes(g_display, g_wnd, &wa);
651 XSelectInput(g_display, g_wnd,
652 (wa.your_event_mask ^ PropertyChangeMask));
653 XFree(data);
654 g_waiting_for_INCR = 0;
655
656 if (g_clip_buflen > 0)
657 {
658 if (!xclip_send_data_with_convert
659 (g_clip_buffer, g_clip_buflen, g_incr_target))
660 {
661 helper_cliprdr_send_empty_response();
662 }
663 xfree(g_clip_buffer);
664 g_clip_buffer = NULL;
665 g_clip_buflen = 0;
666 }
667 }
668 else
669 {
670 /* Another chunk in the INCR transfer */
671 offset += (nitems / 4); /* offset at which to begin the next slurp */
672 g_clip_buffer = xrealloc(g_clip_buffer, g_clip_buflen + nitems);
673 memcpy(g_clip_buffer + g_clip_buflen, data, nitems);
674 g_clip_buflen += nitems;
675
676 XFree(data);
677 }
678 }
679 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
680 return;
681 }
682
683 if ((event->atom == rdesktop_clipboard_formats_atom) &&
684 (event->window == DefaultRootWindow(g_display)) &&
685 !have_primary /* not interested in our own events */ )
686 {
687 if (event->state == PropertyNewValue)
688 {
689 DEBUG_CLIPBOARD(("xclip_handle_PropertyNotify: getting fellow rdesktop formats\n"));
690
691 res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),
692 rdesktop_clipboard_formats_atom, 0,
693 XMaxRequestSize(g_display), False, XA_STRING,
694 &type, &format, &nitems, &bytes_left, &data);
695
696 if ((res == Success) && (nitems > 0))
697 {
698 cliprdr_send_native_format_announce(data, nitems);
699 rdesktop_is_selection_owner = 1;
700 return;
701 }
702 }
703
704 /* For some reason, we couldn't announce the native formats */
705 cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
706 rdesktop_is_selection_owner = 0;
707 }
708 }
709 #endif
710
711
712 /* Called when the RDP server announces new clipboard data formats.
713 In response, we:
714 - take ownership over the clipboard
715 - declare those formats in their Windows native form
716 to other rdesktop instances on this X server */
717 void
718 ui_clip_format_announce(uint8 * data, uint32 length)
719 {
720 XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime);
721 if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
722 {
723 warning("Failed to aquire ownership of PRIMARY clipboard\n");
724 return;
725 }
726
727 have_primary = 1;
728 XChangeProperty(g_display, DefaultRootWindow(g_display),
729 rdesktop_clipboard_formats_atom, XA_STRING, 8, PropModeReplace, data,
730 length);
731
732 XSetSelectionOwner(g_display, clipboard_atom, g_wnd, g_last_gesturetime);
733 if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)
734 warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
735 }
736
737 /* Called when the RDP server responds with clipboard data (after we've requested it). */
738 void
739 ui_clip_handle_data(uint8 * data, uint32 length)
740 {
741 BOOL free_data = False;
742
743 if (selection_request.target == format_string_atom || selection_request.target == XA_STRING)
744 {
745 /* We're expecting a CF_TEXT response */
746 uint8 *firstnull;
747
748 /* translate linebreaks */
749 crlf2lf(data, &length);
750
751 /* Only send data up to null byte, if any */
752 firstnull = (uint8 *) strchr((char *) data, '\0');
753 if (firstnull)
754 {
755 length = firstnull - data + 1;
756 }
757 }
758 #ifdef USE_UNICODE_CLIPBOARD
759 else if (selection_request.target == format_utf8_string_atom)
760 {
761 /* We're expecting a CF_UNICODETEXT response */
762 iconv_t cd = iconv_open("UTF-8", WINDOWS_CODEPAGE);
763 if (cd != (iconv_t) - 1)
764 {
765 size_t utf8_length = length * 2;
766 char *utf8_data = malloc(utf8_length);
767 size_t utf8_length_remaining = utf8_length;
768 char *utf8_data_remaining = utf8_data;
769 char *data_remaining = (char *) data;
770 size_t length_remaining = (size_t) length;
771 if (utf8_data == NULL)
772 {
773 iconv_close(cd);
774 return;
775 }
776 iconv(cd, (ICONV_CONST char **) &data_remaining, &length_remaining,
777 &utf8_data_remaining, &utf8_length_remaining);
778 iconv_close(cd);
779 free_data = True;
780 data = (uint8 *) utf8_data;
781 length = utf8_length - utf8_length_remaining;
782 }
783 }
784 else if (selection_request.target == format_unicode_atom)
785 {
786 /* We're expecting a CF_UNICODETEXT response, so what we're
787 receiving matches our requirements and there's no need
788 for further conversions. */
789 }
790 #endif
791 else if (selection_request.target == rdesktop_clipboard_formats_atom)
792 {
793 /* Pass as-is */
794 }
795 else
796 {
797 xclip_refuse_selection(&selection_request);
798 has_selection_request = False;
799 return;
800 }
801
802 xclip_provide_selection(&selection_request, selection_request.target, 8, data, length - 1);
803 has_selection_request = False;
804
805 if (free_data)
806 free(data);
807 }
808
809 void
810 ui_clip_request_data(uint32 format)
811 {
812 Window selectionowner;
813
814 DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
815 rdp_clipboard_request_format = format;
816
817 if (rdesktop_is_selection_owner)
818 {
819 XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
820 XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
821
822 XConvertSelection(g_display, primary_atom, rdesktop_clipboard_formats_atom,
823 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
824 return;
825 }
826
827 selectionowner = XGetSelectionOwner(g_display, primary_atom);
828 if (selectionowner != None)
829 {
830 XConvertSelection(g_display, primary_atom, targets_atom,
831 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
832 return;
833 }
834
835 /* No PRIMARY, try CLIPBOARD */
836 selectionowner = XGetSelectionOwner(g_display, clipboard_atom);
837 if (selectionowner != None)
838 {
839 XConvertSelection(g_display, clipboard_atom, targets_atom,
840 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
841 return;
842 }
843
844 /* No data available */
845 helper_cliprdr_send_empty_response();
846 }
847
848 void
849 ui_clip_sync(void)
850 {
851 cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
852 }
853
854
855 void
856 xclip_init(void)
857 {
858 if (!cliprdr_init())
859 return;
860
861 primary_atom = XInternAtom(g_display, "PRIMARY", False);
862 clipboard_atom = XInternAtom(g_display, "CLIPBOARD", False);
863 targets_atom = XInternAtom(g_display, "TARGETS", False);
864 timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);
865 rdesktop_clipboard_target_atom =
866 XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
867 incr_atom = XInternAtom(g_display, "INCR", False);
868 format_string_atom = XInternAtom(g_display, "STRING", False);
869 format_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False);
870 format_unicode_atom = XInternAtom(g_display, "text/unicode", False);
871 num_targets = 0;
872 targets[num_targets++] = targets_atom;
873 targets[num_targets++] = timestamp_atom;
874 targets[num_targets++] = rdesktop_clipboard_formats_atom;
875 targets[num_targets++] = format_string_atom;
876 #ifdef USE_UNICODE_CLIPBOARD
877 targets[num_targets++] = format_utf8_string_atom;
878 #endif
879 targets[num_targets++] = format_unicode_atom;
880 targets[num_targets++] = XA_STRING;
881
882 /* rdesktop sets _RDESKTOP_CLIPBOARD_FORMATS on the root window when acquiring the clipboard.
883 Other interested rdesktops can use this to notify their server of the available formats. */
884 rdesktop_clipboard_formats_atom =
885 XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
886 XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);
887 }

  ViewVC Help
Powered by ViewVC 1.1.26