/[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 1212 - (hide annotations)
Mon Mar 27 13:03:27 2006 UTC (18 years, 3 months ago) by ossman_
File MIME type: text/plain
File size: 31527 byte(s)
Examine the magic root window property on startup so that we correctly handle
when another rdesktop owns the clipboard when we start.

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 ossman_ 1207 extern BOOL g_rdpclip;
57 matthewc 432
58 ossman_ 1207 /* Mode of operation.
59     - Auto: Look at both PRIMARY and CLIPBOARD and use the most recent.
60     - Non-auto: Look at just CLIPBOARD. */
61     static BOOL auto_mode = True;
62 forsberg 1027 /* Atoms of the two X selections we're dealing with: CLIPBOARD (explicit-copy) and PRIMARY (selection-copy) */
63     static Atom clipboard_atom, primary_atom;
64     /* Atom of the TARGETS clipboard target */
65     static Atom targets_atom;
66     /* Atom of the TIMESTAMP clipboard target */
67     static Atom timestamp_atom;
68     /* Atom _RDESKTOP_CLIPBOARD_TARGET which has multiple uses:
69     - The 'property' argument in XConvertSelection calls: This is the property of our
70     window into which XConvertSelection will store the received clipboard data.
71     - In a clipboard request of target _RDESKTOP_CLIPBOARD_FORMATS, an XA_INTEGER-typed
72     property carrying the Windows native (CF_...) format desired by the requestor.
73     Requestor set this property (e.g. requestor_wnd[_RDESKTOP_CLIPBOARD_TARGET] = CF_TEXT)
74     before requesting clipboard data from a fellow rdesktop using
75     the _RDESKTOP_CLIPBOARD_FORMATS target. */
76     static Atom rdesktop_clipboard_target_atom;
77 ossman_ 1206 /* Atoms _RDESKTOP_PRIMARY_TIMESTAMP_TARGET and _RDESKTOP_CLIPBOARD_TIMESTAMP_TARGET
78     are used to store the timestamps for when a window got ownership of the selections.
79     We use these to determine which is more recent and should be used. */
80     static Atom rdesktop_primary_timestamp_target_atom, rdesktop_clipboard_timestamp_target_atom;
81     /* Storage for timestamps since we get them in two separate notifications. */
82     static Time primary_timestamp, clipboard_timestamp;
83 forsberg 1027 /* Atom _RDESKTOP_CLIPBOARD_FORMATS which has multiple uses:
84     - The clipboard target (X jargon for "clipboard format") for rdesktop-to-rdesktop interchange
85     of Windows native clipboard data.
86     This target cannot be used standalone; the requestor must keep the
87     _RDESKTOP_CLIPBOARD_TARGET property on his window denoting
88     the Windows native clipboard format being requested.
89     - The root window property set by rdesktop when it owns the clipboard,
90     denoting all Windows native clipboard formats it offers via
91     requests of the _RDESKTOP_CLIPBOARD_FORMATS target. */
92     static Atom rdesktop_clipboard_formats_atom;
93 forsberg 1037 static Atom format_string_atom, format_utf8_string_atom, format_unicode_atom;
94 forsberg 1027 /* Atom of the INCR clipboard type (see ICCCM on "INCR Properties") */
95     static Atom incr_atom;
96     /* Stores the last "selection request" (= another X client requesting clipboard data from us).
97     To satisfy such a request, we request the clipboard data from the RDP server.
98     When we receive the response from the RDP server (asynchronously), this variable gives us
99     the context to proceed. */
100 matthewc 432 static XSelectionRequestEvent selection_request;
101 forsberg 1037 /* Denotes we have a pending selection request. */
102     static Bool has_selection_request;
103     /* Stores the clipboard format (CF_TEXT, CF_UNICODETEXT etc.) requested in the last
104     CLIPDR_DATA_REQUEST (= the RDP server requesting clipboard data from us).
105     When we receive this data from whatever X client offering it, this variable gives us
106     the context to proceed.
107     */
108     static int rdp_clipboard_request_format;
109 forsberg 1027 /* Array of offered clipboard targets that will be sent to fellow X clients upon a TARGETS request. */
110 forsberg 1037 static Atom targets[MAX_TARGETS];
111     static int num_targets;
112 forsberg 1027 /* Denotes that this client currently holds the PRIMARY selection. */
113 matthewc 432 static int have_primary = 0;
114 forsberg 1027 /* Denotes that an rdesktop (not this rdesktop) is owning the selection,
115     allowing us to interchange Windows native clipboard data directly. */
116 matthewc 432 static int rdesktop_is_selection_owner = 0;
117 ossman_ 1210 /* Time when we acquired the selection. */
118     static Time acquire_time = 0;
119 matthewc 432
120 forsberg 1027 /* Denotes that an INCR ("chunked") transfer is in progress. */
121 astrand 913 static int g_waiting_for_INCR = 0;
122 forsberg 1037 /* Denotes the target format of the ongoing INCR ("chunked") transfer. */
123     static Atom g_incr_target = 0;
124 forsberg 1027 /* Buffers an INCR transfer. */
125 astrand 913 static uint8 *g_clip_buffer = 0;
126 forsberg 1027 /* Denotes the size of g_clip_buffer. */
127 astrand 913 static uint32 g_clip_buflen = 0;
128 astrand 551
129 forsberg 1027 /* Translate LF to CR-LF. To do this, we must allocate more memory.
130     The returned string is null-terminated, as required by CF_TEXT.
131     Does not stop on embedded nulls.
132     The length is updated. */
133 matthewc 432 static void
134 astrand 551 crlf2lf(uint8 * data, uint32 * length)
135     {
136     uint8 *dst, *src;
137     src = dst = data;
138     while (src < data + *length)
139     {
140     if (*src != '\x0d')
141     *dst++ = *src;
142     src++;
143     }
144     *length = dst - data;
145     }
146    
147 forsberg 1037 #ifdef USE_UNICODE_CLIPBOARD
148     /* Translate LF to CR-LF. To do this, we must allocate more memory.
149     The returned string is null-terminated, as required by CF_UNICODETEXT.
150     The size is updated. */
151     static uint8 *
152     utf16_lf2crlf(uint8 * data, uint32 * size)
153     {
154     uint8 *result;
155     uint16 *inptr, *outptr;
156    
157     /* Worst case: Every char is LF */
158     result = xmalloc((*size * 2) + 2);
159     if (result == NULL)
160     return NULL;
161    
162 astrand 1038 inptr = (uint16 *) data;
163     outptr = (uint16 *) result;
164 forsberg 1037
165     /* Check for a reversed BOM */
166     Bool swap_endianess = (*inptr == 0xfffe);
167    
168 astrand 1038 while ((uint8 *) inptr < data + *size)
169 forsberg 1037 {
170     uint16 uvalue = *inptr;
171     if (swap_endianess)
172     uvalue = ((uvalue << 8) & 0xff00) + (uvalue >> 8);
173     if (uvalue == 0x0a)
174     *outptr++ = swap_endianess ? 0x0d00 : 0x0d;
175     *outptr++ = *inptr++;
176     }
177 astrand 1038 *outptr++ = 0; /* null termination */
178     *size = (uint8 *) outptr - result;
179 forsberg 1037
180     return result;
181     }
182     #else
183     /* Translate LF to CR-LF. To do this, we must allocate more memory.
184 astrand 551 The length is updated. */
185     static uint8 *
186     lf2crlf(uint8 * data, uint32 * length)
187     {
188     uint8 *result, *p, *o;
189    
190     /* Worst case: Every char is LF */
191     result = xmalloc(*length * 2);
192    
193     p = data;
194     o = result;
195    
196     while (p < data + *length)
197     {
198     if (*p == '\x0a')
199     *o++ = '\x0d';
200     *o++ = *p++;
201     }
202     *length = o - result;
203    
204     /* Convenience */
205     *o++ = '\0';
206    
207     return result;
208     }
209 forsberg 1037 #endif
210 astrand 551
211     static void
212 astrand 435 xclip_provide_selection(XSelectionRequestEvent * req, Atom type, unsigned int format, uint8 * data,
213     uint32 length)
214 matthewc 432 {
215     XEvent xev;
216    
217 jsorg71 450 XChangeProperty(g_display, req->requestor, req->property,
218 matthewc 432 type, format, PropModeReplace, data, length);
219    
220     xev.xselection.type = SelectionNotify;
221     xev.xselection.serial = 0;
222     xev.xselection.send_event = True;
223     xev.xselection.requestor = req->requestor;
224     xev.xselection.selection = req->selection;
225     xev.xselection.target = req->target;
226     xev.xselection.property = req->property;
227     xev.xselection.time = req->time;
228 jsorg71 450 XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);
229 matthewc 432 }
230    
231 forsberg 1037 /* Replies a clipboard requestor, telling that we're unable to satisfy his request for whatever reason.
232     This has the benefit of finalizing the clipboard negotiation and thus not leaving our requestor
233     lingering (and, potentially, stuck). */
234     static void
235     xclip_refuse_selection(XSelectionRequestEvent * req)
236     {
237     XEvent xev;
238    
239     xev.xselection.type = SelectionNotify;
240     xev.xselection.serial = 0;
241     xev.xselection.send_event = True;
242     xev.xselection.requestor = req->requestor;
243     xev.xselection.selection = req->selection;
244     xev.xselection.target = req->target;
245     xev.xselection.property = None;
246     xev.xselection.time = req->time;
247     XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);
248     }
249    
250 ossman_ 1212 /* Examines the special root window property that contains the native Windows
251     formats if another rdesktop process owns the clipboard. */
252     static void
253     xclip_get_root_property()
254     {
255     unsigned long nitems;
256     unsigned long bytes_left;
257     int format, res;
258     uint8 *data;
259     Atom type;
260    
261     res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),
262     rdesktop_clipboard_formats_atom, 0,
263     XMaxRequestSize(g_display), False, XA_STRING,
264     &type, &format, &nitems, &bytes_left, &data);
265    
266     if ((res == Success) && (nitems > 0))
267     {
268     DEBUG_CLIPBOARD(("xclip_get_root_property: got fellow rdesktop formats\n"));
269     cliprdr_send_native_format_announce(data, nitems);
270     rdesktop_is_selection_owner = 1;
271     return;
272     }
273    
274     /* For some reason, we couldn't announce the native formats */
275     cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
276     rdesktop_is_selection_owner = 0;
277     }
278    
279 forsberg 1037 /* Wrapper for cliprdr_send_data which also cleans the request state. */
280     static void
281     helper_cliprdr_send_response(uint8 * data, uint32 length)
282     {
283     if (rdp_clipboard_request_format != 0)
284     {
285     cliprdr_send_data(data, length);
286     rdp_clipboard_request_format = 0;
287     if (!rdesktop_is_selection_owner)
288     cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
289     }
290     }
291    
292     /* Last resort, when we have to provide clipboard data but for whatever
293     reason couldn't get any.
294     */
295     static void
296     helper_cliprdr_send_empty_response()
297     {
298     helper_cliprdr_send_response(NULL, 0);
299     }
300    
301     /* Replies with clipboard data to RDP, converting it from the target format
302     to the expected RDP format as necessary. Returns true if data was sent.
303     */
304     static Bool
305 astrand 1038 xclip_send_data_with_convert(uint8 * source, size_t source_size, Atom target)
306 forsberg 1037 {
307 astrand 1038 #ifdef USE_UNICODE_CLIPBOARD
308 forsberg 1037 if (target == format_string_atom ||
309 astrand 1038 target == format_unicode_atom || target == format_utf8_string_atom)
310 forsberg 1037 {
311     if (rdp_clipboard_request_format != RDP_CF_TEXT)
312     return False;
313    
314     /* Make an attempt to convert any string we send to Unicode.
315     We don't know what the RDP server's ANSI Codepage is, or how to convert
316     to it, so using CF_TEXT is not safe (and is unnecessary, since all
317     WinNT versions are Unicode-minded).
318     */
319     size_t unicode_buffer_size;
320 astrand 1038 char *unicode_buffer;
321 forsberg 1037 iconv_t cd;
322    
323     if (target == format_string_atom)
324     {
325 astrand 1038 char *locale_charset = nl_langinfo(CODESET);
326 forsberg 1037 cd = iconv_open(WINDOWS_CODEPAGE, locale_charset);
327 astrand 1038 if (cd == (iconv_t) - 1)
328 forsberg 1037 {
329     DEBUG_CLIPBOARD(("Locale charset %s not found in iconv. Unable to convert clipboard text.\n", locale_charset));
330     return False;
331     }
332     unicode_buffer_size = source_size * 4;
333     }
334     else if (target == format_unicode_atom)
335     {
336     cd = iconv_open(WINDOWS_CODEPAGE, "UCS-2");
337 astrand 1038 if (cd == (iconv_t) - 1)
338 forsberg 1037 {
339     return False;
340     }
341     unicode_buffer_size = source_size;
342     }
343     else if (target == format_utf8_string_atom)
344     {
345     cd = iconv_open(WINDOWS_CODEPAGE, "UTF-8");
346 astrand 1038 if (cd == (iconv_t) - 1)
347 forsberg 1037 {
348     return False;
349     }
350     /* UTF-8 is guaranteed to be less or equally compact
351     as UTF-16 for all Unicode chars >=2 bytes.
352     */
353     unicode_buffer_size = source_size * 2;
354     }
355     else
356     {
357     return False;
358     }
359    
360     unicode_buffer = xmalloc(unicode_buffer_size);
361     size_t unicode_buffer_size_remaining = unicode_buffer_size;
362 astrand 1038 char *unicode_buffer_remaining = unicode_buffer;
363     char *data_remaining = (char *) source;
364 forsberg 1037 size_t data_size_remaining = source_size;
365 stargo 1050 iconv(cd, (ICONV_CONST char **) &data_remaining, &data_size_remaining,
366     &unicode_buffer_remaining, &unicode_buffer_size_remaining);
367 forsberg 1037 iconv_close(cd);
368    
369     /* translate linebreaks */
370     uint32 translated_data_size = unicode_buffer_size - unicode_buffer_size_remaining;
371 astrand 1038 uint8 *translated_data =
372     utf16_lf2crlf((uint8 *) unicode_buffer, &translated_data_size);
373 forsberg 1037 if (translated_data != NULL)
374     {
375 astrand 1038 DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n",
376     translated_data_size));
377 ossman_ 1203 helper_cliprdr_send_response(translated_data, translated_data_size);
378 forsberg 1037 xfree(translated_data); /* Not the same thing as XFree! */
379     }
380    
381     xfree(unicode_buffer);
382    
383     return True;
384     }
385 astrand 1038 #else
386 forsberg 1037 if (target == format_string_atom)
387     {
388     uint8 *translated_data;
389     uint32 length = source_size;
390    
391     if (rdp_clipboard_request_format != RDP_CF_TEXT)
392     return False;
393    
394     DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
395     translated_data = lf2crlf(source, &length);
396     if (translated_data != NULL)
397     {
398 ossman_ 1203 helper_cliprdr_send_response(translated_data, length);
399 forsberg 1037 xfree(translated_data); /* Not the same thing as XFree! */
400     }
401    
402     return True;
403     }
404 astrand 1038 #endif
405 forsberg 1037 else if (target == rdesktop_clipboard_formats_atom)
406     {
407     helper_cliprdr_send_response(source, source_size + 1);
408    
409     return True;
410     }
411     else
412     {
413     return False;
414     }
415     }
416    
417 ossman_ 1206 static void
418     xclip_clear_target_props()
419     {
420     XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
421     XDeleteProperty(g_display, g_wnd, rdesktop_primary_timestamp_target_atom);
422     XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_timestamp_target_atom);
423     }
424    
425 forsberg 1027 /* This function is called for SelectionNotify events.
426 forsberg 1037 The SelectionNotify event is sent from the clipboard owner to the requestor
427 forsberg 1027 after his request was satisfied.
428     If this function is called, we're the requestor side. */
429 astrand 942 #ifndef MAKE_PROTO
430 astrand 462 void
431 matthewc 432 xclip_handle_SelectionNotify(XSelectionEvent * event)
432     {
433     unsigned long nitems, bytes_left;
434 astrand 913 XWindowAttributes wa;
435 forsberg 1037 Atom type;
436 matthewc 432 Atom *supported_targets;
437     int res, i, format;
438 astrand 1048 uint8 *data = NULL;
439 matthewc 432
440     if (event->property == None)
441     goto fail;
442    
443     DEBUG_CLIPBOARD(("xclip_handle_SelectionNotify: selection=%s, target=%s, property=%s\n",
444 jsorg71 450 XGetAtomName(g_display, event->selection),
445     XGetAtomName(g_display, event->target),
446     XGetAtomName(g_display, event->property)));
447 matthewc 432
448 ossman_ 1206 if (event->target == timestamp_atom)
449     {
450     if (event->selection == primary_atom)
451     {
452     res = XGetWindowProperty(g_display, g_wnd,
453     rdesktop_primary_timestamp_target_atom, 0,
454     XMaxRequestSize(g_display), False, XA_INTEGER,
455     &type, &format, &nitems, &bytes_left, &data);
456     }
457     else
458     {
459     res = XGetWindowProperty(g_display, g_wnd,
460     rdesktop_clipboard_timestamp_target_atom, 0,
461     XMaxRequestSize(g_display), False, XA_INTEGER,
462     &type, &format, &nitems, &bytes_left, &data);
463     }
464 matthewc 432
465 ossman_ 1206
466     if ((res != Success) || (nitems != 1))
467     {
468     DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
469     goto fail;
470     }
471    
472     if (event->selection == primary_atom)
473     {
474     primary_timestamp = *(Time *) data;
475     if (primary_timestamp == 0)
476     primary_timestamp++;
477     XDeleteProperty(g_display, g_wnd, rdesktop_primary_timestamp_target_atom);
478     DEBUG_CLIPBOARD(("Got PRIMARY timestamp: %u\n",
479     (unsigned) primary_timestamp));
480     }
481     else
482     {
483     clipboard_timestamp = *(Time *) data;
484     if (clipboard_timestamp == 0)
485     clipboard_timestamp++;
486     XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_timestamp_target_atom);
487     DEBUG_CLIPBOARD(("Got CLIPBOARD timestamp: %u\n",
488     (unsigned) clipboard_timestamp));
489     }
490    
491     XFree(data);
492    
493     if (primary_timestamp && clipboard_timestamp)
494     {
495     if (primary_timestamp > clipboard_timestamp)
496     {
497     DEBUG_CLIPBOARD(("PRIMARY is most recent selection.\n"));
498     XConvertSelection(g_display, primary_atom, targets_atom,
499     rdesktop_clipboard_target_atom, g_wnd,
500     event->time);
501     }
502     else
503     {
504     DEBUG_CLIPBOARD(("CLIPBOARD is most recent selection.\n"));
505     XConvertSelection(g_display, clipboard_atom, targets_atom,
506     rdesktop_clipboard_target_atom, g_wnd,
507     event->time);
508     }
509     }
510    
511     return;
512     }
513    
514 jsorg71 450 res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
515 forsberg 1026 0, XMaxRequestSize(g_display), False, AnyPropertyType,
516 matthewc 432 &type, &format, &nitems, &bytes_left, &data);
517    
518 ossman_ 1206 xclip_clear_target_props();
519    
520 matthewc 432 if (res != Success)
521     {
522     DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
523     goto fail;
524     }
525    
526 forsberg 1026 if (type == incr_atom)
527     {
528     DEBUG_CLIPBOARD(("Received INCR.\n"));
529    
530     XGetWindowAttributes(g_display, g_wnd, &wa);
531     if ((wa.your_event_mask | PropertyChangeMask) != wa.your_event_mask)
532     {
533     XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));
534     }
535     XFree(data);
536 forsberg 1037 g_incr_target = event->target;
537 forsberg 1026 g_waiting_for_INCR = 1;
538     return;
539 astrand 1028 }
540    
541 astrand 551 /* Negotiate target format */
542 matthewc 432 if (event->target == targets_atom)
543     {
544 forsberg 1037 /* Determine the best of text targets that we have available:
545     Prefer UTF8_STRING > text/unicode (unspecified encoding) > STRING
546     (ignore TEXT and COMPOUND_TEXT because we don't have code to handle them)
547     */
548     int text_target_satisfaction = 0;
549 astrand 1038 Atom best_text_target = 0; /* measures how much we're satisfied with what we found */
550 matthewc 432 if (type != None)
551     {
552     supported_targets = (Atom *) data;
553     for (i = 0; i < nitems; i++)
554     {
555 astrand 435 DEBUG_CLIPBOARD(("Target %d: %s\n", i,
556 jsorg71 450 XGetAtomName(g_display, supported_targets[i])));
557 forsberg 1037 if (supported_targets[i] == format_string_atom)
558 matthewc 432 {
559 forsberg 1037 if (text_target_satisfaction < 1)
560     {
561     DEBUG_CLIPBOARD(("Other party supports STRING, choosing that as best_target\n"));
562     best_text_target = supported_targets[i];
563     text_target_satisfaction = 1;
564     }
565 matthewc 432 }
566 astrand 1038 #ifdef USE_UNICODE_CLIPBOARD
567 forsberg 1037 else if (supported_targets[i] == format_unicode_atom)
568     {
569     if (text_target_satisfaction < 2)
570     {
571     DEBUG_CLIPBOARD(("Other party supports text/unicode, choosing that as best_target\n"));
572     best_text_target = supported_targets[i];
573     text_target_satisfaction = 2;
574     }
575     }
576     else if (supported_targets[i] == format_utf8_string_atom)
577     {
578     if (text_target_satisfaction < 3)
579     {
580     DEBUG_CLIPBOARD(("Other party supports UTF8_STRING, choosing that as best_target\n"));
581     best_text_target = supported_targets[i];
582     text_target_satisfaction = 3;
583     }
584     }
585 astrand 1038 #endif
586 matthewc 432 }
587     }
588    
589 forsberg 1037 /* Kickstarting the next step in the process of satisfying RDP's
590     clipboard request -- specifically, requesting the actual clipboard data.
591     */
592     if (best_text_target != 0)
593     {
594 ossman_ 1205 XConvertSelection(g_display, event->selection, best_text_target,
595 astrand 1038 rdesktop_clipboard_target_atom, g_wnd, event->time);
596 forsberg 1037 return;
597     }
598     else
599     {
600     DEBUG_CLIPBOARD(("Unable to find a textual target to satisfy RDP clipboard text request\n"));
601     goto fail;
602     }
603 matthewc 432 }
604 astrand 551 else
605     {
606 forsberg 1037 if (!xclip_send_data_with_convert(data, nitems, event->target))
607     {
608     goto fail;
609     }
610 astrand 551 }
611 forsberg 1037
612 matthewc 432 XFree(data);
613    
614     return;
615    
616 astrand 1028 fail:
617 ossman_ 1206 xclip_clear_target_props();
618 astrand 1048 if (data)
619     XFree(data);
620 forsberg 1037 helper_cliprdr_send_empty_response();
621 matthewc 432 }
622    
623 forsberg 1027 /* This function is called for SelectionRequest events.
624 forsberg 1037 The SelectionRequest event is sent from the requestor to the clipboard owner
625 forsberg 1027 to request clipboard data.
626     */
627 astrand 462 void
628 astrand 435 xclip_handle_SelectionRequest(XSelectionRequestEvent * event)
629 matthewc 432 {
630     unsigned long nitems, bytes_left;
631 astrand 465 unsigned char *prop_return;
632 matthewc 432 uint32 *wanted_format;
633     int format, res;
634     Atom type;
635    
636     DEBUG_CLIPBOARD(("xclip_handle_SelectionRequest: selection=%s, target=%s, property=%s\n",
637 jsorg71 450 XGetAtomName(g_display, event->selection),
638     XGetAtomName(g_display, event->target),
639     XGetAtomName(g_display, event->property)));
640 matthewc 432
641     if (event->target == targets_atom)
642     {
643 forsberg 1037 xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, num_targets);
644 matthewc 432 return;
645     }
646     else if (event->target == timestamp_atom)
647     {
648 ossman_ 1210 xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & acquire_time, 1);
649 matthewc 432 return;
650     }
651     else
652     {
653 forsberg 1037 /* All the following targets require an async operation with the RDP server
654     and currently we don't do X clipboard request queueing so we can only
655     handle one such request at a time. */
656     if (has_selection_request)
657     {
658     DEBUG_CLIPBOARD(("Error: Another clipboard request was already sent to the RDP server and not yet responded. Refusing this request.\n"));
659     xclip_refuse_selection(event);
660     return;
661     }
662     if (event->target == rdesktop_clipboard_formats_atom)
663     {
664     /* Before the requestor makes a request for the _RDESKTOP_CLIPBOARD_FORMATS target,
665     he should declare requestor[_RDESKTOP_CLIPBOARD_TARGET] = CF_SOMETHING.
666     Otherwise, we default to RDP_CF_TEXT.
667     */
668     res = XGetWindowProperty(g_display, event->requestor,
669 astrand 1038 rdesktop_clipboard_target_atom, 0, 1, True,
670     XA_INTEGER, &type, &format, &nitems, &bytes_left,
671     &prop_return);
672 forsberg 1037 wanted_format = (uint32 *) prop_return;
673     format = (res == Success) ? *wanted_format : RDP_CF_TEXT;
674     XFree(prop_return);
675     }
676 astrand 1038 else if (event->target == format_string_atom || event->target == XA_STRING)
677 forsberg 1037 {
678     /* STRING and XA_STRING are defined to be ISO8859-1 */
679     format = CF_TEXT;
680     }
681     else if (event->target == format_utf8_string_atom)
682     {
683 astrand 1038 #ifdef USE_UNICODE_CLIPBOARD
684 forsberg 1037 format = CF_UNICODETEXT;
685 astrand 1038 #else
686 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"));
687     xclip_refuse_selection(event);
688     return;
689 astrand 1038 #endif
690 forsberg 1037 }
691     else if (event->target == format_unicode_atom)
692     {
693     /* Assuming text/unicode to be UTF-16 */
694     format = CF_UNICODETEXT;
695     }
696     else
697     {
698     DEBUG_CLIPBOARD(("Requested target unavailable. (It was not in TARGETS, so why did you ask for it?!)\n"));
699     xclip_refuse_selection(event);
700     return;
701     }
702    
703     cliprdr_send_data_request(format);
704     selection_request = *event;
705     has_selection_request = True;
706 astrand 1038 return; /* wait for data */
707 matthewc 432 }
708     }
709    
710 forsberg 1037 /* While this rdesktop holds ownership over the clipboard, it means the clipboard data
711     is offered by the RDP server (and when it is pasted inside RDP, there's no network
712     roundtrip).
713    
714     This event (SelectionClear) symbolizes this rdesktop lost onwership of the clipboard
715 forsberg 1027 to some other X client. We should find out what clipboard formats this other
716     client offers and announce that to RDP. */
717 astrand 462 void
718 matthewc 432 xclip_handle_SelectionClear(void)
719     {
720     DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
721     have_primary = 0;
722 jsorg71 450 XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);
723 forsberg 1037 /* FIXME:
724     Without XFIXES, we cannot reliably know the formats offered by the
725     new owner of the X11 clipboard, so we just lie about him
726     offering RDP_CF_TEXT. */
727     cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
728 matthewc 432 }
729    
730 forsberg 1027 /* Called when any property changes in our window or the root window. */
731 astrand 462 void
732 astrand 435 xclip_handle_PropertyNotify(XPropertyEvent * event)
733 matthewc 432 {
734 astrand 1028 unsigned long nitems;
735 forsberg 1026 unsigned long offset = 0;
736     unsigned long bytes_left = 1;
737 ossman_ 1212 int format;
738 astrand 913 XWindowAttributes wa;
739 matthewc 432 uint8 *data;
740     Atom type;
741    
742 astrand 913 if (event->state == PropertyNewValue && g_waiting_for_INCR)
743     {
744     DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: g_waiting_for_INCR != 0\n"));
745    
746 astrand 1028 while (bytes_left > 0)
747     {
748 forsberg 1037 /* Unlike the specification, we don't set the 'delete' arugment to True
749     since we slurp the INCR's chunks in even-smaller chunks of 4096 bytes. */
750 astrand 1028 if ((XGetWindowProperty
751     (g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,
752     False, AnyPropertyType, &type, &format, &nitems, &bytes_left,
753     &data) != Success))
754 forsberg 1026 {
755     XFree(data);
756     return;
757     }
758 astrand 913
759 forsberg 1026 if (nitems == 0)
760 astrand 913 {
761 forsberg 1037 /* INCR transfer finished */
762 forsberg 1026 XGetWindowAttributes(g_display, g_wnd, &wa);
763 astrand 1028 XSelectInput(g_display, g_wnd,
764     (wa.your_event_mask ^ PropertyChangeMask));
765 forsberg 1026 XFree(data);
766     g_waiting_for_INCR = 0;
767 astrand 913
768 forsberg 1026 if (g_clip_buflen > 0)
769     {
770 astrand 1038 if (!xclip_send_data_with_convert
771     (g_clip_buffer, g_clip_buflen, g_incr_target))
772 forsberg 1037 {
773     helper_cliprdr_send_empty_response();
774     }
775 forsberg 1026 xfree(g_clip_buffer);
776 forsberg 1037 g_clip_buffer = NULL;
777 forsberg 1026 g_clip_buflen = 0;
778     }
779 astrand 913 }
780 forsberg 1026 else
781     {
782 forsberg 1037 /* Another chunk in the INCR transfer */
783 astrand 1038 offset += (nitems / 4); /* offset at which to begin the next slurp */
784 forsberg 1037 g_clip_buffer = xrealloc(g_clip_buffer, g_clip_buflen + nitems);
785     memcpy(g_clip_buffer + g_clip_buflen, data, nitems);
786     g_clip_buflen += nitems;
787 astrand 913
788 forsberg 1026 XFree(data);
789     }
790 astrand 913 }
791 forsberg 1026 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
792 forsberg 1037 return;
793 astrand 913 }
794    
795 forsberg 1037 if ((event->atom == rdesktop_clipboard_formats_atom) &&
796     (event->window == DefaultRootWindow(g_display)) &&
797 astrand 1038 !have_primary /* not interested in our own events */ )
798 ossman_ 1212 xclip_get_root_property();
799 matthewc 432 }
800 astrand 942 #endif
801 matthewc 432
802    
803 forsberg 1027 /* Called when the RDP server announces new clipboard data formats.
804     In response, we:
805     - take ownership over the clipboard
806     - declare those formats in their Windows native form
807     to other rdesktop instances on this X server */
808 matthewc 432 void
809 astrand 542 ui_clip_format_announce(uint8 * data, uint32 length)
810 matthewc 432 {
811 ossman_ 1210 acquire_time = g_last_gesturetime;
812    
813     XSetSelectionOwner(g_display, primary_atom, g_wnd, acquire_time);
814 jsorg71 450 if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
815 matthewc 432 {
816     warning("Failed to aquire ownership of PRIMARY clipboard\n");
817     return;
818     }
819    
820     have_primary = 1;
821 jsorg71 450 XChangeProperty(g_display, DefaultRootWindow(g_display),
822 astrand 435 rdesktop_clipboard_formats_atom, XA_STRING, 8, PropModeReplace, data,
823     length);
824 matthewc 432
825 ossman_ 1210 XSetSelectionOwner(g_display, clipboard_atom, g_wnd, acquire_time);
826 jsorg71 450 if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)
827 matthewc 432 warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
828     }
829    
830 forsberg 1037 /* Called when the RDP server responds with clipboard data (after we've requested it). */
831 matthewc 432 void
832 astrand 542 ui_clip_handle_data(uint8 * data, uint32 length)
833 matthewc 432 {
834 forsberg 1037 BOOL free_data = False;
835 astrand 551
836 ossman_ 1209 if (length == 0)
837     {
838     xclip_refuse_selection(&selection_request);
839     has_selection_request = False;
840     return;
841     }
842    
843 astrand 1038 if (selection_request.target == format_string_atom || selection_request.target == XA_STRING)
844     {
845 forsberg 1037 /* We're expecting a CF_TEXT response */
846 astrand 1038 uint8 *firstnull;
847 astrand 551
848 astrand 1038 /* translate linebreaks */
849     crlf2lf(data, &length);
850 forsberg 1037
851 astrand 1038 /* Only send data up to null byte, if any */
852     firstnull = (uint8 *) strchr((char *) data, '\0');
853     if (firstnull)
854     {
855     length = firstnull - data + 1;
856     }
857     }
858 forsberg 1037 #ifdef USE_UNICODE_CLIPBOARD
859     else if (selection_request.target == format_utf8_string_atom)
860     {
861     /* We're expecting a CF_UNICODETEXT response */
862     iconv_t cd = iconv_open("UTF-8", WINDOWS_CODEPAGE);
863 astrand 1038 if (cd != (iconv_t) - 1)
864 astrand 551 {
865 forsberg 1037 size_t utf8_length = length * 2;
866 astrand 1038 char *utf8_data = malloc(utf8_length);
867 forsberg 1037 size_t utf8_length_remaining = utf8_length;
868 astrand 1038 char *utf8_data_remaining = utf8_data;
869     char *data_remaining = (char *) data;
870     size_t length_remaining = (size_t) length;
871 forsberg 1037 if (utf8_data == NULL)
872     {
873     iconv_close(cd);
874     return;
875     }
876 stargo 1050 iconv(cd, (ICONV_CONST char **) &data_remaining, &length_remaining,
877     &utf8_data_remaining, &utf8_length_remaining);
878 forsberg 1037 iconv_close(cd);
879     free_data = True;
880 astrand 1038 data = (uint8 *) utf8_data;
881 forsberg 1037 length = utf8_length - utf8_length_remaining;
882 astrand 551 }
883     }
884 forsberg 1037 else if (selection_request.target == format_unicode_atom)
885     {
886     /* We're expecting a CF_UNICODETEXT response, so what we're
887     receiving matches our requirements and there's no need
888     for further conversions. */
889     }
890     #endif
891     else if (selection_request.target == rdesktop_clipboard_formats_atom)
892     {
893     /* Pass as-is */
894     }
895     else
896     {
897     xclip_refuse_selection(&selection_request);
898     has_selection_request = False;
899     return;
900     }
901 astrand 551
902 forsberg 1037 xclip_provide_selection(&selection_request, selection_request.target, 8, data, length - 1);
903     has_selection_request = False;
904    
905     if (free_data)
906     free(data);
907 matthewc 432 }
908    
909     void
910 ossman_ 1211 ui_clip_request_failed()
911     {
912     xclip_refuse_selection(&selection_request);
913     has_selection_request = False;
914     }
915    
916     void
917 matthewc 432 ui_clip_request_data(uint32 format)
918     {
919 ossman_ 1206 Window primary_owner, clipboard_owner;
920 matthewc 432
921     DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
922 forsberg 1037 rdp_clipboard_request_format = format;
923 matthewc 432
924 ossman_ 1206 xclip_clear_target_props();
925    
926 matthewc 432 if (rdesktop_is_selection_owner)
927     {
928 jsorg71 450 XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
929 matthewc 432 XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
930    
931 jsorg71 450 XConvertSelection(g_display, primary_atom, rdesktop_clipboard_formats_atom,
932     rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
933 matthewc 432 return;
934     }
935    
936 ossman_ 1207 if (auto_mode)
937     primary_owner = XGetSelectionOwner(g_display, primary_atom);
938     else
939     primary_owner = None;
940    
941 ossman_ 1206 clipboard_owner = XGetSelectionOwner(g_display, clipboard_atom);
942    
943     /* Both available */
944     if ((primary_owner != None) && (clipboard_owner != None))
945 matthewc 432 {
946 ossman_ 1206 primary_timestamp = 0;
947     clipboard_timestamp = 0;
948     XConvertSelection(g_display, primary_atom, timestamp_atom,
949     rdesktop_primary_timestamp_target_atom, g_wnd, CurrentTime);
950     XConvertSelection(g_display, clipboard_atom, timestamp_atom,
951     rdesktop_clipboard_timestamp_target_atom, g_wnd, CurrentTime);
952     return;
953     }
954    
955     /* Just PRIMARY */
956     if (primary_owner != None)
957     {
958 jsorg71 450 XConvertSelection(g_display, primary_atom, targets_atom,
959     rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
960 matthewc 432 return;
961     }
962    
963 ossman_ 1206 /* Just CLIPBOARD */
964     if (clipboard_owner != None)
965 matthewc 432 {
966 jsorg71 450 XConvertSelection(g_display, clipboard_atom, targets_atom,
967     rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
968 matthewc 432 return;
969     }
970    
971     /* No data available */
972 ossman_ 1203 helper_cliprdr_send_empty_response();
973 matthewc 432 }
974    
975     void
976     ui_clip_sync(void)
977     {
978 ossman_ 1212 xclip_get_root_property();
979 matthewc 432 }
980    
981 ossman_ 1207 void
982     ui_clip_set_mode(const char *optarg)
983     {
984     g_rdpclip = True;
985 matthewc 432
986 ossman_ 1207 if (str_startswith(optarg, "auto") || str_startswith(optarg, "on")
987     || str_startswith(optarg, "PRIMARYCLIPBOARD"))
988     auto_mode = True;
989     else if (str_startswith(optarg, "CLIPBOARD"))
990     auto_mode = False;
991     else
992     {
993     warning("Invalid clipboard mode '%s'.\n", optarg);
994     g_rdpclip = False;
995     }
996     }
997    
998 matthewc 432 void
999     xclip_init(void)
1000     {
1001 ossman_ 1207 if (!g_rdpclip)
1002     return;
1003    
1004 matthewc 432 if (!cliprdr_init())
1005     return;
1006    
1007 jsorg71 450 primary_atom = XInternAtom(g_display, "PRIMARY", False);
1008     clipboard_atom = XInternAtom(g_display, "CLIPBOARD", False);
1009     targets_atom = XInternAtom(g_display, "TARGETS", False);
1010     timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);
1011 astrand 456 rdesktop_clipboard_target_atom =
1012     XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
1013 ossman_ 1206 rdesktop_primary_timestamp_target_atom =
1014     XInternAtom(g_display, "_RDESKTOP_PRIMARY_TIMESTAMP_TARGET", False);
1015     rdesktop_clipboard_timestamp_target_atom =
1016     XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TIMESTAMP_TARGET", False);
1017 jsorg71 450 incr_atom = XInternAtom(g_display, "INCR", False);
1018 forsberg 1037 format_string_atom = XInternAtom(g_display, "STRING", False);
1019     format_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False);
1020     format_unicode_atom = XInternAtom(g_display, "text/unicode", False);
1021 ossman_ 1208
1022     /* rdesktop sets _RDESKTOP_CLIPBOARD_FORMATS on the root window when acquiring the clipboard.
1023     Other interested rdesktops can use this to notify their server of the available formats. */
1024     rdesktop_clipboard_formats_atom =
1025     XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
1026     XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);
1027    
1028 forsberg 1037 num_targets = 0;
1029     targets[num_targets++] = targets_atom;
1030     targets[num_targets++] = timestamp_atom;
1031     targets[num_targets++] = rdesktop_clipboard_formats_atom;
1032 astrand 1038 #ifdef USE_UNICODE_CLIPBOARD
1033 forsberg 1037 targets[num_targets++] = format_utf8_string_atom;
1034 astrand 1038 #endif
1035 forsberg 1037 targets[num_targets++] = format_unicode_atom;
1036 ossman_ 1208 targets[num_targets++] = format_string_atom;
1037 forsberg 1037 targets[num_targets++] = XA_STRING;
1038 matthewc 432 }

  ViewVC Help
Powered by ViewVC 1.1.26