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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 551 by astrand, Fri Dec 5 10:36:04 2003 UTC revision 1037 by forsberg, Mon Jan 2 15:55:59 2006 UTC
# Line 23  Line 23 
23  #include <X11/Xatom.h>  #include <X11/Xatom.h>
24  #include "rdesktop.h"  #include "rdesktop.h"
25    
26  #define NUM_TARGETS 6  /*
27      To gain better understanding of this code, one could be assisted by the following documents:
28      - Inter-Client Communication Conventions Manual (ICCCM)
29        HTML: http://tronche.com/gui/x/icccm/
30        PDF:  http://ftp.xfree86.org/pub/XFree86/4.5.0/doc/PDF/icccm.pdf
31      - MSDN: Clipboard Formats
32        http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/dataexchange/clipboard/clipboardformats.asp
33    */
34    
35    #ifdef HAVE_ICONV
36    #ifdef HAVE_LANGINFO_H
37    #ifdef HAVE_ICONV_H
38    #include <langinfo.h>
39    #include <iconv.h>
40    #define USE_UNICODE_CLIPBOARD
41    #endif
42    #endif
43    #endif
44    
45    #ifdef USE_UNICODE_CLIPBOARD
46    #define RDP_CF_TEXT CF_UNICODETEXT
47    #else
48    #define RDP_CF_TEXT CF_TEXT
49    #endif
50    
51    #define MAX_TARGETS 7
52    
53  extern Display *g_display;  extern Display *g_display;
54  extern Window g_wnd;  extern Window g_wnd;
55  extern Time g_last_gesturetime;  extern Time g_last_gesturetime;
56    
57  static Atom clipboard_atom, primary_atom, targets_atom, timestamp_atom;  /* Atoms of the two X selections we're dealing with: CLIPBOARD (explicit-copy) and PRIMARY (selection-copy) */
58  static Atom rdesktop_clipboard_target_atom, rdesktop_clipboard_formats_atom, incr_atom;  static Atom clipboard_atom, primary_atom;
59    /* Atom of the TARGETS clipboard target */
60    static Atom targets_atom;
61    /* Atom of the TIMESTAMP clipboard target */
62    static Atom timestamp_atom;
63    /* Atom _RDESKTOP_CLIPBOARD_TARGET which has multiple uses:
64       - The 'property' argument in XConvertSelection calls: This is the property of our
65         window into which XConvertSelection will store the received clipboard data.
66       - In a clipboard request of target _RDESKTOP_CLIPBOARD_FORMATS, an XA_INTEGER-typed
67         property carrying the Windows native (CF_...) format desired by the requestor.
68         Requestor set this property (e.g. requestor_wnd[_RDESKTOP_CLIPBOARD_TARGET] = CF_TEXT)
69         before requesting clipboard data from a fellow rdesktop using
70         the _RDESKTOP_CLIPBOARD_FORMATS target. */
71    static Atom rdesktop_clipboard_target_atom;
72    /* Atom _RDESKTOP_CLIPBOARD_FORMATS which has multiple uses:
73       - The clipboard target (X jargon for "clipboard format") for rdesktop-to-rdesktop interchange
74         of Windows native clipboard data.
75         This target cannot be used standalone; the requestor must keep the
76         _RDESKTOP_CLIPBOARD_TARGET property on his window denoting
77         the Windows native clipboard format being requested.
78       - The root window property set by rdesktop when it owns the clipboard,
79         denoting all Windows native clipboard formats it offers via
80         requests of the _RDESKTOP_CLIPBOARD_FORMATS target. */
81    static Atom rdesktop_clipboard_formats_atom;
82    static Atom format_string_atom, format_utf8_string_atom, format_unicode_atom;
83    /* Atom of the INCR clipboard type (see ICCCM on "INCR Properties") */
84    static Atom incr_atom;
85    /* Stores the last "selection request" (= another X client requesting clipboard data from us).
86       To satisfy such a request, we request the clipboard data from the RDP server.
87       When we receive the response from the RDP server (asynchronously), this variable gives us
88       the context to proceed. */
89  static XSelectionRequestEvent selection_request;  static XSelectionRequestEvent selection_request;
90  static Atom targets[NUM_TARGETS];  /* Denotes we have a pending selection request. */
91    static Bool has_selection_request;
92    /* Stores the clipboard format (CF_TEXT, CF_UNICODETEXT etc.) requested in the last
93       CLIPDR_DATA_REQUEST (= the RDP server requesting clipboard data from us).
94       When we receive this data from whatever X client offering it, this variable gives us
95       the context to proceed.
96     */
97    static int rdp_clipboard_request_format;
98    /* Array of offered clipboard targets that will be sent to fellow X clients upon a TARGETS request. */
99    static Atom targets[MAX_TARGETS];
100    static int num_targets;
101    /* Denotes that this client currently holds the PRIMARY selection. */
102  static int have_primary = 0;  static int have_primary = 0;
103    /* Denotes that an rdesktop (not this rdesktop) is owning the selection,
104       allowing us to interchange Windows native clipboard data directly. */
105  static int rdesktop_is_selection_owner = 0;  static int rdesktop_is_selection_owner = 0;
106    
107    /* Denotes that an INCR ("chunked") transfer is in progress. */
108  /* Replace CR-LF to LF (well, rather removing all CR:s) This is done  static int g_waiting_for_INCR = 0;
109     in-place. The length is updated. Handles embedded nulls */  /* Denotes the target format of the ongoing INCR ("chunked") transfer. */
110    static Atom g_incr_target = 0;
111    /* Buffers an INCR transfer. */
112    static uint8 *g_clip_buffer = 0;
113    /* Denotes the size of g_clip_buffer. */
114    static uint32 g_clip_buflen = 0;
115    
116    /* Translate LF to CR-LF. To do this, we must allocate more memory.
117       The returned string is null-terminated, as required by CF_TEXT.
118       Does not stop on embedded nulls.
119       The length is updated. */
120  static void  static void
121  crlf2lf(uint8 * data, uint32 * length)  crlf2lf(uint8 * data, uint32 * length)
122  {  {
# Line 53  crlf2lf(uint8 * data, uint32 * length) Line 131  crlf2lf(uint8 * data, uint32 * length)
131          *length = dst - data;          *length = dst - data;
132  }  }
133    
134  /* Translate LF to CR-LF. To do this, we must allocate more memory.    #ifdef USE_UNICODE_CLIPBOARD
135    /* Translate LF to CR-LF. To do this, we must allocate more memory.
136       The returned string is null-terminated, as required by CF_UNICODETEXT.
137       The size is updated. */
138    static uint8 *
139    utf16_lf2crlf(uint8 * data, uint32 * size)
140    {
141            uint8 *result;
142            uint16 *inptr, *outptr;
143    
144            /* Worst case: Every char is LF */
145            result = xmalloc((*size * 2) + 2);
146            if (result == NULL)
147                    return NULL;
148    
149            inptr = (uint16*)data;
150            outptr = (uint16*)result;
151    
152            /* Check for a reversed BOM */
153            Bool swap_endianess = (*inptr == 0xfffe);
154    
155            while ((uint8*)inptr < data + *size)
156            {
157                    uint16 uvalue = *inptr;
158                    if (swap_endianess)
159                            uvalue = ((uvalue << 8) & 0xff00) + (uvalue >> 8);
160                    if (uvalue == 0x0a)
161                            *outptr++ = swap_endianess ? 0x0d00 : 0x0d;
162                    *outptr++ = *inptr++;
163            }
164            *outptr++ = 0; /* null termination */
165            *size = (uint8*)outptr - result;
166    
167            return result;
168    }
169    #else
170    /* Translate LF to CR-LF. To do this, we must allocate more memory.
171     The length is updated. */     The length is updated. */
172  static uint8 *  static uint8 *
173  lf2crlf(uint8 * data, uint32 * length)  lf2crlf(uint8 * data, uint32 * length)
# Line 79  lf2crlf(uint8 * data, uint32 * length) Line 193  lf2crlf(uint8 * data, uint32 * length)
193    
194          return result;          return result;
195  }  }
196    #endif
197    
198  static void  static void
199  xclip_provide_selection(XSelectionRequestEvent * req, Atom type, unsigned int format, uint8 * data,  xclip_provide_selection(XSelectionRequestEvent * req, Atom type, unsigned int format, uint8 * data,
# Line 101  xclip_provide_selection(XSelectionReques Line 215  xclip_provide_selection(XSelectionReques
215          XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);          XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);
216  }  }
217    
218    /* Replies a clipboard requestor, telling that we're unable to satisfy his request for whatever reason.
219       This has the benefit of finalizing the clipboard negotiation and thus not leaving our requestor
220       lingering (and, potentially, stuck). */
221    static void
222    xclip_refuse_selection(XSelectionRequestEvent * req)
223    {
224            XEvent xev;
225    
226            xev.xselection.type = SelectionNotify;
227            xev.xselection.serial = 0;
228            xev.xselection.send_event = True;
229            xev.xselection.requestor = req->requestor;
230            xev.xselection.selection = req->selection;
231            xev.xselection.target = req->target;
232            xev.xselection.property = None;
233            xev.xselection.time = req->time;
234            XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);
235    }
236    
237    /* Wrapper for cliprdr_send_data which also cleans the request state. */
238    static void
239    helper_cliprdr_send_response(uint8 * data, uint32 length)
240    {
241            if (rdp_clipboard_request_format != 0)
242            {
243                    cliprdr_send_data(data, length);
244                    rdp_clipboard_request_format = 0;
245                    if (!rdesktop_is_selection_owner)
246                            cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
247            }
248    }
249    
250    /* Last resort, when we have to provide clipboard data but for whatever
251       reason couldn't get any.
252     */
253    static void
254    helper_cliprdr_send_empty_response()
255    {
256            helper_cliprdr_send_response(NULL, 0);
257    }
258    
259    /* Replies with clipboard data to RDP, converting it from the target format
260       to the expected RDP format as necessary. Returns true if data was sent.
261     */
262    static Bool
263    xclip_send_data_with_convert(uint8* source, size_t source_size, Atom target)
264    {
265            #ifdef USE_UNICODE_CLIPBOARD
266            if (target == format_string_atom ||
267                target == format_unicode_atom ||
268                target == format_utf8_string_atom)
269            {
270                    if (rdp_clipboard_request_format != RDP_CF_TEXT)
271                            return False;
272    
273                    /* Make an attempt to convert any string we send to Unicode.
274                       We don't know what the RDP server's ANSI Codepage is, or how to convert
275                       to it, so using CF_TEXT is not safe (and is unnecessary, since all
276                       WinNT versions are Unicode-minded).
277                     */
278                    size_t unicode_buffer_size;
279                    char* unicode_buffer;
280                    iconv_t cd;
281    
282                    if (target == format_string_atom)
283                    {
284                            char* locale_charset = nl_langinfo(CODESET);
285                            cd = iconv_open(WINDOWS_CODEPAGE, locale_charset);
286                            if (cd == (iconv_t)-1)
287                            {
288                                    DEBUG_CLIPBOARD(("Locale charset %s not found in iconv. Unable to convert clipboard text.\n", locale_charset));
289                                    return False;
290                            }
291                            unicode_buffer_size = source_size * 4;
292                    }
293                    else if (target == format_unicode_atom)
294                    {
295                            cd = iconv_open(WINDOWS_CODEPAGE, "UCS-2");
296                            if (cd == (iconv_t)-1)
297                            {
298                                    return False;
299                            }
300                            unicode_buffer_size = source_size;
301                    }
302                    else if (target == format_utf8_string_atom)
303                    {
304                            cd = iconv_open(WINDOWS_CODEPAGE, "UTF-8");
305                            if (cd == (iconv_t)-1)
306                            {
307                                    return False;
308                            }
309                            /* UTF-8 is guaranteed to be less or equally compact
310                               as UTF-16 for all Unicode chars >=2 bytes.
311                             */
312                            unicode_buffer_size = source_size * 2;
313                    }
314                    else
315                    {
316                            return False;
317                    }
318    
319                    unicode_buffer = xmalloc(unicode_buffer_size);
320                    size_t unicode_buffer_size_remaining = unicode_buffer_size;
321                    char* unicode_buffer_remaining = unicode_buffer;
322                    char* data_remaining = (char*)source;
323                    size_t data_size_remaining = source_size;
324                    iconv(cd, &data_remaining, &data_size_remaining, &unicode_buffer_remaining, &unicode_buffer_size_remaining);
325                    iconv_close(cd);
326    
327                    /* translate linebreaks */
328                    uint32 translated_data_size = unicode_buffer_size - unicode_buffer_size_remaining;
329                    uint8* translated_data = utf16_lf2crlf((uint8*)unicode_buffer, &translated_data_size);
330                    if (translated_data != NULL)
331                    {
332                            DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n", translated_data_size));
333                            cliprdr_send_data(translated_data, translated_data_size);
334                            xfree(translated_data); /* Not the same thing as XFree! */
335                    }
336    
337                    xfree(unicode_buffer);
338    
339                    return True;
340            }
341            #else
342            if (target == format_string_atom)
343            {
344                    uint8 *translated_data;
345                    uint32 length = source_size;
346    
347                    if (rdp_clipboard_request_format != RDP_CF_TEXT)
348                            return False;
349    
350                    DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
351                    translated_data = lf2crlf(source, &length);
352                    if (translated_data != NULL)
353                    {
354                            cliprdr_send_data(translated_data, length);
355                            xfree(translated_data); /* Not the same thing as XFree! */
356                    }
357    
358                    return True;
359            }
360            #endif
361            else if (target == rdesktop_clipboard_formats_atom)
362            {
363                    helper_cliprdr_send_response(source, source_size + 1);
364    
365                    return True;
366            }
367            else
368            {
369                    return False;
370            }
371    }
372    
373    /* This function is called for SelectionNotify events.
374       The SelectionNotify event is sent from the clipboard owner to the requestor
375       after his request was satisfied.
376       If this function is called, we're the requestor side. */
377    #ifndef MAKE_PROTO
378  void  void
379  xclip_handle_SelectionNotify(XSelectionEvent * event)  xclip_handle_SelectionNotify(XSelectionEvent * event)
380  {  {
381          unsigned long nitems, bytes_left;          unsigned long nitems, bytes_left;
382          Atom type, best_target, text_target;          XWindowAttributes wa;
383            Atom type;
384          Atom *supported_targets;          Atom *supported_targets;
385          int res, i, format;          int res, i, format;
386          uint8 *data;          uint8 *data;
# Line 122  xclip_handle_SelectionNotify(XSelectionE Line 397  xclip_handle_SelectionNotify(XSelectionE
397                  goto fail;                  goto fail;
398    
399          res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,          res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
400                                   0, XMaxRequestSize(g_display), True, AnyPropertyType,                                   0, XMaxRequestSize(g_display), False, AnyPropertyType,
401                                   &type, &format, &nitems, &bytes_left, &data);                                   &type, &format, &nitems, &bytes_left, &data);
402    
403          if (res != Success)          if (res != Success)
# Line 131  xclip_handle_SelectionNotify(XSelectionE Line 406  xclip_handle_SelectionNotify(XSelectionE
406                  goto fail;                  goto fail;
407          }          }
408    
409            if (type == incr_atom)
410            {
411                    DEBUG_CLIPBOARD(("Received INCR.\n"));
412    
413                    XGetWindowAttributes(g_display, g_wnd, &wa);
414                    if ((wa.your_event_mask | PropertyChangeMask) != wa.your_event_mask)
415                    {
416                            XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));
417                    }
418                    XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
419                    XFree(data);
420                    g_incr_target = event->target;
421                    g_waiting_for_INCR = 1;
422                    return;
423            }
424    
425            XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
426    
427          /* Negotiate target format */          /* Negotiate target format */
428          if (event->target == targets_atom)          if (event->target == targets_atom)
429          {          {
430                  /* FIXME: We should choose format here based on what the server wanted */                  /* Determine the best of text targets that we have available:
431                  best_target = XA_STRING;                     Prefer UTF8_STRING > text/unicode (unspecified encoding) > STRING
432                       (ignore TEXT and COMPOUND_TEXT because we don't have code to handle them)
433                     */
434                    int text_target_satisfaction = 0;
435                    Atom best_text_target = 0; /* measures how much we're satisfied with what we found */
436                  if (type != None)                  if (type != None)
437                  {                  {
438                          supported_targets = (Atom *) data;                          supported_targets = (Atom *) data;
                         text_target = XInternAtom(g_display, "TEXT", False);  
439                          for (i = 0; i < nitems; i++)                          for (i = 0; i < nitems; i++)
440                          {                          {
441                                  DEBUG_CLIPBOARD(("Target %d: %s\n", i,                                  DEBUG_CLIPBOARD(("Target %d: %s\n", i,
442                                                   XGetAtomName(g_display, supported_targets[i])));                                                   XGetAtomName(g_display, supported_targets[i])));
443                                  if (supported_targets[i] == text_target)                                  if (supported_targets[i] == format_string_atom)
444                                  {                                  {
445                                          DEBUG_CLIPBOARD(("Other party supports TEXT, choosing that as best_target\n"));                                          if (text_target_satisfaction < 1)
446                                          best_target = text_target;                                          {
447                                          break;                                                  DEBUG_CLIPBOARD(("Other party supports STRING, choosing that as best_target\n"));
448                                                    best_text_target = supported_targets[i];
449                                                    text_target_satisfaction = 1;
450                                            }
451                                  }                                  }
452                                    #ifdef USE_UNICODE_CLIPBOARD
453                                    else if (supported_targets[i] == format_unicode_atom)
454                                    {
455                                            if (text_target_satisfaction < 2)
456                                            {
457                                                    DEBUG_CLIPBOARD(("Other party supports text/unicode, choosing that as best_target\n"));
458                                                    best_text_target = supported_targets[i];
459                                                    text_target_satisfaction = 2;
460                                            }
461                                    }
462                                    else if (supported_targets[i] == format_utf8_string_atom)
463                                    {
464                                            if (text_target_satisfaction < 3)
465                                            {
466                                                    DEBUG_CLIPBOARD(("Other party supports UTF8_STRING, choosing that as best_target\n"));
467                                                    best_text_target = supported_targets[i];
468                                                    text_target_satisfaction = 3;
469                                            }
470                                    }
471                                    #endif
472                          }                          }
                         XFree(data);  
473                  }                  }
474    
475                  XConvertSelection(g_display, primary_atom, best_target,                  /* Kickstarting the next step in the process of satisfying RDP's
476                                    rdesktop_clipboard_target_atom, g_wnd, event->time);                     clipboard request -- specifically, requesting the actual clipboard data.
477                  return;                   */
478          }                  if (best_text_target != 0)
479                    {
480          if (type == incr_atom)                          XConvertSelection(g_display, clipboard_atom, best_text_target, rdesktop_clipboard_target_atom, g_wnd, event->time);
481          {                          return;
482                  warning("We don't support INCR transfers at this time. Try cutting less data.\n");                  }
483                  goto fail;                  else
484          }                  {
485                            DEBUG_CLIPBOARD(("Unable to find a textual target to satisfy RDP clipboard text request\n"));
486          /* Translate linebreaks, but only if not getting data from                          goto fail;
487             other rdesktop instance */                  }
         if (event->target != rdesktop_clipboard_formats_atom)  
         {  
                 uint8 *translated_data;  
                 uint32 length = nitems;  
   
                 DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));  
                 translated_data = lf2crlf(data, &length);  
                 cliprdr_send_data(translated_data, length + 1);  
                 xfree(translated_data); /* Not the same thing as XFree! */  
488          }          }
489          else          else
490          {          {
491                  cliprdr_send_data(data, nitems + 1);                  if (!xclip_send_data_with_convert(data, nitems, event->target))
492                    {
493                            goto fail;
494                    }
495          }          }
496    
497          XFree(data);          XFree(data);
498    
         if (!rdesktop_is_selection_owner)  
                 cliprdr_send_text_format_announce();  
499          return;          return;
500    
501        fail:        fail:
502          cliprdr_send_data(NULL, 0);          XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
503            XFree(data);
504            helper_cliprdr_send_empty_response();
505  }  }
506    
507    /* This function is called for SelectionRequest events.
508       The SelectionRequest event is sent from the requestor to the clipboard owner
509       to request clipboard data.
510     */
511  void  void
512  xclip_handle_SelectionRequest(XSelectionRequestEvent * event)  xclip_handle_SelectionRequest(XSelectionRequestEvent * event)
513  {  {
# Line 207  xclip_handle_SelectionRequest(XSelection Line 524  xclip_handle_SelectionRequest(XSelection
524    
525          if (event->target == targets_atom)          if (event->target == targets_atom)
526          {          {
527                  xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, NUM_TARGETS);                  xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, num_targets);
528                  return;                  return;
529          }          }
530          else if (event->target == timestamp_atom)          else if (event->target == timestamp_atom)
# Line 215  xclip_handle_SelectionRequest(XSelection Line 532  xclip_handle_SelectionRequest(XSelection
532                  xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);                  xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);
533                  return;                  return;
534          }          }
         else if (event->target == rdesktop_clipboard_formats_atom)  
         {  
                 res = XGetWindowProperty(g_display, event->requestor,  
                                          rdesktop_clipboard_target_atom, 0, 1, True, XA_INTEGER,  
                                          &type, &format, &nitems, &bytes_left, &prop_return);  
                 wanted_format = (uint32 *) prop_return;  
                 format = (res == Success) ? *wanted_format : CF_TEXT;  
                 /* FIXME: Need to free returned data? */  
         }  
535          else          else
536          {          {
537                  format = CF_TEXT;                  /* 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);                  cliprdr_send_data_request(format);
588          selection_request = *event;                  selection_request = *event;
589          /* wait for data */                  has_selection_request = True;
590                    return; /* wait for data */
591            }
592  }  }
593    
594    /* While this rdesktop holds ownership over the clipboard, it means the clipboard data
595       is offered by the RDP server (and when it is pasted inside RDP, there's no network
596       roundtrip).
597    
598       This event (SelectionClear) symbolizes this rdesktop lost onwership of the clipboard
599       to some other X client. We should find out what clipboard formats this other
600       client offers and announce that to RDP. */
601  void  void
602  xclip_handle_SelectionClear(void)  xclip_handle_SelectionClear(void)
603  {  {
604          DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));          DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
605          have_primary = 0;          have_primary = 0;
606          XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);          XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);
607          cliprdr_send_text_format_announce();          /* FIXME:
608               Without XFIXES, we cannot reliably know the formats offered by the
609               new owner of the X11 clipboard, so we just lie about him
610               offering RDP_CF_TEXT. */
611            cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
612  }  }
613    
614    /* Called when any property changes in our window or the root window. */
615  void  void
616  xclip_handle_PropertyNotify(XPropertyEvent * event)  xclip_handle_PropertyNotify(XPropertyEvent * event)
617  {  {
618          unsigned long nitems, bytes_left;          unsigned long nitems;
619            unsigned long offset = 0;
620            unsigned long bytes_left = 1;
621          int format, res;          int format, res;
622            XWindowAttributes wa;
623          uint8 *data;          uint8 *data;
624          Atom type;          Atom type;
625    
626          if (event->atom != rdesktop_clipboard_formats_atom)          if (event->state == PropertyNewValue && g_waiting_for_INCR)
627                  return;          {
628                    DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: g_waiting_for_INCR != 0\n"));
629    
630                    while (bytes_left > 0)
631                    {
632                            /* Unlike the specification, we don't set the 'delete' arugment to True
633                               since we slurp the INCR's chunks in even-smaller chunks of 4096 bytes. */
634                            if ((XGetWindowProperty
635                                 (g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,
636                                  False, AnyPropertyType, &type, &format, &nitems, &bytes_left,
637                                  &data) != Success))
638                            {
639                                    XFree(data);
640                                    return;
641                            }
642    
643                            if (nitems == 0)
644                            {
645                                    /* INCR transfer finished */
646                                    XGetWindowAttributes(g_display, g_wnd, &wa);
647                                    XSelectInput(g_display, g_wnd,
648                                                 (wa.your_event_mask ^ PropertyChangeMask));
649                                    XFree(data);
650                                    g_waiting_for_INCR = 0;
651    
652                                    if (g_clip_buflen > 0)
653                                    {
654                                            if (!xclip_send_data_with_convert(g_clip_buffer, g_clip_buflen, g_incr_target))
655                                            {
656                                                    helper_cliprdr_send_empty_response();
657                                            }
658                                            xfree(g_clip_buffer);
659                                            g_clip_buffer = NULL;
660                                            g_clip_buflen = 0;
661                                    }
662                            }
663                            else
664                            {
665                                    /* Another chunk in the INCR transfer */
666                                    offset += (nitems / 4); /* offset at which to begin the next slurp */
667                                    g_clip_buffer = xrealloc(g_clip_buffer, g_clip_buflen + nitems);
668                                    memcpy(g_clip_buffer + g_clip_buflen, data, nitems);
669                                    g_clip_buflen += nitems;
670    
671          if (have_primary)       /* from us */                                  XFree(data);
672                            }
673                    }
674                    XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
675                  return;                  return;
676            }
677    
678          if (event->state == PropertyNewValue)          if ((event->atom == rdesktop_clipboard_formats_atom) &&
679                (event->window == DefaultRootWindow(g_display)) &&
680                !have_primary /* not interested in our own events */)
681          {          {
682                  res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),                  if (event->state == PropertyNewValue)
                                          rdesktop_clipboard_formats_atom, 0,  
                                          XMaxRequestSize(g_display), False, XA_STRING, &type,  
                                          &format, &nitems, &bytes_left, &data);  
   
                 if ((res == Success) && (nitems > 0))  
683                  {                  {
684                          cliprdr_send_native_format_announce(data, nitems);                          DEBUG_CLIPBOARD(("xclip_handle_PropertyNotify: getting fellow rdesktop formats\n"));
685                          rdesktop_is_selection_owner = 1;  
686                          return;                          res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),
687                                                     rdesktop_clipboard_formats_atom, 0,
688                                                     XMaxRequestSize(g_display), False, XA_STRING, &type,
689                                                     &format, &nitems, &bytes_left, &data);
690    
691                            if ((res == Success) && (nitems > 0))
692                            {
693                                    cliprdr_send_native_format_announce(data, nitems);
694                                    rdesktop_is_selection_owner = 1;
695                                    return;
696                            }
697                  }                  }
         }  
698    
699          /* PropertyDelete, or XGetWindowProperty failed */                  /* For some reason, we couldn't announce the native formats */
700          cliprdr_send_text_format_announce();                  cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
701          rdesktop_is_selection_owner = 0;                  rdesktop_is_selection_owner = 0;
702            }
703  }  }
704    #endif
705    
706    
707    /* Called when the RDP server announces new clipboard data formats.
708       In response, we:
709       - take ownership over the clipboard
710       - declare those formats in their Windows native form
711         to other rdesktop instances on this X server */
712  void  void
713  ui_clip_format_announce(uint8 * data, uint32 length)  ui_clip_format_announce(uint8 * data, uint32 length)
714  {  {
# Line 298  ui_clip_format_announce(uint8 * data, ui Line 729  ui_clip_format_announce(uint8 * data, ui
729                  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");                  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
730  }  }
731    
732    /* Called when the RDP server responds with clipboard data (after we've requested it). */
733  void  void
734  ui_clip_handle_data(uint8 * data, uint32 length)  ui_clip_handle_data(uint8 * data, uint32 length)
735  {  {
736          if (selection_request.target != rdesktop_clipboard_formats_atom)          BOOL free_data = False;
         {  
                 uint8 *firstnull;  
737    
738                  /* translate linebreaks */          if (selection_request.target == format_string_atom ||
739                  crlf2lf(data, &length);              selection_request.target == XA_STRING)
740            {
741                  /* Only send data up to null byte, if any */                  /* We're expecting a CF_TEXT response */
742                  firstnull = strchr(data, '\0');                  uint8 *firstnull;
743                  if (firstnull)  
744                    /* translate linebreaks */
745                    crlf2lf(data, &length);
746    
747                    /* Only send data up to null byte, if any */
748                    firstnull = (uint8 *) strchr((char *) data, '\0');
749                    if (firstnull)
750                    {
751                            length = firstnull - data + 1;
752                    }
753            }
754    #ifdef USE_UNICODE_CLIPBOARD
755            else if (selection_request.target == format_utf8_string_atom)
756            {
757                    /* We're expecting a CF_UNICODETEXT response */
758                    iconv_t cd = iconv_open("UTF-8", WINDOWS_CODEPAGE);
759                    if (cd != (iconv_t)-1)
760                  {                  {
761                          length = firstnull - data + 1;                          size_t utf8_length = length * 2;
762                            char* utf8_data = malloc(utf8_length);
763                            size_t utf8_length_remaining = utf8_length;
764                            char* utf8_data_remaining = utf8_data;
765                            char* data_remaining = (char*)data;
766                            size_t length_remaining = (size_t)length;
767                            if (utf8_data == NULL)
768                            {
769                                    iconv_close(cd);
770                                    return;
771                            }
772                            iconv(cd, &data_remaining, &length_remaining, &utf8_data_remaining, &utf8_length_remaining);
773                            iconv_close(cd);
774                            free_data = True;
775                            data = (uint8*)utf8_data;
776                            length = utf8_length - utf8_length_remaining;
777                  }                  }
778          }          }
779            else if (selection_request.target == format_unicode_atom)
780            {
781                    /* We're expecting a CF_UNICODETEXT response, so what we're
782                       receiving matches our requirements and there's no need
783                       for further conversions. */
784            }
785    #endif
786            else if (selection_request.target == rdesktop_clipboard_formats_atom)
787            {
788                    /* Pass as-is */
789            }
790            else
791            {
792                    xclip_refuse_selection(&selection_request);
793                    has_selection_request = False;
794                    return;
795            }
796    
797            xclip_provide_selection(&selection_request, selection_request.target, 8, data, length - 1);
798            has_selection_request = False;
799    
800          xclip_provide_selection(&selection_request, XA_STRING, 8, data, length - 1);          if (free_data)
801                    free(data);
802  }  }
803    
804  void  void
# Line 326  ui_clip_request_data(uint32 format) Line 807  ui_clip_request_data(uint32 format)
807          Window selectionowner;          Window selectionowner;
808    
809          DEBUG_CLIPBOARD(("Request from server for format %d\n", format));          DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
810            rdp_clipboard_request_format = format;
811    
812          if (rdesktop_is_selection_owner)          if (rdesktop_is_selection_owner)
813          {          {
# Line 361  ui_clip_request_data(uint32 format) Line 843  ui_clip_request_data(uint32 format)
843  void  void
844  ui_clip_sync(void)  ui_clip_sync(void)
845  {  {
846          cliprdr_send_text_format_announce();          cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
847  }  }
848    
849    
# Line 378  xclip_init(void) Line 860  xclip_init(void)
860          rdesktop_clipboard_target_atom =          rdesktop_clipboard_target_atom =
861                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
862          incr_atom = XInternAtom(g_display, "INCR", False);          incr_atom = XInternAtom(g_display, "INCR", False);
863          targets[0] = targets_atom;          format_string_atom = XInternAtom(g_display, "STRING", False);
864          targets[1] = XInternAtom(g_display, "TEXT", False);          format_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False);
865          targets[2] = XInternAtom(g_display, "UTF8_STRING", False);          format_unicode_atom = XInternAtom(g_display, "text/unicode", False);
866          targets[3] = XInternAtom(g_display, "text/unicode", False);          num_targets = 0;
867          targets[4] = XInternAtom(g_display, "TIMESTAMP", False);          targets[num_targets++] = targets_atom;
868          targets[5] = XA_STRING;          targets[num_targets++] = timestamp_atom;
869            targets[num_targets++] = rdesktop_clipboard_formats_atom;
870            targets[num_targets++] = format_string_atom;
871            #ifdef USE_UNICODE_CLIPBOARD
872            targets[num_targets++] = format_utf8_string_atom;
873            #endif
874            targets[num_targets++] = format_unicode_atom;
875            targets[num_targets++] = XA_STRING;
876    
877          /* rdesktop sets _RDESKTOP_CLIPBOARD_FORMATS on the root window when acquiring the clipboard.          /* 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. */             Other interested rdesktops can use this to notify their server of the available formats. */

Legend:
Removed from v.551  
changed lines
  Added in v.1037

  ViewVC Help
Powered by ViewVC 1.1.26