/[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 1037 - (show annotations)
Mon Jan 2 15:55:59 2006 UTC (18 years, 4 months ago) by forsberg
File MIME type: text/plain
File size: 27247 byte(s)
Applied patch [1] with unicode support in clipboard code from Ilya Konstantinov

[1] https://sourceforge.net/tracker/?func=detail&atid=381349&aid=1394324&group_id=24366

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 ||
268 target == format_utf8_string_atom)
269 {
270 if (rdp_clipboard_request_format != RDP_CF_TEXT)
271 return False;
272
273 /* Make an attempt to convert any string we send to Unicode.
274 We don't know what the RDP server's ANSI Codepage is, or how to convert
275 to it, so using CF_TEXT is not safe (and is unnecessary, since all
276 WinNT versions are Unicode-minded).
277 */
278 size_t unicode_buffer_size;
279 char* unicode_buffer;
280 iconv_t cd;
281
282 if (target == format_string_atom)
283 {
284 char* locale_charset = nl_langinfo(CODESET);
285 cd = iconv_open(WINDOWS_CODEPAGE, locale_charset);
286 if (cd == (iconv_t)-1)
287 {
288 DEBUG_CLIPBOARD(("Locale charset %s not found in iconv. Unable to convert clipboard text.\n", locale_charset));
289 return False;
290 }
291 unicode_buffer_size = source_size * 4;
292 }
293 else if (target == format_unicode_atom)
294 {
295 cd = iconv_open(WINDOWS_CODEPAGE, "UCS-2");
296 if (cd == (iconv_t)-1)
297 {
298 return False;
299 }
300 unicode_buffer_size = source_size;
301 }
302 else if (target == format_utf8_string_atom)
303 {
304 cd = iconv_open(WINDOWS_CODEPAGE, "UTF-8");
305 if (cd == (iconv_t)-1)
306 {
307 return False;
308 }
309 /* UTF-8 is guaranteed to be less or equally compact
310 as UTF-16 for all Unicode chars >=2 bytes.
311 */
312 unicode_buffer_size = source_size * 2;
313 }
314 else
315 {
316 return False;
317 }
318
319 unicode_buffer = xmalloc(unicode_buffer_size);
320 size_t unicode_buffer_size_remaining = unicode_buffer_size;
321 char* unicode_buffer_remaining = unicode_buffer;
322 char* data_remaining = (char*)source;
323 size_t data_size_remaining = source_size;
324 iconv(cd, &data_remaining, &data_size_remaining, &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 = utf16_lf2crlf((uint8*)unicode_buffer, &translated_data_size);
330 if (translated_data != NULL)
331 {
332 DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n", translated_data_size));
333 cliprdr_send_data(translated_data, translated_data_size);
334 xfree(translated_data); /* Not the same thing as XFree! */
335 }
336
337 xfree(unicode_buffer);
338
339 return True;
340 }
341 #else
342 if (target == format_string_atom)
343 {
344 uint8 *translated_data;
345 uint32 length = source_size;
346
347 if (rdp_clipboard_request_format != RDP_CF_TEXT)
348 return False;
349
350 DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
351 translated_data = lf2crlf(source, &length);
352 if (translated_data != NULL)
353 {
354 cliprdr_send_data(translated_data, length);
355 xfree(translated_data); /* Not the same thing as XFree! */
356 }
357
358 return True;
359 }
360 #endif
361 else if (target == rdesktop_clipboard_formats_atom)
362 {
363 helper_cliprdr_send_response(source, source_size + 1);
364
365 return True;
366 }
367 else
368 {
369 return False;
370 }
371 }
372
373 /* This function is called for SelectionNotify events.
374 The SelectionNotify event is sent from the clipboard owner to the requestor
375 after his request was satisfied.
376 If this function is called, we're the requestor side. */
377 #ifndef MAKE_PROTO
378 void
379 xclip_handle_SelectionNotify(XSelectionEvent * event)
380 {
381 unsigned long nitems, bytes_left;
382 XWindowAttributes wa;
383 Atom type;
384 Atom *supported_targets;
385 int res, i, format;
386 uint8 *data;
387
388 if (event->property == None)
389 goto fail;
390
391 DEBUG_CLIPBOARD(("xclip_handle_SelectionNotify: selection=%s, target=%s, property=%s\n",
392 XGetAtomName(g_display, event->selection),
393 XGetAtomName(g_display, event->target),
394 XGetAtomName(g_display, event->property)));
395
396 if (event->property == None)
397 goto fail;
398
399 res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
400 0, XMaxRequestSize(g_display), False, AnyPropertyType,
401 &type, &format, &nitems, &bytes_left, &data);
402
403 if (res != Success)
404 {
405 DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
406 goto fail;
407 }
408
409 if (type == incr_atom)
410 {
411 DEBUG_CLIPBOARD(("Received INCR.\n"));
412
413 XGetWindowAttributes(g_display, g_wnd, &wa);
414 if ((wa.your_event_mask | PropertyChangeMask) != wa.your_event_mask)
415 {
416 XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));
417 }
418 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
419 XFree(data);
420 g_incr_target = event->target;
421 g_waiting_for_INCR = 1;
422 return;
423 }
424
425 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
426
427 /* Negotiate target format */
428 if (event->target == targets_atom)
429 {
430 /* Determine the best of text targets that we have available:
431 Prefer UTF8_STRING > text/unicode (unspecified encoding) > STRING
432 (ignore TEXT and COMPOUND_TEXT because we don't have code to handle them)
433 */
434 int text_target_satisfaction = 0;
435 Atom best_text_target = 0; /* measures how much we're satisfied with what we found */
436 if (type != None)
437 {
438 supported_targets = (Atom *) data;
439 for (i = 0; i < nitems; i++)
440 {
441 DEBUG_CLIPBOARD(("Target %d: %s\n", i,
442 XGetAtomName(g_display, supported_targets[i])));
443 if (supported_targets[i] == format_string_atom)
444 {
445 if (text_target_satisfaction < 1)
446 {
447 DEBUG_CLIPBOARD(("Other party supports STRING, choosing that as best_target\n"));
448 best_text_target = supported_targets[i];
449 text_target_satisfaction = 1;
450 }
451 }
452 #ifdef USE_UNICODE_CLIPBOARD
453 else if (supported_targets[i] == format_unicode_atom)
454 {
455 if (text_target_satisfaction < 2)
456 {
457 DEBUG_CLIPBOARD(("Other party supports text/unicode, choosing that as best_target\n"));
458 best_text_target = supported_targets[i];
459 text_target_satisfaction = 2;
460 }
461 }
462 else if (supported_targets[i] == format_utf8_string_atom)
463 {
464 if (text_target_satisfaction < 3)
465 {
466 DEBUG_CLIPBOARD(("Other party supports UTF8_STRING, choosing that as best_target\n"));
467 best_text_target = supported_targets[i];
468 text_target_satisfaction = 3;
469 }
470 }
471 #endif
472 }
473 }
474
475 /* Kickstarting the next step in the process of satisfying RDP's
476 clipboard request -- specifically, requesting the actual clipboard data.
477 */
478 if (best_text_target != 0)
479 {
480 XConvertSelection(g_display, clipboard_atom, best_text_target, rdesktop_clipboard_target_atom, g_wnd, event->time);
481 return;
482 }
483 else
484 {
485 DEBUG_CLIPBOARD(("Unable to find a textual target to satisfy RDP clipboard text request\n"));
486 goto fail;
487 }
488 }
489 else
490 {
491 if (!xclip_send_data_with_convert(data, nitems, event->target))
492 {
493 goto fail;
494 }
495 }
496
497 XFree(data);
498
499 return;
500
501 fail:
502 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
503 XFree(data);
504 helper_cliprdr_send_empty_response();
505 }
506
507 /* This function is called for SelectionRequest events.
508 The SelectionRequest event is sent from the requestor to the clipboard owner
509 to request clipboard data.
510 */
511 void
512 xclip_handle_SelectionRequest(XSelectionRequestEvent * event)
513 {
514 unsigned long nitems, bytes_left;
515 unsigned char *prop_return;
516 uint32 *wanted_format;
517 int format, res;
518 Atom type;
519
520 DEBUG_CLIPBOARD(("xclip_handle_SelectionRequest: selection=%s, target=%s, property=%s\n",
521 XGetAtomName(g_display, event->selection),
522 XGetAtomName(g_display, event->target),
523 XGetAtomName(g_display, event->property)));
524
525 if (event->target == targets_atom)
526 {
527 xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, num_targets);
528 return;
529 }
530 else if (event->target == timestamp_atom)
531 {
532 xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);
533 return;
534 }
535 else
536 {
537 /* All the following targets require an async operation with the RDP server
538 and currently we don't do X clipboard request queueing so we can only
539 handle one such request at a time. */
540 if (has_selection_request)
541 {
542 DEBUG_CLIPBOARD(("Error: Another clipboard request was already sent to the RDP server and not yet responded. Refusing this request.\n"));
543 xclip_refuse_selection(event);
544 return;
545 }
546 if (event->target == rdesktop_clipboard_formats_atom)
547 {
548 /* Before the requestor makes a request for the _RDESKTOP_CLIPBOARD_FORMATS target,
549 he should declare requestor[_RDESKTOP_CLIPBOARD_TARGET] = CF_SOMETHING.
550 Otherwise, we default to RDP_CF_TEXT.
551 */
552 res = XGetWindowProperty(g_display, event->requestor,
553 rdesktop_clipboard_target_atom, 0, 1, True, XA_INTEGER,
554 &type, &format, &nitems, &bytes_left, &prop_return);
555 wanted_format = (uint32 *) prop_return;
556 format = (res == Success) ? *wanted_format : RDP_CF_TEXT;
557 XFree(prop_return);
558 }
559 else if (event->target == format_string_atom ||
560 event->target == XA_STRING)
561 {
562 /* STRING and XA_STRING are defined to be ISO8859-1 */
563 format = CF_TEXT;
564 }
565 else if (event->target == format_utf8_string_atom)
566 {
567 #ifdef USE_UNICODE_CLIPBOARD
568 format = CF_UNICODETEXT;
569 #else
570 DEBUG_CLIPBOARD(("Requested target unavailable due to lack of Unicode support. (It was not in TARGETS, so why did you ask for it?!)\n"));
571 xclip_refuse_selection(event);
572 return;
573 #endif
574 }
575 else if (event->target == format_unicode_atom)
576 {
577 /* Assuming text/unicode to be UTF-16 */
578 format = CF_UNICODETEXT;
579 }
580 else
581 {
582 DEBUG_CLIPBOARD(("Requested target unavailable. (It was not in TARGETS, so why did you ask for it?!)\n"));
583 xclip_refuse_selection(event);
584 return;
585 }
586
587 cliprdr_send_data_request(format);
588 selection_request = *event;
589 has_selection_request = True;
590 return; /* wait for data */
591 }
592 }
593
594 /* While this rdesktop holds ownership over the clipboard, it means the clipboard data
595 is offered by the RDP server (and when it is pasted inside RDP, there's no network
596 roundtrip).
597
598 This event (SelectionClear) symbolizes this rdesktop lost onwership of the clipboard
599 to some other X client. We should find out what clipboard formats this other
600 client offers and announce that to RDP. */
601 void
602 xclip_handle_SelectionClear(void)
603 {
604 DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
605 have_primary = 0;
606 XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);
607 /* FIXME:
608 Without XFIXES, we cannot reliably know the formats offered by the
609 new owner of the X11 clipboard, so we just lie about him
610 offering RDP_CF_TEXT. */
611 cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
612 }
613
614 /* Called when any property changes in our window or the root window. */
615 void
616 xclip_handle_PropertyNotify(XPropertyEvent * event)
617 {
618 unsigned long nitems;
619 unsigned long offset = 0;
620 unsigned long bytes_left = 1;
621 int format, res;
622 XWindowAttributes wa;
623 uint8 *data;
624 Atom type;
625
626 if (event->state == PropertyNewValue && g_waiting_for_INCR)
627 {
628 DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: g_waiting_for_INCR != 0\n"));
629
630 while (bytes_left > 0)
631 {
632 /* Unlike the specification, we don't set the 'delete' arugment to True
633 since we slurp the INCR's chunks in even-smaller chunks of 4096 bytes. */
634 if ((XGetWindowProperty
635 (g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,
636 False, AnyPropertyType, &type, &format, &nitems, &bytes_left,
637 &data) != Success))
638 {
639 XFree(data);
640 return;
641 }
642
643 if (nitems == 0)
644 {
645 /* INCR transfer finished */
646 XGetWindowAttributes(g_display, g_wnd, &wa);
647 XSelectInput(g_display, g_wnd,
648 (wa.your_event_mask ^ PropertyChangeMask));
649 XFree(data);
650 g_waiting_for_INCR = 0;
651
652 if (g_clip_buflen > 0)
653 {
654 if (!xclip_send_data_with_convert(g_clip_buffer, g_clip_buflen, g_incr_target))
655 {
656 helper_cliprdr_send_empty_response();
657 }
658 xfree(g_clip_buffer);
659 g_clip_buffer = NULL;
660 g_clip_buflen = 0;
661 }
662 }
663 else
664 {
665 /* Another chunk in the INCR transfer */
666 offset += (nitems / 4); /* offset at which to begin the next slurp */
667 g_clip_buffer = xrealloc(g_clip_buffer, g_clip_buflen + nitems);
668 memcpy(g_clip_buffer + g_clip_buflen, data, nitems);
669 g_clip_buflen += nitems;
670
671 XFree(data);
672 }
673 }
674 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
675 return;
676 }
677
678 if ((event->atom == rdesktop_clipboard_formats_atom) &&
679 (event->window == DefaultRootWindow(g_display)) &&
680 !have_primary /* not interested in our own events */)
681 {
682 if (event->state == PropertyNewValue)
683 {
684 DEBUG_CLIPBOARD(("xclip_handle_PropertyNotify: getting fellow rdesktop formats\n"));
685
686 res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),
687 rdesktop_clipboard_formats_atom, 0,
688 XMaxRequestSize(g_display), False, XA_STRING, &type,
689 &format, &nitems, &bytes_left, &data);
690
691 if ((res == Success) && (nitems > 0))
692 {
693 cliprdr_send_native_format_announce(data, nitems);
694 rdesktop_is_selection_owner = 1;
695 return;
696 }
697 }
698
699 /* For some reason, we couldn't announce the native formats */
700 cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
701 rdesktop_is_selection_owner = 0;
702 }
703 }
704 #endif
705
706
707 /* Called when the RDP server announces new clipboard data formats.
708 In response, we:
709 - take ownership over the clipboard
710 - declare those formats in their Windows native form
711 to other rdesktop instances on this X server */
712 void
713 ui_clip_format_announce(uint8 * data, uint32 length)
714 {
715 XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime);
716 if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
717 {
718 warning("Failed to aquire ownership of PRIMARY clipboard\n");
719 return;
720 }
721
722 have_primary = 1;
723 XChangeProperty(g_display, DefaultRootWindow(g_display),
724 rdesktop_clipboard_formats_atom, XA_STRING, 8, PropModeReplace, data,
725 length);
726
727 XSetSelectionOwner(g_display, clipboard_atom, g_wnd, g_last_gesturetime);
728 if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)
729 warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
730 }
731
732 /* Called when the RDP server responds with clipboard data (after we've requested it). */
733 void
734 ui_clip_handle_data(uint8 * data, uint32 length)
735 {
736 BOOL free_data = False;
737
738 if (selection_request.target == format_string_atom ||
739 selection_request.target == XA_STRING)
740 {
741 /* We're expecting a CF_TEXT response */
742 uint8 *firstnull;
743
744 /* translate linebreaks */
745 crlf2lf(data, &length);
746
747 /* Only send data up to null byte, if any */
748 firstnull = (uint8 *) strchr((char *) data, '\0');
749 if (firstnull)
750 {
751 length = firstnull - data + 1;
752 }
753 }
754 #ifdef USE_UNICODE_CLIPBOARD
755 else if (selection_request.target == format_utf8_string_atom)
756 {
757 /* We're expecting a CF_UNICODETEXT response */
758 iconv_t cd = iconv_open("UTF-8", WINDOWS_CODEPAGE);
759 if (cd != (iconv_t)-1)
760 {
761 size_t utf8_length = length * 2;
762 char* utf8_data = malloc(utf8_length);
763 size_t utf8_length_remaining = utf8_length;
764 char* utf8_data_remaining = utf8_data;
765 char* data_remaining = (char*)data;
766 size_t length_remaining = (size_t)length;
767 if (utf8_data == NULL)
768 {
769 iconv_close(cd);
770 return;
771 }
772 iconv(cd, &data_remaining, &length_remaining, &utf8_data_remaining, &utf8_length_remaining);
773 iconv_close(cd);
774 free_data = True;
775 data = (uint8*)utf8_data;
776 length = utf8_length - utf8_length_remaining;
777 }
778 }
779 else if (selection_request.target == format_unicode_atom)
780 {
781 /* We're expecting a CF_UNICODETEXT response, so what we're
782 receiving matches our requirements and there's no need
783 for further conversions. */
784 }
785 #endif
786 else if (selection_request.target == rdesktop_clipboard_formats_atom)
787 {
788 /* Pass as-is */
789 }
790 else
791 {
792 xclip_refuse_selection(&selection_request);
793 has_selection_request = False;
794 return;
795 }
796
797 xclip_provide_selection(&selection_request, selection_request.target, 8, data, length - 1);
798 has_selection_request = False;
799
800 if (free_data)
801 free(data);
802 }
803
804 void
805 ui_clip_request_data(uint32 format)
806 {
807 Window selectionowner;
808
809 DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
810 rdp_clipboard_request_format = format;
811
812 if (rdesktop_is_selection_owner)
813 {
814 XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
815 XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
816
817 XConvertSelection(g_display, primary_atom, rdesktop_clipboard_formats_atom,
818 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
819 return;
820 }
821
822 selectionowner = XGetSelectionOwner(g_display, primary_atom);
823 if (selectionowner != None)
824 {
825 XConvertSelection(g_display, primary_atom, targets_atom,
826 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
827 return;
828 }
829
830 /* No PRIMARY, try CLIPBOARD */
831 selectionowner = XGetSelectionOwner(g_display, clipboard_atom);
832 if (selectionowner != None)
833 {
834 XConvertSelection(g_display, clipboard_atom, targets_atom,
835 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
836 return;
837 }
838
839 /* No data available */
840 cliprdr_send_data(NULL, 0);
841 }
842
843 void
844 ui_clip_sync(void)
845 {
846 cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
847 }
848
849
850 void
851 xclip_init(void)
852 {
853 if (!cliprdr_init())
854 return;
855
856 primary_atom = XInternAtom(g_display, "PRIMARY", False);
857 clipboard_atom = XInternAtom(g_display, "CLIPBOARD", False);
858 targets_atom = XInternAtom(g_display, "TARGETS", False);
859 timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);
860 rdesktop_clipboard_target_atom =
861 XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
862 incr_atom = XInternAtom(g_display, "INCR", False);
863 format_string_atom = XInternAtom(g_display, "STRING", False);
864 format_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False);
865 format_unicode_atom = XInternAtom(g_display, "text/unicode", False);
866 num_targets = 0;
867 targets[num_targets++] = targets_atom;
868 targets[num_targets++] = timestamp_atom;
869 targets[num_targets++] = rdesktop_clipboard_formats_atom;
870 targets[num_targets++] = format_string_atom;
871 #ifdef USE_UNICODE_CLIPBOARD
872 targets[num_targets++] = format_utf8_string_atom;
873 #endif
874 targets[num_targets++] = format_unicode_atom;
875 targets[num_targets++] = XA_STRING;
876
877 /* rdesktop sets _RDESKTOP_CLIPBOARD_FORMATS on the root window when acquiring the clipboard.
878 Other interested rdesktops can use this to notify their server of the available formats. */
879 rdesktop_clipboard_formats_atom =
880 XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
881 XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);
882 }

  ViewVC Help
Powered by ViewVC 1.1.26