/[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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1038 - (hide annotations)
Thu Jan 5 11:56:57 2006 UTC (18 years, 5 months ago) by astrand
File MIME type: text/plain
File size: 27279 byte(s)
Indentation fix

1 matthewc 432 /* -*- 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 forsberg 1027 /*
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 forsberg 1037 #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 matthewc 432
45 forsberg 1037 #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 jsorg71 450 extern Display *g_display;
54     extern Window g_wnd;
55     extern Time g_last_gesturetime;
56 matthewc 432
57 forsberg 1027 /* 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 forsberg 1037 static Atom format_string_atom, format_utf8_string_atom, format_unicode_atom;
83 forsberg 1027 /* 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 matthewc 432 static XSelectionRequestEvent selection_request;
90 forsberg 1037 /* 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 forsberg 1027 /* Array of offered clipboard targets that will be sent to fellow X clients upon a TARGETS request. */
99 forsberg 1037 static Atom targets[MAX_TARGETS];
100     static int num_targets;
101 forsberg 1027 /* Denotes that this client currently holds the PRIMARY selection. */
102 matthewc 432 static int have_primary = 0;
103 forsberg 1027 /* Denotes that an rdesktop (not this rdesktop) is owning the selection,
104     allowing us to interchange Windows native clipboard data directly. */
105 matthewc 432 static int rdesktop_is_selection_owner = 0;
106    
107 forsberg 1027 /* Denotes that an INCR ("chunked") transfer is in progress. */
108 astrand 913 static int g_waiting_for_INCR = 0;
109 forsberg 1037 /* Denotes the target format of the ongoing INCR ("chunked") transfer. */
110     static Atom g_incr_target = 0;
111 forsberg 1027 /* Buffers an INCR transfer. */
112 astrand 913 static uint8 *g_clip_buffer = 0;
113 forsberg 1027 /* Denotes the size of g_clip_buffer. */
114 astrand 913 static uint32 g_clip_buflen = 0;
115 astrand 551
116 forsberg 1027 /* 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 matthewc 432 static void
121 astrand 551 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 forsberg 1037 #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 astrand 1038 inptr = (uint16 *) data;
150     outptr = (uint16 *) result;
151 forsberg 1037
152     /* Check for a reversed BOM */
153     Bool swap_endianess = (*inptr == 0xfffe);
154    
155 astrand 1038 while ((uint8 *) inptr < data + *size)
156 forsberg 1037 {
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 astrand 1038 *outptr++ = 0; /* null termination */
165     *size = (uint8 *) outptr - result;
166 forsberg 1037
167     return result;
168     }
169     #else
170     /* Translate LF to CR-LF. To do this, we must allocate more memory.
171 astrand 551 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 forsberg 1037 #endif
197 astrand 551
198     static void
199 astrand 435 xclip_provide_selection(XSelectionRequestEvent * req, Atom type, unsigned int format, uint8 * data,
200     uint32 length)
201 matthewc 432 {
202     XEvent xev;
203    
204 jsorg71 450 XChangeProperty(g_display, req->requestor, req->property,
205 matthewc 432 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 jsorg71 450 XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);
216 matthewc 432 }
217    
218 forsberg 1037 /* 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 astrand 1038 xclip_send_data_with_convert(uint8 * source, size_t source_size, Atom target)
264 forsberg 1037 {
265 astrand 1038 #ifdef USE_UNICODE_CLIPBOARD
266 forsberg 1037 if (target == format_string_atom ||
267 astrand 1038 target == format_unicode_atom || target == format_utf8_string_atom)
268 forsberg 1037 {
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 astrand 1038 char *unicode_buffer;
279 forsberg 1037 iconv_t cd;
280    
281     if (target == format_string_atom)
282     {
283 astrand 1038 char *locale_charset = nl_langinfo(CODESET);
284 forsberg 1037 cd = iconv_open(WINDOWS_CODEPAGE, locale_charset);
285 astrand 1038 if (cd == (iconv_t) - 1)
286 forsberg 1037 {
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 astrand 1038 if (cd == (iconv_t) - 1)
296 forsberg 1037 {
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 astrand 1038 if (cd == (iconv_t) - 1)
305 forsberg 1037 {
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 astrand 1038 char *unicode_buffer_remaining = unicode_buffer;
321     char *data_remaining = (char *) source;
322 forsberg 1037 size_t data_size_remaining = source_size;
323 astrand 1038 iconv(cd, &data_remaining, &data_size_remaining, &unicode_buffer_remaining,
324     &unicode_buffer_size_remaining);
325 forsberg 1037 iconv_close(cd);
326    
327     /* translate linebreaks */
328     uint32 translated_data_size = unicode_buffer_size - unicode_buffer_size_remaining;
329 astrand 1038 uint8 *translated_data =
330     utf16_lf2crlf((uint8 *) unicode_buffer, &translated_data_size);
331 forsberg 1037 if (translated_data != NULL)
332     {
333 astrand 1038 DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n",
334     translated_data_size));
335 forsberg 1037 cliprdr_send_data(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 astrand 1038 #else
344 forsberg 1037 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     cliprdr_send_data(translated_data, length);
357     xfree(translated_data); /* Not the same thing as XFree! */
358     }
359    
360     return True;
361     }
362 astrand 1038 #endif
363 forsberg 1037 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 forsberg 1027 /* This function is called for SelectionNotify events.
376 forsberg 1037 The SelectionNotify event is sent from the clipboard owner to the requestor
377 forsberg 1027 after his request was satisfied.
378     If this function is called, we're the requestor side. */
379 astrand 942 #ifndef MAKE_PROTO
380 astrand 462 void
381 matthewc 432 xclip_handle_SelectionNotify(XSelectionEvent * event)
382     {
383     unsigned long nitems, bytes_left;
384 astrand 913 XWindowAttributes wa;
385 forsberg 1037 Atom type;
386 matthewc 432 Atom *supported_targets;
387     int res, i, format;
388     uint8 *data;
389    
390     if (event->property == None)
391     goto fail;
392    
393     DEBUG_CLIPBOARD(("xclip_handle_SelectionNotify: selection=%s, target=%s, property=%s\n",
394 jsorg71 450 XGetAtomName(g_display, event->selection),
395     XGetAtomName(g_display, event->target),
396     XGetAtomName(g_display, event->property)));
397 matthewc 432
398     if (event->property == None)
399     goto fail;
400    
401 jsorg71 450 res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
402 forsberg 1026 0, XMaxRequestSize(g_display), False, AnyPropertyType,
403 matthewc 432 &type, &format, &nitems, &bytes_left, &data);
404    
405     if (res != Success)
406     {
407     DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
408     goto fail;
409     }
410    
411 forsberg 1026 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 forsberg 1037 g_incr_target = event->target;
423 forsberg 1026 g_waiting_for_INCR = 1;
424     return;
425 astrand 1028 }
426    
427 forsberg 1026 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
428 astrand 1028
429 astrand 551 /* Negotiate target format */
430 matthewc 432 if (event->target == targets_atom)
431     {
432 forsberg 1037 /* 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 astrand 1038 Atom best_text_target = 0; /* measures how much we're satisfied with what we found */
438 matthewc 432 if (type != None)
439     {
440     supported_targets = (Atom *) data;
441     for (i = 0; i < nitems; i++)
442     {
443 astrand 435 DEBUG_CLIPBOARD(("Target %d: %s\n", i,
444 jsorg71 450 XGetAtomName(g_display, supported_targets[i])));
445 forsberg 1037 if (supported_targets[i] == format_string_atom)
446 matthewc 432 {
447 forsberg 1037 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 matthewc 432 }
454 astrand 1038 #ifdef USE_UNICODE_CLIPBOARD
455 forsberg 1037 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 astrand 1038 #endif
474 matthewc 432 }
475     }
476    
477 forsberg 1037 /* 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 astrand 1038 XConvertSelection(g_display, clipboard_atom, best_text_target,
483     rdesktop_clipboard_target_atom, g_wnd, event->time);
484 forsberg 1037 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 matthewc 432 }
492 astrand 551 else
493     {
494 forsberg 1037 if (!xclip_send_data_with_convert(data, nitems, event->target))
495     {
496     goto fail;
497     }
498 astrand 551 }
499 forsberg 1037
500 matthewc 432 XFree(data);
501    
502     return;
503    
504 astrand 1028 fail:
505 forsberg 1026 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
506     XFree(data);
507 forsberg 1037 helper_cliprdr_send_empty_response();
508 matthewc 432 }
509    
510 forsberg 1027 /* This function is called for SelectionRequest events.
511 forsberg 1037 The SelectionRequest event is sent from the requestor to the clipboard owner
512 forsberg 1027 to request clipboard data.
513     */
514 astrand 462 void
515 astrand 435 xclip_handle_SelectionRequest(XSelectionRequestEvent * event)
516 matthewc 432 {
517     unsigned long nitems, bytes_left;
518 astrand 465 unsigned char *prop_return;
519 matthewc 432 uint32 *wanted_format;
520     int format, res;
521     Atom type;
522    
523     DEBUG_CLIPBOARD(("xclip_handle_SelectionRequest: selection=%s, target=%s, property=%s\n",
524 jsorg71 450 XGetAtomName(g_display, event->selection),
525     XGetAtomName(g_display, event->target),
526     XGetAtomName(g_display, event->property)));
527 matthewc 432
528     if (event->target == targets_atom)
529     {
530 forsberg 1037 xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, num_targets);
531 matthewc 432 return;
532     }
533     else if (event->target == timestamp_atom)
534     {
535 jsorg71 450 xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);
536 matthewc 432 return;
537     }
538     else
539     {
540 forsberg 1037 /* All the following targets require an async operation with the RDP server
541     and currently we don't do X clipboard request queueing so we can only
542     handle one such request at a time. */
543     if (has_selection_request)
544     {
545     DEBUG_CLIPBOARD(("Error: Another clipboard request was already sent to the RDP server and not yet responded. Refusing this request.\n"));
546     xclip_refuse_selection(event);
547     return;
548     }
549     if (event->target == rdesktop_clipboard_formats_atom)
550     {
551     /* Before the requestor makes a request for the _RDESKTOP_CLIPBOARD_FORMATS target,
552     he should declare requestor[_RDESKTOP_CLIPBOARD_TARGET] = CF_SOMETHING.
553     Otherwise, we default to RDP_CF_TEXT.
554     */
555     res = XGetWindowProperty(g_display, event->requestor,
556 astrand 1038 rdesktop_clipboard_target_atom, 0, 1, True,
557     XA_INTEGER, &type, &format, &nitems, &bytes_left,
558     &prop_return);
559 forsberg 1037 wanted_format = (uint32 *) prop_return;
560     format = (res == Success) ? *wanted_format : RDP_CF_TEXT;
561     XFree(prop_return);
562     }
563 astrand 1038 else if (event->target == format_string_atom || event->target == XA_STRING)
564 forsberg 1037 {
565     /* STRING and XA_STRING are defined to be ISO8859-1 */
566     format = CF_TEXT;
567     }
568     else if (event->target == format_utf8_string_atom)
569     {
570 astrand 1038 #ifdef USE_UNICODE_CLIPBOARD
571 forsberg 1037 format = CF_UNICODETEXT;
572 astrand 1038 #else
573 forsberg 1037 DEBUG_CLIPBOARD(("Requested target unavailable due to lack of Unicode support. (It was not in TARGETS, so why did you ask for it?!)\n"));
574     xclip_refuse_selection(event);
575     return;
576 astrand 1038 #endif
577 forsberg 1037 }
578     else if (event->target == format_unicode_atom)
579     {
580     /* Assuming text/unicode to be UTF-16 */
581     format = CF_UNICODETEXT;
582     }
583     else
584     {
585     DEBUG_CLIPBOARD(("Requested target unavailable. (It was not in TARGETS, so why did you ask for it?!)\n"));
586     xclip_refuse_selection(event);
587     return;
588     }
589    
590     cliprdr_send_data_request(format);
591     selection_request = *event;
592     has_selection_request = True;
593 astrand 1038 return; /* wait for data */
594 matthewc 432 }
595     }
596    
597 forsberg 1037 /* While this rdesktop holds ownership over the clipboard, it means the clipboard data
598     is offered by the RDP server (and when it is pasted inside RDP, there's no network
599     roundtrip).
600    
601     This event (SelectionClear) symbolizes this rdesktop lost onwership of the clipboard
602 forsberg 1027 to some other X client. We should find out what clipboard formats this other
603     client offers and announce that to RDP. */
604 astrand 462 void
605 matthewc 432 xclip_handle_SelectionClear(void)
606     {
607     DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
608     have_primary = 0;
609 jsorg71 450 XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);
610 forsberg 1037 /* FIXME:
611     Without XFIXES, we cannot reliably know the formats offered by the
612     new owner of the X11 clipboard, so we just lie about him
613     offering RDP_CF_TEXT. */
614     cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
615 matthewc 432 }
616    
617 forsberg 1027 /* Called when any property changes in our window or the root window. */
618 astrand 462 void
619 astrand 435 xclip_handle_PropertyNotify(XPropertyEvent * event)
620 matthewc 432 {
621 astrand 1028 unsigned long nitems;
622 forsberg 1026 unsigned long offset = 0;
623     unsigned long bytes_left = 1;
624 matthewc 432 int format, res;
625 astrand 913 XWindowAttributes wa;
626 matthewc 432 uint8 *data;
627     Atom type;
628    
629 astrand 913 if (event->state == PropertyNewValue && g_waiting_for_INCR)
630     {
631     DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: g_waiting_for_INCR != 0\n"));
632    
633 astrand 1028 while (bytes_left > 0)
634     {
635 forsberg 1037 /* Unlike the specification, we don't set the 'delete' arugment to True
636     since we slurp the INCR's chunks in even-smaller chunks of 4096 bytes. */
637 astrand 1028 if ((XGetWindowProperty
638     (g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,
639     False, AnyPropertyType, &type, &format, &nitems, &bytes_left,
640     &data) != Success))
641 forsberg 1026 {
642     XFree(data);
643     return;
644     }
645 astrand 913
646 forsberg 1026 if (nitems == 0)
647 astrand 913 {
648 forsberg 1037 /* INCR transfer finished */
649 forsberg 1026 XGetWindowAttributes(g_display, g_wnd, &wa);
650 astrand 1028 XSelectInput(g_display, g_wnd,
651     (wa.your_event_mask ^ PropertyChangeMask));
652 forsberg 1026 XFree(data);
653     g_waiting_for_INCR = 0;
654 astrand 913
655 forsberg 1026 if (g_clip_buflen > 0)
656     {
657 astrand 1038 if (!xclip_send_data_with_convert
658     (g_clip_buffer, g_clip_buflen, g_incr_target))
659 forsberg 1037 {
660     helper_cliprdr_send_empty_response();
661     }
662 forsberg 1026 xfree(g_clip_buffer);
663 forsberg 1037 g_clip_buffer = NULL;
664 forsberg 1026 g_clip_buflen = 0;
665     }
666 astrand 913 }
667 forsberg 1026 else
668     {
669 forsberg 1037 /* Another chunk in the INCR transfer */
670 astrand 1038 offset += (nitems / 4); /* offset at which to begin the next slurp */
671 forsberg 1037 g_clip_buffer = xrealloc(g_clip_buffer, g_clip_buflen + nitems);
672     memcpy(g_clip_buffer + g_clip_buflen, data, nitems);
673     g_clip_buflen += nitems;
674 astrand 913
675 forsberg 1026 XFree(data);
676     }
677 astrand 913 }
678 forsberg 1026 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
679 forsberg 1037 return;
680 astrand 913 }
681    
682 forsberg 1037 if ((event->atom == rdesktop_clipboard_formats_atom) &&
683     (event->window == DefaultRootWindow(g_display)) &&
684 astrand 1038 !have_primary /* not interested in our own events */ )
685 forsberg 1037 {
686     if (event->state == PropertyNewValue)
687     {
688     DEBUG_CLIPBOARD(("xclip_handle_PropertyNotify: getting fellow rdesktop formats\n"));
689 matthewc 432
690 forsberg 1037 res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),
691     rdesktop_clipboard_formats_atom, 0,
692 astrand 1038 XMaxRequestSize(g_display), False, XA_STRING,
693     &type, &format, &nitems, &bytes_left, &data);
694 matthewc 432
695 forsberg 1037 if ((res == Success) && (nitems > 0))
696     {
697     cliprdr_send_native_format_announce(data, nitems);
698     rdesktop_is_selection_owner = 1;
699     return;
700     }
701     }
702 matthewc 432
703 forsberg 1037 /* For some reason, we couldn't announce the native formats */
704     cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
705     rdesktop_is_selection_owner = 0;
706 matthewc 432 }
707     }
708 astrand 942 #endif
709 matthewc 432
710    
711 forsberg 1027 /* Called when the RDP server announces new clipboard data formats.
712     In response, we:
713     - take ownership over the clipboard
714     - declare those formats in their Windows native form
715     to other rdesktop instances on this X server */
716 matthewc 432 void
717 astrand 542 ui_clip_format_announce(uint8 * data, uint32 length)
718 matthewc 432 {
719 jsorg71 450 XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime);
720     if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
721 matthewc 432 {
722     warning("Failed to aquire ownership of PRIMARY clipboard\n");
723     return;
724     }
725    
726     have_primary = 1;
727 jsorg71 450 XChangeProperty(g_display, DefaultRootWindow(g_display),
728 astrand 435 rdesktop_clipboard_formats_atom, XA_STRING, 8, PropModeReplace, data,
729     length);
730 matthewc 432
731 jsorg71 450 XSetSelectionOwner(g_display, clipboard_atom, g_wnd, g_last_gesturetime);
732     if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)
733 matthewc 432 warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
734     }
735    
736 forsberg 1037 /* Called when the RDP server responds with clipboard data (after we've requested it). */
737 matthewc 432 void
738 astrand 542 ui_clip_handle_data(uint8 * data, uint32 length)
739 matthewc 432 {
740 forsberg 1037 BOOL free_data = False;
741 astrand 551
742 astrand 1038 if (selection_request.target == format_string_atom || selection_request.target == XA_STRING)
743     {
744 forsberg 1037 /* We're expecting a CF_TEXT response */
745 astrand 1038 uint8 *firstnull;
746 astrand 551
747 astrand 1038 /* translate linebreaks */
748     crlf2lf(data, &length);
749 forsberg 1037
750 astrand 1038 /* Only send data up to null byte, if any */
751     firstnull = (uint8 *) strchr((char *) data, '\0');
752     if (firstnull)
753     {
754     length = firstnull - data + 1;
755     }
756     }
757 forsberg 1037 #ifdef USE_UNICODE_CLIPBOARD
758     else if (selection_request.target == format_utf8_string_atom)
759     {
760     /* We're expecting a CF_UNICODETEXT response */
761     iconv_t cd = iconv_open("UTF-8", WINDOWS_CODEPAGE);
762 astrand 1038 if (cd != (iconv_t) - 1)
763 astrand 551 {
764 forsberg 1037 size_t utf8_length = length * 2;
765 astrand 1038 char *utf8_data = malloc(utf8_length);
766 forsberg 1037 size_t utf8_length_remaining = utf8_length;
767 astrand 1038 char *utf8_data_remaining = utf8_data;
768     char *data_remaining = (char *) data;
769     size_t length_remaining = (size_t) length;
770 forsberg 1037 if (utf8_data == NULL)
771     {
772     iconv_close(cd);
773     return;
774     }
775 astrand 1038 iconv(cd, &data_remaining, &length_remaining, &utf8_data_remaining,
776     &utf8_length_remaining);
777 forsberg 1037 iconv_close(cd);
778     free_data = True;
779 astrand 1038 data = (uint8 *) utf8_data;
780 forsberg 1037 length = utf8_length - utf8_length_remaining;
781 astrand 551 }
782     }
783 forsberg 1037 else if (selection_request.target == format_unicode_atom)
784     {
785     /* We're expecting a CF_UNICODETEXT response, so what we're
786     receiving matches our requirements and there's no need
787     for further conversions. */
788     }
789     #endif
790     else if (selection_request.target == rdesktop_clipboard_formats_atom)
791     {
792     /* Pass as-is */
793     }
794     else
795     {
796     xclip_refuse_selection(&selection_request);
797     has_selection_request = False;
798     return;
799     }
800 astrand 551
801 forsberg 1037 xclip_provide_selection(&selection_request, selection_request.target, 8, data, length - 1);
802     has_selection_request = False;
803    
804     if (free_data)
805     free(data);
806 matthewc 432 }
807    
808     void
809     ui_clip_request_data(uint32 format)
810     {
811     Window selectionowner;
812    
813     DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
814 forsberg 1037 rdp_clipboard_request_format = format;
815 matthewc 432
816     if (rdesktop_is_selection_owner)
817     {
818 jsorg71 450 XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
819 matthewc 432 XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
820    
821 jsorg71 450 XConvertSelection(g_display, primary_atom, rdesktop_clipboard_formats_atom,
822     rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
823 matthewc 432 return;
824     }
825    
826 jsorg71 450 selectionowner = XGetSelectionOwner(g_display, primary_atom);
827 matthewc 432 if (selectionowner != None)
828     {
829 jsorg71 450 XConvertSelection(g_display, primary_atom, targets_atom,
830     rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
831 matthewc 432 return;
832     }
833    
834     /* No PRIMARY, try CLIPBOARD */
835 jsorg71 450 selectionowner = XGetSelectionOwner(g_display, clipboard_atom);
836 matthewc 432 if (selectionowner != None)
837     {
838 jsorg71 450 XConvertSelection(g_display, clipboard_atom, targets_atom,
839     rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
840 matthewc 432 return;
841     }
842    
843     /* No data available */
844     cliprdr_send_data(NULL, 0);
845     }
846    
847     void
848     ui_clip_sync(void)
849     {
850 forsberg 1037 cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
851 matthewc 432 }
852    
853    
854     void
855     xclip_init(void)
856     {
857     if (!cliprdr_init())
858     return;
859    
860 jsorg71 450 primary_atom = XInternAtom(g_display, "PRIMARY", False);
861     clipboard_atom = XInternAtom(g_display, "CLIPBOARD", False);
862     targets_atom = XInternAtom(g_display, "TARGETS", False);
863     timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);
864 astrand 456 rdesktop_clipboard_target_atom =
865     XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
866 jsorg71 450 incr_atom = XInternAtom(g_display, "INCR", False);
867 forsberg 1037 format_string_atom = XInternAtom(g_display, "STRING", False);
868     format_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False);
869     format_unicode_atom = XInternAtom(g_display, "text/unicode", False);
870     num_targets = 0;
871     targets[num_targets++] = targets_atom;
872     targets[num_targets++] = timestamp_atom;
873     targets[num_targets++] = rdesktop_clipboard_formats_atom;
874     targets[num_targets++] = format_string_atom;
875 astrand 1038 #ifdef USE_UNICODE_CLIPBOARD
876 forsberg 1037 targets[num_targets++] = format_utf8_string_atom;
877 astrand 1038 #endif
878 forsberg 1037 targets[num_targets++] = format_unicode_atom;
879     targets[num_targets++] = XA_STRING;
880 matthewc 432
881     /* rdesktop sets _RDESKTOP_CLIPBOARD_FORMATS on the root window when acquiring the clipboard.
882     Other interested rdesktops can use this to notify their server of the available formats. */
883 astrand 435 rdesktop_clipboard_formats_atom =
884 jsorg71 450 XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
885     XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);
886 matthewc 432 }

  ViewVC Help
Powered by ViewVC 1.1.26