/[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 1203 by ossman_, Mon Mar 27 08:39:20 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 || target == format_utf8_string_atom)
268            {
269                    if (rdp_clipboard_request_format != RDP_CF_TEXT)
270                            return False;
271    
272                    /* Make an attempt to convert any string we send to Unicode.
273                       We don't know what the RDP server's ANSI Codepage is, or how to convert
274                       to it, so using CF_TEXT is not safe (and is unnecessary, since all
275                       WinNT versions are Unicode-minded).
276                     */
277                    size_t unicode_buffer_size;
278                    char *unicode_buffer;
279                    iconv_t cd;
280    
281                    if (target == format_string_atom)
282                    {
283                            char *locale_charset = nl_langinfo(CODESET);
284                            cd = iconv_open(WINDOWS_CODEPAGE, locale_charset);
285                            if (cd == (iconv_t) - 1)
286                            {
287                                    DEBUG_CLIPBOARD(("Locale charset %s not found in iconv. Unable to convert clipboard text.\n", locale_charset));
288                                    return False;
289                            }
290                            unicode_buffer_size = source_size * 4;
291                    }
292                    else if (target == format_unicode_atom)
293                    {
294                            cd = iconv_open(WINDOWS_CODEPAGE, "UCS-2");
295                            if (cd == (iconv_t) - 1)
296                            {
297                                    return False;
298                            }
299                            unicode_buffer_size = source_size;
300                    }
301                    else if (target == format_utf8_string_atom)
302                    {
303                            cd = iconv_open(WINDOWS_CODEPAGE, "UTF-8");
304                            if (cd == (iconv_t) - 1)
305                            {
306                                    return False;
307                            }
308                            /* UTF-8 is guaranteed to be less or equally compact
309                               as UTF-16 for all Unicode chars >=2 bytes.
310                             */
311                            unicode_buffer_size = source_size * 2;
312                    }
313                    else
314                    {
315                            return False;
316                    }
317    
318                    unicode_buffer = xmalloc(unicode_buffer_size);
319                    size_t unicode_buffer_size_remaining = unicode_buffer_size;
320                    char *unicode_buffer_remaining = unicode_buffer;
321                    char *data_remaining = (char *) source;
322                    size_t data_size_remaining = source_size;
323                    iconv(cd, (ICONV_CONST char **) &data_remaining, &data_size_remaining,
324                          &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 =
330                            utf16_lf2crlf((uint8 *) unicode_buffer, &translated_data_size);
331                    if (translated_data != NULL)
332                    {
333                            DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n",
334                                             translated_data_size));
335                            helper_cliprdr_send_response(translated_data, translated_data_size);
336                            xfree(translated_data); /* Not the same thing as XFree! */
337                    }
338    
339                    xfree(unicode_buffer);
340    
341                    return True;
342            }
343    #else
344            if (target == format_string_atom)
345            {
346                    uint8 *translated_data;
347                    uint32 length = source_size;
348    
349                    if (rdp_clipboard_request_format != RDP_CF_TEXT)
350                            return False;
351    
352                    DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
353                    translated_data = lf2crlf(source, &length);
354                    if (translated_data != NULL)
355                    {
356                            helper_cliprdr_send_response(translated_data, length);
357                            xfree(translated_data); /* Not the same thing as XFree! */
358                    }
359    
360                    return True;
361            }
362    #endif
363            else if (target == rdesktop_clipboard_formats_atom)
364            {
365                    helper_cliprdr_send_response(source, source_size + 1);
366    
367                    return True;
368            }
369            else
370            {
371                    return False;
372            }
373    }
374    
375  /* This function is called for SelectionNotify events.  /* This function is called for SelectionNotify events.
376     The SelectionNotify message is sent from the clipboard owner to the requestor     The SelectionNotify event is sent from the clipboard owner to the requestor
377     after his request was satisfied.     after his request was satisfied.
378     If this function is called, we're the requestor side. */     If this function is called, we're the requestor side. */
379  #ifndef MAKE_PROTO  #ifndef MAKE_PROTO
# Line 161  xclip_handle_SelectionNotify(XSelectionE Line 382  xclip_handle_SelectionNotify(XSelectionE
382  {  {
383          unsigned long nitems, bytes_left;          unsigned long nitems, bytes_left;
384          XWindowAttributes wa;          XWindowAttributes wa;
385          Atom type, best_target, text_target;          Atom type;
386          Atom *supported_targets;          Atom *supported_targets;
387          int res, i, format;          int res, i, format;
388          uint8 *data;          uint8 *data = NULL;
389    
390          if (event->property == None)          if (event->property == None)
391                  goto fail;                  goto fail;
# Line 187  xclip_handle_SelectionNotify(XSelectionE Line 408  xclip_handle_SelectionNotify(XSelectionE
408                  goto fail;                  goto fail;
409          }          }
410    
   
411          if (type == incr_atom)          if (type == incr_atom)
412          {          {
413                  DEBUG_CLIPBOARD(("Received INCR.\n"));                  DEBUG_CLIPBOARD(("Received INCR.\n"));
# Line 199  xclip_handle_SelectionNotify(XSelectionE Line 419  xclip_handle_SelectionNotify(XSelectionE
419                  }                  }
420                  XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);                  XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
421                  XFree(data);                  XFree(data);
422                    g_incr_target = event->target;
423                  g_waiting_for_INCR = 1;                  g_waiting_for_INCR = 1;
424                  return;                  return;
425          }          }
# Line 208  xclip_handle_SelectionNotify(XSelectionE Line 429  xclip_handle_SelectionNotify(XSelectionE
429          /* Negotiate target format */          /* Negotiate target format */
430          if (event->target == targets_atom)          if (event->target == targets_atom)
431          {          {
432                  /* FIXME: We should choose format here based on what the server wanted */                  /* Determine the best of text targets that we have available:
433                  best_target = XA_STRING;                     Prefer UTF8_STRING > text/unicode (unspecified encoding) > STRING
434                       (ignore TEXT and COMPOUND_TEXT because we don't have code to handle them)
435                     */
436                    int text_target_satisfaction = 0;
437                    Atom best_text_target = 0;      /* measures how much we're satisfied with what we found */
438                  if (type != None)                  if (type != None)
439                  {                  {
440                          supported_targets = (Atom *) data;                          supported_targets = (Atom *) data;
                         text_target = XInternAtom(g_display, "TEXT", False);  
441                          for (i = 0; i < nitems; i++)                          for (i = 0; i < nitems; i++)
442                          {                          {
443                                  DEBUG_CLIPBOARD(("Target %d: %s\n", i,                                  DEBUG_CLIPBOARD(("Target %d: %s\n", i,
444                                                   XGetAtomName(g_display, supported_targets[i])));                                                   XGetAtomName(g_display, supported_targets[i])));
445                                  if (supported_targets[i] == text_target)                                  if (supported_targets[i] == format_string_atom)
446                                  {                                  {
447                                          DEBUG_CLIPBOARD(("Other party supports TEXT, choosing that as best_target\n"));                                          if (text_target_satisfaction < 1)
448                                          best_target = text_target;                                          {
449                                          break;                                                  DEBUG_CLIPBOARD(("Other party supports STRING, choosing that as best_target\n"));
450                                                    best_text_target = supported_targets[i];
451                                                    text_target_satisfaction = 1;
452                                            }
453                                  }                                  }
454    #ifdef USE_UNICODE_CLIPBOARD
455                                    else if (supported_targets[i] == format_unicode_atom)
456                                    {
457                                            if (text_target_satisfaction < 2)
458                                            {
459                                                    DEBUG_CLIPBOARD(("Other party supports text/unicode, choosing that as best_target\n"));
460                                                    best_text_target = supported_targets[i];
461                                                    text_target_satisfaction = 2;
462                                            }
463                                    }
464                                    else if (supported_targets[i] == format_utf8_string_atom)
465                                    {
466                                            if (text_target_satisfaction < 3)
467                                            {
468                                                    DEBUG_CLIPBOARD(("Other party supports UTF8_STRING, choosing that as best_target\n"));
469                                                    best_text_target = supported_targets[i];
470                                                    text_target_satisfaction = 3;
471                                            }
472                                    }
473    #endif
474                          }                          }
                         XFree(data);  
475                  }                  }
476    
477                  XConvertSelection(g_display, primary_atom, best_target,                  /* Kickstarting the next step in the process of satisfying RDP's
478                                    rdesktop_clipboard_target_atom, g_wnd, event->time);                     clipboard request -- specifically, requesting the actual clipboard data.
479                  return;                   */
480          }                  if (best_text_target != 0)
481                    {
482          /* Translate linebreaks, but only if not getting data from                          XConvertSelection(g_display, clipboard_atom, best_text_target,
483             other rdesktop instance */                                            rdesktop_clipboard_target_atom, g_wnd, event->time);
484          if (event->target != rdesktop_clipboard_formats_atom)                          return;
485          {                  }
486                  uint8 *translated_data;                  else
487                  uint32 length = nitems;                  {
488                            DEBUG_CLIPBOARD(("Unable to find a textual target to satisfy RDP clipboard text request\n"));
489                  DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));                          goto fail;
490                  translated_data = lf2crlf(data, &length);                  }
                 cliprdr_send_data(translated_data, length + 1);  
                 xfree(translated_data); /* Not the same thing as XFree! */  
491          }          }
492          else          else
493          {          {
494                  cliprdr_send_data(data, nitems + 1);                  if (!xclip_send_data_with_convert(data, nitems, event->target))
495                    {
496                            goto fail;
497                    }
498          }          }
499    
500          XFree(data);          XFree(data);
501    
         if (!rdesktop_is_selection_owner)  
                 cliprdr_send_simple_native_format_announce(CF_TEXT);  
502          return;          return;
503    
504        fail:        fail:
505          XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);          XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
506          XFree(data);          if (data)
507          cliprdr_send_data(NULL, 0);                  XFree(data);
508            helper_cliprdr_send_empty_response();
509  }  }
510    
511  /* This function is called for SelectionRequest events.  /* This function is called for SelectionRequest events.
512     The SelectionRequest message is sent from the requestor to the clipboard owner     The SelectionRequest event is sent from the requestor to the clipboard owner
513     to request clipboard data.     to request clipboard data.
514   */   */
515  void  void
# Line 281  xclip_handle_SelectionRequest(XSelection Line 528  xclip_handle_SelectionRequest(XSelection
528    
529          if (event->target == targets_atom)          if (event->target == targets_atom)
530          {          {
531                  xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, NUM_TARGETS);                  xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, num_targets);
532                  return;                  return;
533          }          }
534          else if (event->target == timestamp_atom)          else if (event->target == timestamp_atom)
# Line 289  xclip_handle_SelectionRequest(XSelection Line 536  xclip_handle_SelectionRequest(XSelection
536                  xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);                  xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);
537                  return;                  return;
538          }          }
         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? */  
         }  
539          else          else
540          {          {
541                  format = CF_TEXT;                  /* All the following targets require an async operation with the RDP server
542          }                     and currently we don't do X clipboard request queueing so we can only
543                       handle one such request at a time. */
544                    if (has_selection_request)
545                    {
546                            DEBUG_CLIPBOARD(("Error: Another clipboard request was already sent to the RDP server and not yet responded. Refusing this request.\n"));
547                            xclip_refuse_selection(event);
548                            return;
549                    }
550                    if (event->target == rdesktop_clipboard_formats_atom)
551                    {
552                            /* Before the requestor makes a request for the _RDESKTOP_CLIPBOARD_FORMATS target,
553                               he should declare requestor[_RDESKTOP_CLIPBOARD_TARGET] = CF_SOMETHING.
554                               Otherwise, we default to RDP_CF_TEXT.
555                             */
556                            res = XGetWindowProperty(g_display, event->requestor,
557                                                     rdesktop_clipboard_target_atom, 0, 1, True,
558                                                     XA_INTEGER, &type, &format, &nitems, &bytes_left,
559                                                     &prop_return);
560                            wanted_format = (uint32 *) prop_return;
561                            format = (res == Success) ? *wanted_format : RDP_CF_TEXT;
562                            XFree(prop_return);
563                    }
564                    else if (event->target == format_string_atom || event->target == XA_STRING)
565                    {
566                            /* STRING and XA_STRING are defined to be ISO8859-1 */
567                            format = CF_TEXT;
568                    }
569                    else if (event->target == format_utf8_string_atom)
570                    {
571    #ifdef USE_UNICODE_CLIPBOARD
572                            format = CF_UNICODETEXT;
573    #else
574                            DEBUG_CLIPBOARD(("Requested target unavailable due to lack of Unicode support. (It was not in TARGETS, so why did you ask for it?!)\n"));
575                            xclip_refuse_selection(event);
576                            return;
577    #endif
578                    }
579                    else if (event->target == format_unicode_atom)
580                    {
581                            /* Assuming text/unicode to be UTF-16 */
582                            format = CF_UNICODETEXT;
583                    }
584                    else
585                    {
586                            DEBUG_CLIPBOARD(("Requested target unavailable. (It was not in TARGETS, so why did you ask for it?!)\n"));
587                            xclip_refuse_selection(event);
588                            return;
589                    }
590    
591          cliprdr_send_data_request(format);                  cliprdr_send_data_request(format);
592          selection_request = *event;                  selection_request = *event;
593          /* wait for data */                  has_selection_request = True;
594                    return;         /* wait for data */
595            }
596  }  }
597    
598  /* 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
599     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
600     roundtrip). This event symbolizes this rdesktop lost onwership of the clipboard     roundtrip).
601    
602       This event (SelectionClear) symbolizes this rdesktop lost onwership of the clipboard
603     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
604     client offers and announce that to RDP. */     client offers and announce that to RDP. */
605  void  void
# Line 319  xclip_handle_SelectionClear(void) Line 608  xclip_handle_SelectionClear(void)
608          DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));          DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
609          have_primary = 0;          have_primary = 0;
610          XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);          XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);
611          cliprdr_send_simple_native_format_announce(CF_TEXT);          /* FIXME:
612               Without XFIXES, we cannot reliably know the formats offered by the
613               new owner of the X11 clipboard, so we just lie about him
614               offering RDP_CF_TEXT. */
615            cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
616  }  }
617    
618  /* 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 633  xclip_handle_PropertyNotify(XPropertyEve
633    
634                  while (bytes_left > 0)                  while (bytes_left > 0)
635                  {                  {
636                            /* Unlike the specification, we don't set the 'delete' arugment to True
637                               since we slurp the INCR's chunks in even-smaller chunks of 4096 bytes. */
638                          if ((XGetWindowProperty                          if ((XGetWindowProperty
639                               (g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,                               (g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,
640                                False, AnyPropertyType, &type, &format, &nitems, &bytes_left,                                False, AnyPropertyType, &type, &format, &nitems, &bytes_left,
# Line 351  xclip_handle_PropertyNotify(XPropertyEve Line 646  xclip_handle_PropertyNotify(XPropertyEve
646    
647                          if (nitems == 0)                          if (nitems == 0)
648                          {                          {
649                                    /* INCR transfer finished */
650                                  XGetWindowAttributes(g_display, g_wnd, &wa);                                  XGetWindowAttributes(g_display, g_wnd, &wa);
651                                  XSelectInput(g_display, g_wnd,                                  XSelectInput(g_display, g_wnd,
652                                               (wa.your_event_mask ^ PropertyChangeMask));                                               (wa.your_event_mask ^ PropertyChangeMask));
# Line 359  xclip_handle_PropertyNotify(XPropertyEve Line 655  xclip_handle_PropertyNotify(XPropertyEve
655    
656                                  if (g_clip_buflen > 0)                                  if (g_clip_buflen > 0)
657                                  {                                  {
658                                          cliprdr_send_data(g_clip_buffer, g_clip_buflen + 1);                                          if (!xclip_send_data_with_convert
659                                                (g_clip_buffer, g_clip_buflen, g_incr_target))
660                                          if (!rdesktop_is_selection_owner)                                          {
661                                                  cliprdr_send_simple_native_format_announce(CF_TEXT);                                                  helper_cliprdr_send_empty_response();
662                                            }
663                                          xfree(g_clip_buffer);                                          xfree(g_clip_buffer);
664                                          g_clip_buffer = 0;                                          g_clip_buffer = NULL;
665                                          g_clip_buflen = 0;                                          g_clip_buflen = 0;
666                                  }                                  }
667                          }                          }
668                          else                          else
669                          {                          {
670                                  uint8 *translated_data;                                  /* Another chunk in the INCR transfer */
671                                  uint32 length = nitems;                                  offset += (nitems / 4); /* offset at which to begin the next slurp */
672                                  uint8 *tmp;                                  g_clip_buffer = xrealloc(g_clip_buffer, g_clip_buflen + nitems);
673                                    memcpy(g_clip_buffer + g_clip_buflen, data, nitems);
674                                  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;  
675    
676                                  XFree(data);                                  XFree(data);
677                          }                          }
678                  }                  }
679                  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 */  
680                  return;                  return;
681            }
682    
683          if (event->state == PropertyNewValue)          if ((event->atom == rdesktop_clipboard_formats_atom) &&
684                (event->window == DefaultRootWindow(g_display)) &&
685                !have_primary /* not interested in our own events */ )
686          {          {
687                  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))  
688                  {                  {
689                          cliprdr_send_native_format_announce(data, nitems);                          DEBUG_CLIPBOARD(("xclip_handle_PropertyNotify: getting fellow rdesktop formats\n"));
690                          rdesktop_is_selection_owner = 1;  
691                          return;                          res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),
692                                                     rdesktop_clipboard_formats_atom, 0,
693                                                     XMaxRequestSize(g_display), False, XA_STRING,
694                                                     &type, &format, &nitems, &bytes_left, &data);
695    
696                            if ((res == Success) && (nitems > 0))
697                            {
698                                    cliprdr_send_native_format_announce(data, nitems);
699                                    rdesktop_is_selection_owner = 1;
700                                    return;
701                            }
702                  }                  }
         }  
703    
704          /* PropertyDelete, or XGetWindowProperty failed */                  /* For some reason, we couldn't announce the native formats */
705          cliprdr_send_simple_native_format_announce(CF_TEXT);                  cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
706          rdesktop_is_selection_owner = 0;                  rdesktop_is_selection_owner = 0;
707            }
708  }  }
709  #endif  #endif
710    
# Line 449  ui_clip_format_announce(uint8 * data, ui Line 734  ui_clip_format_announce(uint8 * data, ui
734                  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");                  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
735  }  }
736    
737    /* Called when the RDP server responds with clipboard data (after we've requested it). */
738  void  void
739  ui_clip_handle_data(uint8 * data, uint32 length)  ui_clip_handle_data(uint8 * data, uint32 length)
740  {  {
741          if (selection_request.target != rdesktop_clipboard_formats_atom)          BOOL free_data = False;
742    
743            if (selection_request.target == format_string_atom || selection_request.target == XA_STRING)
744          {          {
745                    /* We're expecting a CF_TEXT response */
746                  uint8 *firstnull;                  uint8 *firstnull;
747    
748                  /* translate linebreaks */                  /* translate linebreaks */
# Line 467  ui_clip_handle_data(uint8 * data, uint32 Line 755  ui_clip_handle_data(uint8 * data, uint32
755                          length = firstnull - data + 1;                          length = firstnull - data + 1;
756                  }                  }
757          }          }
758    #ifdef USE_UNICODE_CLIPBOARD
759            else if (selection_request.target == format_utf8_string_atom)
760            {
761                    /* We're expecting a CF_UNICODETEXT response */
762                    iconv_t cd = iconv_open("UTF-8", WINDOWS_CODEPAGE);
763                    if (cd != (iconv_t) - 1)
764                    {
765                            size_t utf8_length = length * 2;
766                            char *utf8_data = malloc(utf8_length);
767                            size_t utf8_length_remaining = utf8_length;
768                            char *utf8_data_remaining = utf8_data;
769                            char *data_remaining = (char *) data;
770                            size_t length_remaining = (size_t) length;
771                            if (utf8_data == NULL)
772                            {
773                                    iconv_close(cd);
774                                    return;
775                            }
776                            iconv(cd, (ICONV_CONST char **) &data_remaining, &length_remaining,
777                                  &utf8_data_remaining, &utf8_length_remaining);
778                            iconv_close(cd);
779                            free_data = True;
780                            data = (uint8 *) utf8_data;
781                            length = utf8_length - utf8_length_remaining;
782                    }
783            }
784            else if (selection_request.target == format_unicode_atom)
785            {
786                    /* We're expecting a CF_UNICODETEXT response, so what we're
787                       receiving matches our requirements and there's no need
788                       for further conversions. */
789            }
790    #endif
791            else if (selection_request.target == rdesktop_clipboard_formats_atom)
792            {
793                    /* Pass as-is */
794            }
795            else
796            {
797                    xclip_refuse_selection(&selection_request);
798                    has_selection_request = False;
799                    return;
800            }
801    
802          xclip_provide_selection(&selection_request, XA_STRING, 8, data, length - 1);          xclip_provide_selection(&selection_request, selection_request.target, 8, data, length - 1);
803            has_selection_request = False;
804    
805            if (free_data)
806                    free(data);
807  }  }
808    
809  void  void
# Line 477  ui_clip_request_data(uint32 format) Line 812  ui_clip_request_data(uint32 format)
812          Window selectionowner;          Window selectionowner;
813    
814          DEBUG_CLIPBOARD(("Request from server for format %d\n", format));          DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
815            rdp_clipboard_request_format = format;
816    
817          if (rdesktop_is_selection_owner)          if (rdesktop_is_selection_owner)
818          {          {
# Line 506  ui_clip_request_data(uint32 format) Line 842  ui_clip_request_data(uint32 format)
842          }          }
843    
844          /* No data available */          /* No data available */
845          cliprdr_send_data(NULL, 0);          helper_cliprdr_send_empty_response();
846  }  }
847    
848  void  void
849  ui_clip_sync(void)  ui_clip_sync(void)
850  {  {
851          cliprdr_send_simple_native_format_announce(CF_TEXT);          cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
852  }  }
853    
854    
# Line 529  xclip_init(void) Line 865  xclip_init(void)
865          rdesktop_clipboard_target_atom =          rdesktop_clipboard_target_atom =
866                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
867          incr_atom = XInternAtom(g_display, "INCR", False);          incr_atom = XInternAtom(g_display, "INCR", False);
868          targets[0] = targets_atom;          format_string_atom = XInternAtom(g_display, "STRING", False);
869          targets[1] = XInternAtom(g_display, "TEXT", False);          format_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False);
870          targets[2] = XInternAtom(g_display, "STRING", False);          format_unicode_atom = XInternAtom(g_display, "text/unicode", False);
871          targets[3] = XInternAtom(g_display, "text/unicode", False);          num_targets = 0;
872          targets[4] = XInternAtom(g_display, "TIMESTAMP", False);          targets[num_targets++] = targets_atom;
873          targets[5] = XA_STRING;          targets[num_targets++] = timestamp_atom;
874            targets[num_targets++] = rdesktop_clipboard_formats_atom;
875            targets[num_targets++] = format_string_atom;
876    #ifdef USE_UNICODE_CLIPBOARD
877            targets[num_targets++] = format_utf8_string_atom;
878    #endif
879            targets[num_targets++] = format_unicode_atom;
880            targets[num_targets++] = XA_STRING;
881    
882          /* 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.
883             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.1203

  ViewVC Help
Powered by ViewVC 1.1.26