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

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

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

  ViewVC Help
Powered by ViewVC 1.1.26