/[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 1028 by astrand, Mon Nov 14 14:46:16 2005 UTC revision 1037 by forsberg, Mon Jan 2 15:55:59 2006 UTC
# Line 32  Line 32 
32      http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/dataexchange/clipboard/clipboardformats.asp      http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/dataexchange/clipboard/clipboardformats.asp
33  */  */
34    
35  #define NUM_TARGETS 6  #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;
# Line 63  static Atom rdesktop_clipboard_target_at Line 79  static Atom rdesktop_clipboard_target_at
79       denoting all Windows native clipboard formats it offers via       denoting all Windows native clipboard formats it offers via
80       requests of the _RDESKTOP_CLIPBOARD_FORMATS target. */       requests of the _RDESKTOP_CLIPBOARD_FORMATS target. */
81  static Atom rdesktop_clipboard_formats_atom;  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") */  /* Atom of the INCR clipboard type (see ICCCM on "INCR Properties") */
84  static Atom incr_atom;  static Atom incr_atom;
85  /* Stores the last "selection request" (= another X client requesting clipboard data from us).  /* Stores the last "selection request" (= another X client requesting clipboard data from us).
# Line 70  static Atom incr_atom; Line 87  static Atom incr_atom;
87     When we receive the response from the RDP server (asynchronously), this variable gives us     When we receive the response from the RDP server (asynchronously), this variable gives us
88     the context to proceed. */     the context to proceed. */
89  static XSelectionRequestEvent selection_request;  static XSelectionRequestEvent selection_request;
90    /* 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. */  /* Array of offered clipboard targets that will be sent to fellow X clients upon a TARGETS request. */
99  static Atom targets[NUM_TARGETS];  static Atom targets[MAX_TARGETS];
100    static int num_targets;
101  /* Denotes that this client currently holds the PRIMARY selection. */  /* 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,  /* Denotes that an rdesktop (not this rdesktop) is owning the selection,
# Line 80  static int rdesktop_is_selection_owner = Line 106  static int rdesktop_is_selection_owner =
106    
107  /* Denotes that an INCR ("chunked") transfer is in progress. */  /* Denotes that an INCR ("chunked") transfer is in progress. */
108  static int g_waiting_for_INCR = 0;  static int g_waiting_for_INCR = 0;
109    /* Denotes the target format of the ongoing INCR ("chunked") transfer. */
110    static Atom g_incr_target = 0;
111  /* Buffers an INCR transfer. */  /* Buffers an INCR transfer. */
112  static uint8 *g_clip_buffer = 0;  static uint8 *g_clip_buffer = 0;
113  /* Denotes the size of g_clip_buffer. */  /* Denotes the size of g_clip_buffer. */
# Line 103  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 129  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 151  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.  /* This function is called for SelectionNotify events.
374     The SelectionNotify message is sent from the clipboard owner to the requestor     The SelectionNotify event is sent from the clipboard owner to the requestor
375     after his request was satisfied.     after his request was satisfied.
376     If this function is called, we're the requestor side. */     If this function is called, we're the requestor side. */
377  #ifndef MAKE_PROTO  #ifndef MAKE_PROTO
# Line 161  xclip_handle_SelectionNotify(XSelectionE Line 380  xclip_handle_SelectionNotify(XSelectionE
380  {  {
381          unsigned long nitems, bytes_left;          unsigned long nitems, bytes_left;
382          XWindowAttributes wa;          XWindowAttributes wa;
383          Atom type, best_target, text_target;          Atom type;
384          Atom *supported_targets;          Atom *supported_targets;
385          int res, i, format;          int res, i, format;
386          uint8 *data;          uint8 *data;
# Line 187  xclip_handle_SelectionNotify(XSelectionE Line 406  xclip_handle_SelectionNotify(XSelectionE
406                  goto fail;                  goto fail;
407          }          }
408    
   
409          if (type == incr_atom)          if (type == incr_atom)
410          {          {
411                  DEBUG_CLIPBOARD(("Received INCR.\n"));                  DEBUG_CLIPBOARD(("Received INCR.\n"));
# Line 199  xclip_handle_SelectionNotify(XSelectionE Line 417  xclip_handle_SelectionNotify(XSelectionE
417                  }                  }
418                  XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);                  XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
419                  XFree(data);                  XFree(data);
420                    g_incr_target = event->target;
421                  g_waiting_for_INCR = 1;                  g_waiting_for_INCR = 1;
422                  return;                  return;
423          }          }
# Line 208  xclip_handle_SelectionNotify(XSelectionE Line 427  xclip_handle_SelectionNotify(XSelectionE
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                                            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                                    }
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                                          DEBUG_CLIPBOARD(("Other party supports TEXT, choosing that as best_target\n"));                                          if (text_target_satisfaction < 3)
465                                          best_target = text_target;                                          {
466                                          break;                                                  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          /* Translate linebreaks, but only if not getting data from                          XConvertSelection(g_display, clipboard_atom, best_text_target, rdesktop_clipboard_target_atom, g_wnd, event->time);
481             other rdesktop instance */                          return;
482          if (event->target != rdesktop_clipboard_formats_atom)                  }
483          {                  else
484                  uint8 *translated_data;                  {
485                  uint32 length = nitems;                          DEBUG_CLIPBOARD(("Unable to find a textual target to satisfy RDP clipboard text request\n"));
486                            goto fail;
487                  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_simple_native_format_announce(CF_TEXT);  
499          return;          return;
500    
501        fail:        fail:
502          XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);          XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
503          XFree(data);          XFree(data);
504          cliprdr_send_data(NULL, 0);          helper_cliprdr_send_empty_response();
505  }  }
506    
507  /* This function is called for SelectionRequest events.  /* This function is called for SelectionRequest events.
508     The SelectionRequest message is sent from the requestor to the clipboard owner     The SelectionRequest event is sent from the requestor to the clipboard owner
509     to request clipboard data.     to request clipboard data.
510   */   */
511  void  void
# Line 281  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 289  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  /* When this rdesktop holds ownership over the clipboard, it means the clipboard data  /* While this rdesktop holds ownership over the clipboard, it means the clipboard data
595     is offered by the RDP server (and when its pasted inside RDP, there's no network     is offered by the RDP server (and when it is pasted inside RDP, there's no network
596     roundtrip). This event symbolizes this rdesktop lost onwership of the clipboard     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     to some other X client. We should find out what clipboard formats this other
600     client offers and announce that to RDP. */     client offers and announce that to RDP. */
601  void  void
# Line 319  xclip_handle_SelectionClear(void) Line 604  xclip_handle_SelectionClear(void)
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_simple_native_format_announce(CF_TEXT);          /* 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. */  /* Called when any property changes in our window or the root window. */
# Line 340  xclip_handle_PropertyNotify(XPropertyEve Line 629  xclip_handle_PropertyNotify(XPropertyEve
629    
630                  while (bytes_left > 0)                  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                          if ((XGetWindowProperty
635                               (g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,                               (g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,
636                                False, AnyPropertyType, &type, &format, &nitems, &bytes_left,                                False, AnyPropertyType, &type, &format, &nitems, &bytes_left,
# Line 351  xclip_handle_PropertyNotify(XPropertyEve Line 642  xclip_handle_PropertyNotify(XPropertyEve
642    
643                          if (nitems == 0)                          if (nitems == 0)
644                          {                          {
645                                    /* INCR transfer finished */
646                                  XGetWindowAttributes(g_display, g_wnd, &wa);                                  XGetWindowAttributes(g_display, g_wnd, &wa);
647                                  XSelectInput(g_display, g_wnd,                                  XSelectInput(g_display, g_wnd,
648                                               (wa.your_event_mask ^ PropertyChangeMask));                                               (wa.your_event_mask ^ PropertyChangeMask));
# Line 359  xclip_handle_PropertyNotify(XPropertyEve Line 651  xclip_handle_PropertyNotify(XPropertyEve
651    
652                                  if (g_clip_buflen > 0)                                  if (g_clip_buflen > 0)
653                                  {                                  {
654                                          cliprdr_send_data(g_clip_buffer, g_clip_buflen + 1);                                          if (!xclip_send_data_with_convert(g_clip_buffer, g_clip_buflen, g_incr_target))
655                                            {
656                                          if (!rdesktop_is_selection_owner)                                                  helper_cliprdr_send_empty_response();
657                                                  cliprdr_send_simple_native_format_announce(CF_TEXT);                                          }
   
658                                          xfree(g_clip_buffer);                                          xfree(g_clip_buffer);
659                                          g_clip_buffer = 0;                                          g_clip_buffer = NULL;
660                                          g_clip_buflen = 0;                                          g_clip_buflen = 0;
661                                  }                                  }
662                          }                          }
663                          else                          else
664                          {                          {
665                                  uint8 *translated_data;                                  /* Another chunk in the INCR transfer */
666                                  uint32 length = nitems;                                  offset += (nitems / 4); /* offset at which to begin the next slurp */
667                                  uint8 *tmp;                                  g_clip_buffer = xrealloc(g_clip_buffer, g_clip_buflen + nitems);
668                                    memcpy(g_clip_buffer + g_clip_buflen, data, nitems);
669                                  offset += (length / 4);                                  g_clip_buflen += nitems;
                                 DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));  
                                 translated_data = lf2crlf(data, &length);  
   
                                 tmp = xmalloc(length + g_clip_buflen);  
                                 strncpy((char *) tmp, (char *) g_clip_buffer, g_clip_buflen);  
                                 xfree(g_clip_buffer);  
   
                                 strncpy((char *) (tmp + g_clip_buflen), (char *) translated_data,  
                                         length);  
                                 xfree(translated_data);  
   
                                 g_clip_buffer = tmp;  
                                 g_clip_buflen += length;  
670    
671                                  XFree(data);                                  XFree(data);
672                          }                          }
673                  }                  }
674                  XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);                  XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
         }  
   
         if (event->atom != rdesktop_clipboard_formats_atom)  
                 return;  
   
         if (have_primary)       /* from us */  
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_simple_native_format_announce(CF_TEXT);                  cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
701          rdesktop_is_selection_owner = 0;                  rdesktop_is_selection_owner = 0;
702            }
703  }  }
704  #endif  #endif
705    
# Line 449  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 = (uint8 *) strchr((char *) 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 477  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 512  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_simple_native_format_announce(CF_TEXT);          cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
847  }  }
848    
849    
# Line 529  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, "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.1028  
changed lines
  Added in v.1037

  ViewVC Help
Powered by ViewVC 1.1.26