/[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 1048 - (hide annotations)
Tue Feb 28 08:09:41 2006 UTC (18 years, 2 months ago) by astrand
File MIME type: text/plain
File size: 27298 byte(s)
Applied patch from bug #1431217: Segmentation fault in xclip_handle_SelectionNotify.

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 astrand 1048 uint8 *data = NULL;
389 matthewc 432
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 astrand 1048 if (data)
507     XFree(data);
508 forsberg 1037 helper_cliprdr_send_empty_response();
509 matthewc 432 }
510    
511 forsberg 1027 /* This function is called for SelectionRequest events.
512 forsberg 1037 The SelectionRequest event is sent from the requestor to the clipboard owner
513 forsberg 1027 to request clipboard data.
514     */
515 astrand 462 void
516 astrand 435 xclip_handle_SelectionRequest(XSelectionRequestEvent * event)
517 matthewc 432 {
518     unsigned long nitems, bytes_left;
519 astrand 465 unsigned char *prop_return;
520 matthewc 432 uint32 *wanted_format;
521     int format, res;
522     Atom type;
523    
524     DEBUG_CLIPBOARD(("xclip_handle_SelectionRequest: selection=%s, target=%s, property=%s\n",
525 jsorg71 450 XGetAtomName(g_display, event->selection),
526     XGetAtomName(g_display, event->target),
527     XGetAtomName(g_display, event->property)));
528 matthewc 432
529     if (event->target == targets_atom)
530     {
531 forsberg 1037 xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, num_targets);
532 matthewc 432 return;
533     }
534     else if (event->target == timestamp_atom)
535     {
536 jsorg71 450 xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);
537 matthewc 432 return;
538     }
539     else
540     {
541 forsberg 1037 /* All the following targets require an async operation with the RDP server
542     and currently we don't do X clipboard request queueing so we can only
543     handle one such request at a time. */
544     if (has_selection_request)
545     {
546     DEBUG_CLIPBOARD(("Error: Another clipboard request was already sent to the RDP server and not yet responded. Refusing this request.\n"));
547     xclip_refuse_selection(event);
548     return;
549     }
550     if (event->target == rdesktop_clipboard_formats_atom)
551     {
552     /* Before the requestor makes a request for the _RDESKTOP_CLIPBOARD_FORMATS target,
553     he should declare requestor[_RDESKTOP_CLIPBOARD_TARGET] = CF_SOMETHING.
554     Otherwise, we default to RDP_CF_TEXT.
555     */
556     res = XGetWindowProperty(g_display, event->requestor,
557 astrand 1038 rdesktop_clipboard_target_atom, 0, 1, True,
558     XA_INTEGER, &type, &format, &nitems, &bytes_left,
559     &prop_return);
560 forsberg 1037 wanted_format = (uint32 *) prop_return;
561     format = (res == Success) ? *wanted_format : RDP_CF_TEXT;
562     XFree(prop_return);
563     }
564 astrand 1038 else if (event->target == format_string_atom || event->target == XA_STRING)
565 forsberg 1037 {
566     /* STRING and XA_STRING are defined to be ISO8859-1 */
567     format = CF_TEXT;
568     }
569     else if (event->target == format_utf8_string_atom)
570     {
571 astrand 1038 #ifdef USE_UNICODE_CLIPBOARD
572 forsberg 1037 format = CF_UNICODETEXT;
573 astrand 1038 #else
574 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"));
575     xclip_refuse_selection(event);
576     return;
577 astrand 1038 #endif
578 forsberg 1037 }
579     else if (event->target == format_unicode_atom)
580     {
581     /* Assuming text/unicode to be UTF-16 */
582     format = CF_UNICODETEXT;
583     }
584     else
585     {
586     DEBUG_CLIPBOARD(("Requested target unavailable. (It was not in TARGETS, so why did you ask for it?!)\n"));
587     xclip_refuse_selection(event);
588     return;
589     }
590    
591     cliprdr_send_data_request(format);
592     selection_request = *event;
593     has_selection_request = True;
594 astrand 1038 return; /* wait for data */
595 matthewc 432 }
596     }
597    
598 forsberg 1037 /* While this rdesktop holds ownership over the clipboard, it means the clipboard data
599     is offered by the RDP server (and when it is pasted inside RDP, there's no network
600     roundtrip).
601    
602     This event (SelectionClear) symbolizes this rdesktop lost onwership of the clipboard
603 forsberg 1027 to some other X client. We should find out what clipboard formats this other
604     client offers and announce that to RDP. */
605 astrand 462 void
606 matthewc 432 xclip_handle_SelectionClear(void)
607     {
608     DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
609     have_primary = 0;
610 jsorg71 450 XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);
611 forsberg 1037 /* FIXME:
612     Without XFIXES, we cannot reliably know the formats offered by the
613     new owner of the X11 clipboard, so we just lie about him
614     offering RDP_CF_TEXT. */
615     cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
616 matthewc 432 }
617    
618 forsberg 1027 /* Called when any property changes in our window or the root window. */
619 astrand 462 void
620 astrand 435 xclip_handle_PropertyNotify(XPropertyEvent * event)
621 matthewc 432 {
622 astrand 1028 unsigned long nitems;
623 forsberg 1026 unsigned long offset = 0;
624     unsigned long bytes_left = 1;
625 matthewc 432 int format, res;
626 astrand 913 XWindowAttributes wa;
627 matthewc 432 uint8 *data;
628     Atom type;
629    
630 astrand 913 if (event->state == PropertyNewValue && g_waiting_for_INCR)
631     {
632     DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: g_waiting_for_INCR != 0\n"));
633    
634 astrand 1028 while (bytes_left > 0)
635     {
636 forsberg 1037 /* Unlike the specification, we don't set the 'delete' arugment to True
637     since we slurp the INCR's chunks in even-smaller chunks of 4096 bytes. */
638 astrand 1028 if ((XGetWindowProperty
639     (g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,
640     False, AnyPropertyType, &type, &format, &nitems, &bytes_left,
641     &data) != Success))
642 forsberg 1026 {
643     XFree(data);
644     return;
645     }
646 astrand 913
647 forsberg 1026 if (nitems == 0)
648 astrand 913 {
649 forsberg 1037 /* INCR transfer finished */
650 forsberg 1026 XGetWindowAttributes(g_display, g_wnd, &wa);
651 astrand 1028 XSelectInput(g_display, g_wnd,
652     (wa.your_event_mask ^ PropertyChangeMask));
653 forsberg 1026 XFree(data);
654     g_waiting_for_INCR = 0;
655 astrand 913
656 forsberg 1026 if (g_clip_buflen > 0)
657     {
658 astrand 1038 if (!xclip_send_data_with_convert
659     (g_clip_buffer, g_clip_buflen, g_incr_target))
660 forsberg 1037 {
661     helper_cliprdr_send_empty_response();
662     }
663 forsberg 1026 xfree(g_clip_buffer);
664 forsberg 1037 g_clip_buffer = NULL;
665 forsberg 1026 g_clip_buflen = 0;
666     }
667 astrand 913 }
668 forsberg 1026 else
669     {
670 forsberg 1037 /* Another chunk in the INCR transfer */
671 astrand 1038 offset += (nitems / 4); /* offset at which to begin the next slurp */
672 forsberg 1037 g_clip_buffer = xrealloc(g_clip_buffer, g_clip_buflen + nitems);
673     memcpy(g_clip_buffer + g_clip_buflen, data, nitems);
674     g_clip_buflen += nitems;
675 astrand 913
676 forsberg 1026 XFree(data);
677     }
678 astrand 913 }
679 forsberg 1026 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
680 forsberg 1037 return;
681 astrand 913 }
682    
683 forsberg 1037 if ((event->atom == rdesktop_clipboard_formats_atom) &&
684     (event->window == DefaultRootWindow(g_display)) &&
685 astrand 1038 !have_primary /* not interested in our own events */ )
686 forsberg 1037 {
687     if (event->state == PropertyNewValue)
688     {
689     DEBUG_CLIPBOARD(("xclip_handle_PropertyNotify: getting fellow rdesktop formats\n"));
690 matthewc 432
691 forsberg 1037 res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),
692     rdesktop_clipboard_formats_atom, 0,
693 astrand 1038 XMaxRequestSize(g_display), False, XA_STRING,
694     &type, &format, &nitems, &bytes_left, &data);
695 matthewc 432
696 forsberg 1037 if ((res == Success) && (nitems > 0))
697     {
698     cliprdr_send_native_format_announce(data, nitems);
699     rdesktop_is_selection_owner = 1;
700     return;
701     }
702     }
703 matthewc 432
704 forsberg 1037 /* For some reason, we couldn't announce the native formats */
705     cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
706     rdesktop_is_selection_owner = 0;
707 matthewc 432 }
708     }
709 astrand 942 #endif
710 matthewc 432
711    
712 forsberg 1027 /* Called when the RDP server announces new clipboard data formats.
713     In response, we:
714     - take ownership over the clipboard
715     - declare those formats in their Windows native form
716     to other rdesktop instances on this X server */
717 matthewc 432 void
718 astrand 542 ui_clip_format_announce(uint8 * data, uint32 length)
719 matthewc 432 {
720 jsorg71 450 XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime);
721     if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
722 matthewc 432 {
723     warning("Failed to aquire ownership of PRIMARY clipboard\n");
724     return;
725     }
726    
727     have_primary = 1;
728 jsorg71 450 XChangeProperty(g_display, DefaultRootWindow(g_display),
729 astrand 435 rdesktop_clipboard_formats_atom, XA_STRING, 8, PropModeReplace, data,
730     length);
731 matthewc 432
732 jsorg71 450 XSetSelectionOwner(g_display, clipboard_atom, g_wnd, g_last_gesturetime);
733     if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)
734 matthewc 432 warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
735     }
736    
737 forsberg 1037 /* Called when the RDP server responds with clipboard data (after we've requested it). */
738 matthewc 432 void
739 astrand 542 ui_clip_handle_data(uint8 * data, uint32 length)
740 matthewc 432 {
741 forsberg 1037 BOOL free_data = False;
742 astrand 551
743 astrand 1038 if (selection_request.target == format_string_atom || selection_request.target == XA_STRING)
744     {
745 forsberg 1037 /* We're expecting a CF_TEXT response */
746 astrand 1038 uint8 *firstnull;
747 astrand 551
748 astrand 1038 /* translate linebreaks */
749     crlf2lf(data, &length);
750 forsberg 1037
751 astrand 1038 /* Only send data up to null byte, if any */
752     firstnull = (uint8 *) strchr((char *) data, '\0');
753     if (firstnull)
754     {
755     length = firstnull - data + 1;
756     }
757     }
758 forsberg 1037 #ifdef USE_UNICODE_CLIPBOARD
759     else if (selection_request.target == format_utf8_string_atom)
760     {
761     /* We're expecting a CF_UNICODETEXT response */
762     iconv_t cd = iconv_open("UTF-8", WINDOWS_CODEPAGE);
763 astrand 1038 if (cd != (iconv_t) - 1)
764 astrand 551 {
765 forsberg 1037 size_t utf8_length = length * 2;
766 astrand 1038 char *utf8_data = malloc(utf8_length);
767 forsberg 1037 size_t utf8_length_remaining = utf8_length;
768 astrand 1038 char *utf8_data_remaining = utf8_data;
769     char *data_remaining = (char *) data;
770     size_t length_remaining = (size_t) length;
771 forsberg 1037 if (utf8_data == NULL)
772     {
773     iconv_close(cd);
774     return;
775     }
776 astrand 1038 iconv(cd, &data_remaining, &length_remaining, &utf8_data_remaining,
777     &utf8_length_remaining);
778 forsberg 1037 iconv_close(cd);
779     free_data = True;
780 astrand 1038 data = (uint8 *) utf8_data;
781 forsberg 1037 length = utf8_length - utf8_length_remaining;
782 astrand 551 }
783     }
784 forsberg 1037 else if (selection_request.target == format_unicode_atom)
785     {
786     /* We're expecting a CF_UNICODETEXT response, so what we're
787     receiving matches our requirements and there's no need
788     for further conversions. */
789     }
790     #endif
791     else if (selection_request.target == rdesktop_clipboard_formats_atom)
792     {
793     /* Pass as-is */
794     }
795     else
796     {
797     xclip_refuse_selection(&selection_request);
798     has_selection_request = False;
799     return;
800     }
801 astrand 551
802 forsberg 1037 xclip_provide_selection(&selection_request, selection_request.target, 8, data, length - 1);
803     has_selection_request = False;
804    
805     if (free_data)
806     free(data);
807 matthewc 432 }
808    
809     void
810     ui_clip_request_data(uint32 format)
811     {
812     Window selectionowner;
813    
814     DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
815 forsberg 1037 rdp_clipboard_request_format = format;
816 matthewc 432
817     if (rdesktop_is_selection_owner)
818     {
819 jsorg71 450 XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
820 matthewc 432 XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
821    
822 jsorg71 450 XConvertSelection(g_display, primary_atom, rdesktop_clipboard_formats_atom,
823     rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
824 matthewc 432 return;
825     }
826    
827 jsorg71 450 selectionowner = XGetSelectionOwner(g_display, primary_atom);
828 matthewc 432 if (selectionowner != None)
829     {
830 jsorg71 450 XConvertSelection(g_display, primary_atom, targets_atom,
831     rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
832 matthewc 432 return;
833     }
834    
835     /* No PRIMARY, try CLIPBOARD */
836 jsorg71 450 selectionowner = XGetSelectionOwner(g_display, clipboard_atom);
837 matthewc 432 if (selectionowner != None)
838     {
839 jsorg71 450 XConvertSelection(g_display, clipboard_atom, targets_atom,
840     rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
841 matthewc 432 return;
842     }
843    
844     /* No data available */
845     cliprdr_send_data(NULL, 0);
846     }
847    
848     void
849     ui_clip_sync(void)
850     {
851 forsberg 1037 cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
852 matthewc 432 }
853    
854    
855     void
856     xclip_init(void)
857     {
858     if (!cliprdr_init())
859     return;
860    
861 jsorg71 450 primary_atom = XInternAtom(g_display, "PRIMARY", False);
862     clipboard_atom = XInternAtom(g_display, "CLIPBOARD", False);
863     targets_atom = XInternAtom(g_display, "TARGETS", False);
864     timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);
865 astrand 456 rdesktop_clipboard_target_atom =
866     XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
867 jsorg71 450 incr_atom = XInternAtom(g_display, "INCR", False);
868 forsberg 1037 format_string_atom = XInternAtom(g_display, "STRING", False);
869     format_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False);
870     format_unicode_atom = XInternAtom(g_display, "text/unicode", False);
871     num_targets = 0;
872     targets[num_targets++] = targets_atom;
873     targets[num_targets++] = timestamp_atom;
874     targets[num_targets++] = rdesktop_clipboard_formats_atom;
875     targets[num_targets++] = format_string_atom;
876 astrand 1038 #ifdef USE_UNICODE_CLIPBOARD
877 forsberg 1037 targets[num_targets++] = format_utf8_string_atom;
878 astrand 1038 #endif
879 forsberg 1037 targets[num_targets++] = format_unicode_atom;
880     targets[num_targets++] = XA_STRING;
881 matthewc 432
882     /* rdesktop sets _RDESKTOP_CLIPBOARD_FORMATS on the root window when acquiring the clipboard.
883     Other interested rdesktops can use this to notify their server of the available formats. */
884 astrand 435 rdesktop_clipboard_formats_atom =
885 jsorg71 450 XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
886     XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);
887 matthewc 432 }

  ViewVC Help
Powered by ViewVC 1.1.26