/[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 1207 by ossman_, Mon Mar 27 09:20:24 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;
55  extern Time g_last_gesturetime;  extern Time g_last_gesturetime;
56    extern BOOL g_rdpclip;
57    
58    /* Mode of operation.
59       - Auto: Look at both PRIMARY and CLIPBOARD and use the most recent.
60       - Non-auto: Look at just CLIPBOARD. */
61    static BOOL auto_mode = True;
62  /* Atoms of the two X selections we're dealing with: CLIPBOARD (explicit-copy) and PRIMARY (selection-copy) */  /* Atoms of the two X selections we're dealing with: CLIPBOARD (explicit-copy) and PRIMARY (selection-copy) */
63  static Atom clipboard_atom, primary_atom;  static Atom clipboard_atom, primary_atom;
64  /* Atom of the TARGETS clipboard target */  /* Atom of the TARGETS clipboard target */
# Line 53  static Atom timestamp_atom; Line 74  static Atom timestamp_atom;
74       before requesting clipboard data from a fellow rdesktop using       before requesting clipboard data from a fellow rdesktop using
75       the _RDESKTOP_CLIPBOARD_FORMATS target. */       the _RDESKTOP_CLIPBOARD_FORMATS target. */
76  static Atom rdesktop_clipboard_target_atom;  static Atom rdesktop_clipboard_target_atom;
77    /* Atoms _RDESKTOP_PRIMARY_TIMESTAMP_TARGET and _RDESKTOP_CLIPBOARD_TIMESTAMP_TARGET
78       are used to store the timestamps for when a window got ownership of the selections.
79       We use these to determine which is more recent and should be used. */
80    static Atom rdesktop_primary_timestamp_target_atom, rdesktop_clipboard_timestamp_target_atom;
81    /* Storage for timestamps since we get them in two separate notifications. */
82    static Time primary_timestamp, clipboard_timestamp;
83  /* Atom _RDESKTOP_CLIPBOARD_FORMATS which has multiple uses:  /* Atom _RDESKTOP_CLIPBOARD_FORMATS which has multiple uses:
84     - The clipboard target (X jargon for "clipboard format") for rdesktop-to-rdesktop interchange     - The clipboard target (X jargon for "clipboard format") for rdesktop-to-rdesktop interchange
85       of Windows native clipboard data.       of Windows native clipboard data.
# Line 63  static Atom rdesktop_clipboard_target_at Line 90  static Atom rdesktop_clipboard_target_at
90       denoting all Windows native clipboard formats it offers via       denoting all Windows native clipboard formats it offers via
91       requests of the _RDESKTOP_CLIPBOARD_FORMATS target. */       requests of the _RDESKTOP_CLIPBOARD_FORMATS target. */
92  static Atom rdesktop_clipboard_formats_atom;  static Atom rdesktop_clipboard_formats_atom;
93    static Atom format_string_atom, format_utf8_string_atom, format_unicode_atom;
94  /* Atom of the INCR clipboard type (see ICCCM on "INCR Properties") */  /* Atom of the INCR clipboard type (see ICCCM on "INCR Properties") */
95  static Atom incr_atom;  static Atom incr_atom;
96  /* 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 98  static Atom incr_atom;
98     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
99     the context to proceed. */     the context to proceed. */
100  static XSelectionRequestEvent selection_request;  static XSelectionRequestEvent selection_request;
101    /* Denotes we have a pending selection request. */
102    static Bool has_selection_request;
103    /* Stores the clipboard format (CF_TEXT, CF_UNICODETEXT etc.) requested in the last
104       CLIPDR_DATA_REQUEST (= the RDP server requesting clipboard data from us).
105       When we receive this data from whatever X client offering it, this variable gives us
106       the context to proceed.
107     */
108    static int rdp_clipboard_request_format;
109  /* 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. */
110  static Atom targets[NUM_TARGETS];  static Atom targets[MAX_TARGETS];
111    static int num_targets;
112  /* Denotes that this client currently holds the PRIMARY selection. */  /* Denotes that this client currently holds the PRIMARY selection. */
113  static int have_primary = 0;  static int have_primary = 0;
114  /* 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 117  static int rdesktop_is_selection_owner =
117    
118  /* Denotes that an INCR ("chunked") transfer is in progress. */  /* Denotes that an INCR ("chunked") transfer is in progress. */
119  static int g_waiting_for_INCR = 0;  static int g_waiting_for_INCR = 0;
120    /* Denotes the target format of the ongoing INCR ("chunked") transfer. */
121    static Atom g_incr_target = 0;
122  /* Buffers an INCR transfer. */  /* Buffers an INCR transfer. */
123  static uint8 *g_clip_buffer = 0;  static uint8 *g_clip_buffer = 0;
124  /* Denotes the size of g_clip_buffer. */  /* Denotes the size of g_clip_buffer. */
# Line 103  crlf2lf(uint8 * data, uint32 * length) Line 142  crlf2lf(uint8 * data, uint32 * length)
142          *length = dst - data;          *length = dst - data;
143  }  }
144    
145  /* Translate LF to CR-LF. To do this, we must allocate more memory.    #ifdef USE_UNICODE_CLIPBOARD
146    /* Translate LF to CR-LF. To do this, we must allocate more memory.
147       The returned string is null-terminated, as required by CF_UNICODETEXT.
148       The size is updated. */
149    static uint8 *
150    utf16_lf2crlf(uint8 * data, uint32 * size)
151    {
152            uint8 *result;
153            uint16 *inptr, *outptr;
154    
155            /* Worst case: Every char is LF */
156            result = xmalloc((*size * 2) + 2);
157            if (result == NULL)
158                    return NULL;
159    
160            inptr = (uint16 *) data;
161            outptr = (uint16 *) result;
162    
163            /* Check for a reversed BOM */
164            Bool swap_endianess = (*inptr == 0xfffe);
165    
166            while ((uint8 *) inptr < data + *size)
167            {
168                    uint16 uvalue = *inptr;
169                    if (swap_endianess)
170                            uvalue = ((uvalue << 8) & 0xff00) + (uvalue >> 8);
171                    if (uvalue == 0x0a)
172                            *outptr++ = swap_endianess ? 0x0d00 : 0x0d;
173                    *outptr++ = *inptr++;
174            }
175            *outptr++ = 0;          /* null termination */
176            *size = (uint8 *) outptr - result;
177    
178            return result;
179    }
180    #else
181    /* Translate LF to CR-LF. To do this, we must allocate more memory.
182     The length is updated. */     The length is updated. */
183  static uint8 *  static uint8 *
184  lf2crlf(uint8 * data, uint32 * length)  lf2crlf(uint8 * data, uint32 * length)
# Line 129  lf2crlf(uint8 * data, uint32 * length) Line 204  lf2crlf(uint8 * data, uint32 * length)
204    
205          return result;          return result;
206  }  }
207    #endif
208    
209  static void  static void
210  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 226  xclip_provide_selection(XSelectionReques
226          XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);          XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);
227  }  }
228    
229    /* Replies a clipboard requestor, telling that we're unable to satisfy his request for whatever reason.
230       This has the benefit of finalizing the clipboard negotiation and thus not leaving our requestor
231       lingering (and, potentially, stuck). */
232    static void
233    xclip_refuse_selection(XSelectionRequestEvent * req)
234    {
235            XEvent xev;
236    
237            xev.xselection.type = SelectionNotify;
238            xev.xselection.serial = 0;
239            xev.xselection.send_event = True;
240            xev.xselection.requestor = req->requestor;
241            xev.xselection.selection = req->selection;
242            xev.xselection.target = req->target;
243            xev.xselection.property = None;
244            xev.xselection.time = req->time;
245            XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);
246    }
247    
248    /* Wrapper for cliprdr_send_data which also cleans the request state. */
249    static void
250    helper_cliprdr_send_response(uint8 * data, uint32 length)
251    {
252            if (rdp_clipboard_request_format != 0)
253            {
254                    cliprdr_send_data(data, length);
255                    rdp_clipboard_request_format = 0;
256                    if (!rdesktop_is_selection_owner)
257                            cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
258            }
259    }
260    
261    /* Last resort, when we have to provide clipboard data but for whatever
262       reason couldn't get any.
263     */
264    static void
265    helper_cliprdr_send_empty_response()
266    {
267            helper_cliprdr_send_response(NULL, 0);
268    }
269    
270    /* Replies with clipboard data to RDP, converting it from the target format
271       to the expected RDP format as necessary. Returns true if data was sent.
272     */
273    static Bool
274    xclip_send_data_with_convert(uint8 * source, size_t source_size, Atom target)
275    {
276    #ifdef USE_UNICODE_CLIPBOARD
277            if (target == format_string_atom ||
278                target == format_unicode_atom || target == format_utf8_string_atom)
279            {
280                    if (rdp_clipboard_request_format != RDP_CF_TEXT)
281                            return False;
282    
283                    /* Make an attempt to convert any string we send to Unicode.
284                       We don't know what the RDP server's ANSI Codepage is, or how to convert
285                       to it, so using CF_TEXT is not safe (and is unnecessary, since all
286                       WinNT versions are Unicode-minded).
287                     */
288                    size_t unicode_buffer_size;
289                    char *unicode_buffer;
290                    iconv_t cd;
291    
292                    if (target == format_string_atom)
293                    {
294                            char *locale_charset = nl_langinfo(CODESET);
295                            cd = iconv_open(WINDOWS_CODEPAGE, locale_charset);
296                            if (cd == (iconv_t) - 1)
297                            {
298                                    DEBUG_CLIPBOARD(("Locale charset %s not found in iconv. Unable to convert clipboard text.\n", locale_charset));
299                                    return False;
300                            }
301                            unicode_buffer_size = source_size * 4;
302                    }
303                    else if (target == format_unicode_atom)
304                    {
305                            cd = iconv_open(WINDOWS_CODEPAGE, "UCS-2");
306                            if (cd == (iconv_t) - 1)
307                            {
308                                    return False;
309                            }
310                            unicode_buffer_size = source_size;
311                    }
312                    else if (target == format_utf8_string_atom)
313                    {
314                            cd = iconv_open(WINDOWS_CODEPAGE, "UTF-8");
315                            if (cd == (iconv_t) - 1)
316                            {
317                                    return False;
318                            }
319                            /* UTF-8 is guaranteed to be less or equally compact
320                               as UTF-16 for all Unicode chars >=2 bytes.
321                             */
322                            unicode_buffer_size = source_size * 2;
323                    }
324                    else
325                    {
326                            return False;
327                    }
328    
329                    unicode_buffer = xmalloc(unicode_buffer_size);
330                    size_t unicode_buffer_size_remaining = unicode_buffer_size;
331                    char *unicode_buffer_remaining = unicode_buffer;
332                    char *data_remaining = (char *) source;
333                    size_t data_size_remaining = source_size;
334                    iconv(cd, (ICONV_CONST char **) &data_remaining, &data_size_remaining,
335                          &unicode_buffer_remaining, &unicode_buffer_size_remaining);
336                    iconv_close(cd);
337    
338                    /* translate linebreaks */
339                    uint32 translated_data_size = unicode_buffer_size - unicode_buffer_size_remaining;
340                    uint8 *translated_data =
341                            utf16_lf2crlf((uint8 *) unicode_buffer, &translated_data_size);
342                    if (translated_data != NULL)
343                    {
344                            DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n",
345                                             translated_data_size));
346                            helper_cliprdr_send_response(translated_data, translated_data_size);
347                            xfree(translated_data); /* Not the same thing as XFree! */
348                    }
349    
350                    xfree(unicode_buffer);
351    
352                    return True;
353            }
354    #else
355            if (target == format_string_atom)
356            {
357                    uint8 *translated_data;
358                    uint32 length = source_size;
359    
360                    if (rdp_clipboard_request_format != RDP_CF_TEXT)
361                            return False;
362    
363                    DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
364                    translated_data = lf2crlf(source, &length);
365                    if (translated_data != NULL)
366                    {
367                            helper_cliprdr_send_response(translated_data, length);
368                            xfree(translated_data); /* Not the same thing as XFree! */
369                    }
370    
371                    return True;
372            }
373    #endif
374            else if (target == rdesktop_clipboard_formats_atom)
375            {
376                    helper_cliprdr_send_response(source, source_size + 1);
377    
378                    return True;
379            }
380            else
381            {
382                    return False;
383            }
384    }
385    
386    static void
387    xclip_clear_target_props()
388    {
389            XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
390            XDeleteProperty(g_display, g_wnd, rdesktop_primary_timestamp_target_atom);
391            XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_timestamp_target_atom);
392    }
393    
394  /* This function is called for SelectionNotify events.  /* This function is called for SelectionNotify events.
395     The SelectionNotify message is sent from the clipboard owner to the requestor     The SelectionNotify event is sent from the clipboard owner to the requestor
396     after his request was satisfied.     after his request was satisfied.
397     If this function is called, we're the requestor side. */     If this function is called, we're the requestor side. */
398  #ifndef MAKE_PROTO  #ifndef MAKE_PROTO
# Line 161  xclip_handle_SelectionNotify(XSelectionE Line 401  xclip_handle_SelectionNotify(XSelectionE
401  {  {
402          unsigned long nitems, bytes_left;          unsigned long nitems, bytes_left;
403          XWindowAttributes wa;          XWindowAttributes wa;
404          Atom type, best_target, text_target;          Atom type;
405          Atom *supported_targets;          Atom *supported_targets;
406          int res, i, format;          int res, i, format;
407          uint8 *data;          uint8 *data = NULL;
408    
409          if (event->property == None)          if (event->property == None)
410                  goto fail;                  goto fail;
# Line 174  xclip_handle_SelectionNotify(XSelectionE Line 414  xclip_handle_SelectionNotify(XSelectionE
414                           XGetAtomName(g_display, event->target),                           XGetAtomName(g_display, event->target),
415                           XGetAtomName(g_display, event->property)));                           XGetAtomName(g_display, event->property)));
416    
417          if (event->property == None)          if (event->target == timestamp_atom)
418                  goto fail;          {
419                    if (event->selection == primary_atom)
420                    {
421                            res = XGetWindowProperty(g_display, g_wnd,
422                                                     rdesktop_primary_timestamp_target_atom, 0,
423                                                     XMaxRequestSize(g_display), False, XA_INTEGER,
424                                                     &type, &format, &nitems, &bytes_left, &data);
425                    }
426                    else
427                    {
428                            res = XGetWindowProperty(g_display, g_wnd,
429                                                     rdesktop_clipboard_timestamp_target_atom, 0,
430                                                     XMaxRequestSize(g_display), False, XA_INTEGER,
431                                                     &type, &format, &nitems, &bytes_left, &data);
432                    }
433    
434    
435                    if ((res != Success) || (nitems != 1))
436                    {
437                            DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
438                            goto fail;
439                    }
440    
441                    if (event->selection == primary_atom)
442                    {
443                            primary_timestamp = *(Time *) data;
444                            if (primary_timestamp == 0)
445                                    primary_timestamp++;
446                            XDeleteProperty(g_display, g_wnd, rdesktop_primary_timestamp_target_atom);
447                            DEBUG_CLIPBOARD(("Got PRIMARY timestamp: %u\n",
448                                             (unsigned) primary_timestamp));
449                    }
450                    else
451                    {
452                            clipboard_timestamp = *(Time *) data;
453                            if (clipboard_timestamp == 0)
454                                    clipboard_timestamp++;
455                            XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_timestamp_target_atom);
456                            DEBUG_CLIPBOARD(("Got CLIPBOARD timestamp: %u\n",
457                                             (unsigned) clipboard_timestamp));
458                    }
459    
460                    XFree(data);
461    
462                    if (primary_timestamp && clipboard_timestamp)
463                    {
464                            if (primary_timestamp > clipboard_timestamp)
465                            {
466                                    DEBUG_CLIPBOARD(("PRIMARY is most recent selection.\n"));
467                                    XConvertSelection(g_display, primary_atom, targets_atom,
468                                                      rdesktop_clipboard_target_atom, g_wnd,
469                                                      event->time);
470                            }
471                            else
472                            {
473                                    DEBUG_CLIPBOARD(("CLIPBOARD is most recent selection.\n"));
474                                    XConvertSelection(g_display, clipboard_atom, targets_atom,
475                                                      rdesktop_clipboard_target_atom, g_wnd,
476                                                      event->time);
477                            }
478                    }
479    
480                    return;
481            }
482    
483          res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,          res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
484                                   0, XMaxRequestSize(g_display), False, AnyPropertyType,                                   0, XMaxRequestSize(g_display), False, AnyPropertyType,
485                                   &type, &format, &nitems, &bytes_left, &data);                                   &type, &format, &nitems, &bytes_left, &data);
486    
487            xclip_clear_target_props();
488    
489          if (res != Success)          if (res != Success)
490          {          {
491                  DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));                  DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
492                  goto fail;                  goto fail;
493          }          }
494    
   
495          if (type == incr_atom)          if (type == incr_atom)
496          {          {
497                  DEBUG_CLIPBOARD(("Received INCR.\n"));                  DEBUG_CLIPBOARD(("Received INCR.\n"));
# Line 197  xclip_handle_SelectionNotify(XSelectionE Line 501  xclip_handle_SelectionNotify(XSelectionE
501                  {                  {
502                          XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));                          XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));
503                  }                  }
                 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);  
504                  XFree(data);                  XFree(data);
505                    g_incr_target = event->target;
506                  g_waiting_for_INCR = 1;                  g_waiting_for_INCR = 1;
507                  return;                  return;
508          }          }
509    
         XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);  
   
510          /* Negotiate target format */          /* Negotiate target format */
511          if (event->target == targets_atom)          if (event->target == targets_atom)
512          {          {
513                  /* FIXME: We should choose format here based on what the server wanted */                  /* Determine the best of text targets that we have available:
514                  best_target = XA_STRING;                     Prefer UTF8_STRING > text/unicode (unspecified encoding) > STRING
515                       (ignore TEXT and COMPOUND_TEXT because we don't have code to handle them)
516                     */
517                    int text_target_satisfaction = 0;
518                    Atom best_text_target = 0;      /* measures how much we're satisfied with what we found */
519                  if (type != None)                  if (type != None)
520                  {                  {
521                          supported_targets = (Atom *) data;                          supported_targets = (Atom *) data;
                         text_target = XInternAtom(g_display, "TEXT", False);  
522                          for (i = 0; i < nitems; i++)                          for (i = 0; i < nitems; i++)
523                          {                          {
524                                  DEBUG_CLIPBOARD(("Target %d: %s\n", i,                                  DEBUG_CLIPBOARD(("Target %d: %s\n", i,
525                                                   XGetAtomName(g_display, supported_targets[i])));                                                   XGetAtomName(g_display, supported_targets[i])));
526                                  if (supported_targets[i] == text_target)                                  if (supported_targets[i] == format_string_atom)
527                                  {                                  {
528                                          DEBUG_CLIPBOARD(("Other party supports TEXT, choosing that as best_target\n"));                                          if (text_target_satisfaction < 1)
529                                          best_target = text_target;                                          {
530                                          break;                                                  DEBUG_CLIPBOARD(("Other party supports STRING, choosing that as best_target\n"));
531                                                    best_text_target = supported_targets[i];
532                                                    text_target_satisfaction = 1;
533                                            }
534                                  }                                  }
535    #ifdef USE_UNICODE_CLIPBOARD
536                                    else if (supported_targets[i] == format_unicode_atom)
537                                    {
538                                            if (text_target_satisfaction < 2)
539                                            {
540                                                    DEBUG_CLIPBOARD(("Other party supports text/unicode, choosing that as best_target\n"));
541                                                    best_text_target = supported_targets[i];
542                                                    text_target_satisfaction = 2;
543                                            }
544                                    }
545                                    else if (supported_targets[i] == format_utf8_string_atom)
546                                    {
547                                            if (text_target_satisfaction < 3)
548                                            {
549                                                    DEBUG_CLIPBOARD(("Other party supports UTF8_STRING, choosing that as best_target\n"));
550                                                    best_text_target = supported_targets[i];
551                                                    text_target_satisfaction = 3;
552                                            }
553                                    }
554    #endif
555                          }                          }
                         XFree(data);  
556                  }                  }
557    
558                  XConvertSelection(g_display, primary_atom, best_target,                  /* Kickstarting the next step in the process of satisfying RDP's
559                                    rdesktop_clipboard_target_atom, g_wnd, event->time);                     clipboard request -- specifically, requesting the actual clipboard data.
560                  return;                   */
561          }                  if (best_text_target != 0)
562                    {
563          /* Translate linebreaks, but only if not getting data from                          XConvertSelection(g_display, event->selection, best_text_target,
564             other rdesktop instance */                                            rdesktop_clipboard_target_atom, g_wnd, event->time);
565          if (event->target != rdesktop_clipboard_formats_atom)                          return;
566          {                  }
567                  uint8 *translated_data;                  else
568                  uint32 length = nitems;                  {
569                            DEBUG_CLIPBOARD(("Unable to find a textual target to satisfy RDP clipboard text request\n"));
570                  DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));                          goto fail;
571                  translated_data = lf2crlf(data, &length);                  }
                 cliprdr_send_data(translated_data, length + 1);  
                 xfree(translated_data); /* Not the same thing as XFree! */  
572          }          }
573          else          else
574          {          {
575                  cliprdr_send_data(data, nitems + 1);                  if (!xclip_send_data_with_convert(data, nitems, event->target))
576                    {
577                            goto fail;
578                    }
579          }          }
580    
581          XFree(data);          XFree(data);
582    
         if (!rdesktop_is_selection_owner)  
                 cliprdr_send_simple_native_format_announce(CF_TEXT);  
583          return;          return;
584    
585        fail:        fail:
586          XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);          xclip_clear_target_props();
587          XFree(data);          if (data)
588          cliprdr_send_data(NULL, 0);                  XFree(data);
589            helper_cliprdr_send_empty_response();
590  }  }
591    
592  /* This function is called for SelectionRequest events.  /* This function is called for SelectionRequest events.
593     The SelectionRequest message is sent from the requestor to the clipboard owner     The SelectionRequest event is sent from the requestor to the clipboard owner
594     to request clipboard data.     to request clipboard data.
595   */   */
596  void  void
# Line 281  xclip_handle_SelectionRequest(XSelection Line 609  xclip_handle_SelectionRequest(XSelection
609    
610          if (event->target == targets_atom)          if (event->target == targets_atom)
611          {          {
612                  xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, NUM_TARGETS);                  xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, num_targets);
613                  return;                  return;
614          }          }
615          else if (event->target == timestamp_atom)          else if (event->target == timestamp_atom)
# Line 289  xclip_handle_SelectionRequest(XSelection Line 617  xclip_handle_SelectionRequest(XSelection
617                  xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);                  xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);
618                  return;                  return;
619          }          }
         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? */  
         }  
620          else          else
621          {          {
622                  format = CF_TEXT;                  /* All the following targets require an async operation with the RDP server
623          }                     and currently we don't do X clipboard request queueing so we can only
624                       handle one such request at a time. */
625                    if (has_selection_request)
626                    {
627                            DEBUG_CLIPBOARD(("Error: Another clipboard request was already sent to the RDP server and not yet responded. Refusing this request.\n"));
628                            xclip_refuse_selection(event);
629                            return;
630                    }
631                    if (event->target == rdesktop_clipboard_formats_atom)
632                    {
633                            /* Before the requestor makes a request for the _RDESKTOP_CLIPBOARD_FORMATS target,
634                               he should declare requestor[_RDESKTOP_CLIPBOARD_TARGET] = CF_SOMETHING.
635                               Otherwise, we default to RDP_CF_TEXT.
636                             */
637                            res = XGetWindowProperty(g_display, event->requestor,
638                                                     rdesktop_clipboard_target_atom, 0, 1, True,
639                                                     XA_INTEGER, &type, &format, &nitems, &bytes_left,
640                                                     &prop_return);
641                            wanted_format = (uint32 *) prop_return;
642                            format = (res == Success) ? *wanted_format : RDP_CF_TEXT;
643                            XFree(prop_return);
644                    }
645                    else if (event->target == format_string_atom || event->target == XA_STRING)
646                    {
647                            /* STRING and XA_STRING are defined to be ISO8859-1 */
648                            format = CF_TEXT;
649                    }
650                    else if (event->target == format_utf8_string_atom)
651                    {
652    #ifdef USE_UNICODE_CLIPBOARD
653                            format = CF_UNICODETEXT;
654    #else
655                            DEBUG_CLIPBOARD(("Requested target unavailable due to lack of Unicode support. (It was not in TARGETS, so why did you ask for it?!)\n"));
656                            xclip_refuse_selection(event);
657                            return;
658    #endif
659                    }
660                    else if (event->target == format_unicode_atom)
661                    {
662                            /* Assuming text/unicode to be UTF-16 */
663                            format = CF_UNICODETEXT;
664                    }
665                    else
666                    {
667                            DEBUG_CLIPBOARD(("Requested target unavailable. (It was not in TARGETS, so why did you ask for it?!)\n"));
668                            xclip_refuse_selection(event);
669                            return;
670                    }
671    
672          cliprdr_send_data_request(format);                  cliprdr_send_data_request(format);
673          selection_request = *event;                  selection_request = *event;
674          /* wait for data */                  has_selection_request = True;
675                    return;         /* wait for data */
676            }
677  }  }
678    
679  /* 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
680     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
681     roundtrip). This event symbolizes this rdesktop lost onwership of the clipboard     roundtrip).
682    
683       This event (SelectionClear) symbolizes this rdesktop lost onwership of the clipboard
684     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
685     client offers and announce that to RDP. */     client offers and announce that to RDP. */
686  void  void
# Line 319  xclip_handle_SelectionClear(void) Line 689  xclip_handle_SelectionClear(void)
689          DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));          DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
690          have_primary = 0;          have_primary = 0;
691          XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);          XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);
692          cliprdr_send_simple_native_format_announce(CF_TEXT);          /* FIXME:
693               Without XFIXES, we cannot reliably know the formats offered by the
694               new owner of the X11 clipboard, so we just lie about him
695               offering RDP_CF_TEXT. */
696            cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
697  }  }
698    
699  /* 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 714  xclip_handle_PropertyNotify(XPropertyEve
714    
715                  while (bytes_left > 0)                  while (bytes_left > 0)
716                  {                  {
717                            /* Unlike the specification, we don't set the 'delete' arugment to True
718                               since we slurp the INCR's chunks in even-smaller chunks of 4096 bytes. */
719                          if ((XGetWindowProperty                          if ((XGetWindowProperty
720                               (g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,                               (g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,
721                                False, AnyPropertyType, &type, &format, &nitems, &bytes_left,                                False, AnyPropertyType, &type, &format, &nitems, &bytes_left,
# Line 351  xclip_handle_PropertyNotify(XPropertyEve Line 727  xclip_handle_PropertyNotify(XPropertyEve
727    
728                          if (nitems == 0)                          if (nitems == 0)
729                          {                          {
730                                    /* INCR transfer finished */
731                                  XGetWindowAttributes(g_display, g_wnd, &wa);                                  XGetWindowAttributes(g_display, g_wnd, &wa);
732                                  XSelectInput(g_display, g_wnd,                                  XSelectInput(g_display, g_wnd,
733                                               (wa.your_event_mask ^ PropertyChangeMask));                                               (wa.your_event_mask ^ PropertyChangeMask));
# Line 359  xclip_handle_PropertyNotify(XPropertyEve Line 736  xclip_handle_PropertyNotify(XPropertyEve
736    
737                                  if (g_clip_buflen > 0)                                  if (g_clip_buflen > 0)
738                                  {                                  {
739                                          cliprdr_send_data(g_clip_buffer, g_clip_buflen + 1);                                          if (!xclip_send_data_with_convert
740                                                (g_clip_buffer, g_clip_buflen, g_incr_target))
741                                          if (!rdesktop_is_selection_owner)                                          {
742                                                  cliprdr_send_simple_native_format_announce(CF_TEXT);                                                  helper_cliprdr_send_empty_response();
743                                            }
744                                          xfree(g_clip_buffer);                                          xfree(g_clip_buffer);
745                                          g_clip_buffer = 0;                                          g_clip_buffer = NULL;
746                                          g_clip_buflen = 0;                                          g_clip_buflen = 0;
747                                  }                                  }
748                          }                          }
749                          else                          else
750                          {                          {
751                                  uint8 *translated_data;                                  /* Another chunk in the INCR transfer */
752                                  uint32 length = nitems;                                  offset += (nitems / 4); /* offset at which to begin the next slurp */
753                                  uint8 *tmp;                                  g_clip_buffer = xrealloc(g_clip_buffer, g_clip_buflen + nitems);
754                                    memcpy(g_clip_buffer + g_clip_buflen, data, nitems);
755                                  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;  
756    
757                                  XFree(data);                                  XFree(data);
758                          }                          }
759                  }                  }
760                  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 */  
761                  return;                  return;
762            }
763    
764          if (event->state == PropertyNewValue)          if ((event->atom == rdesktop_clipboard_formats_atom) &&
765                (event->window == DefaultRootWindow(g_display)) &&
766                !have_primary /* not interested in our own events */ )
767          {          {
768                  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))  
769                  {                  {
770                          cliprdr_send_native_format_announce(data, nitems);                          DEBUG_CLIPBOARD(("xclip_handle_PropertyNotify: getting fellow rdesktop formats\n"));
771                          rdesktop_is_selection_owner = 1;  
772                          return;                          res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),
773                                                     rdesktop_clipboard_formats_atom, 0,
774                                                     XMaxRequestSize(g_display), False, XA_STRING,
775                                                     &type, &format, &nitems, &bytes_left, &data);
776    
777                            if ((res == Success) && (nitems > 0))
778                            {
779                                    cliprdr_send_native_format_announce(data, nitems);
780                                    rdesktop_is_selection_owner = 1;
781                                    return;
782                            }
783                  }                  }
         }  
784    
785          /* PropertyDelete, or XGetWindowProperty failed */                  /* For some reason, we couldn't announce the native formats */
786          cliprdr_send_simple_native_format_announce(CF_TEXT);                  cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
787          rdesktop_is_selection_owner = 0;                  rdesktop_is_selection_owner = 0;
788            }
789  }  }
790  #endif  #endif
791    
# Line 449  ui_clip_format_announce(uint8 * data, ui Line 815  ui_clip_format_announce(uint8 * data, ui
815                  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");                  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
816  }  }
817    
818    /* Called when the RDP server responds with clipboard data (after we've requested it). */
819  void  void
820  ui_clip_handle_data(uint8 * data, uint32 length)  ui_clip_handle_data(uint8 * data, uint32 length)
821  {  {
822          if (selection_request.target != rdesktop_clipboard_formats_atom)          BOOL free_data = False;
823    
824            if (selection_request.target == format_string_atom || selection_request.target == XA_STRING)
825          {          {
826                    /* We're expecting a CF_TEXT response */
827                  uint8 *firstnull;                  uint8 *firstnull;
828    
829                  /* translate linebreaks */                  /* translate linebreaks */
# Line 467  ui_clip_handle_data(uint8 * data, uint32 Line 836  ui_clip_handle_data(uint8 * data, uint32
836                          length = firstnull - data + 1;                          length = firstnull - data + 1;
837                  }                  }
838          }          }
839    #ifdef USE_UNICODE_CLIPBOARD
840            else if (selection_request.target == format_utf8_string_atom)
841            {
842                    /* We're expecting a CF_UNICODETEXT response */
843                    iconv_t cd = iconv_open("UTF-8", WINDOWS_CODEPAGE);
844                    if (cd != (iconv_t) - 1)
845                    {
846                            size_t utf8_length = length * 2;
847                            char *utf8_data = malloc(utf8_length);
848                            size_t utf8_length_remaining = utf8_length;
849                            char *utf8_data_remaining = utf8_data;
850                            char *data_remaining = (char *) data;
851                            size_t length_remaining = (size_t) length;
852                            if (utf8_data == NULL)
853                            {
854                                    iconv_close(cd);
855                                    return;
856                            }
857                            iconv(cd, (ICONV_CONST char **) &data_remaining, &length_remaining,
858                                  &utf8_data_remaining, &utf8_length_remaining);
859                            iconv_close(cd);
860                            free_data = True;
861                            data = (uint8 *) utf8_data;
862                            length = utf8_length - utf8_length_remaining;
863                    }
864            }
865            else if (selection_request.target == format_unicode_atom)
866            {
867                    /* We're expecting a CF_UNICODETEXT response, so what we're
868                       receiving matches our requirements and there's no need
869                       for further conversions. */
870            }
871    #endif
872            else if (selection_request.target == rdesktop_clipboard_formats_atom)
873            {
874                    /* Pass as-is */
875            }
876            else
877            {
878                    xclip_refuse_selection(&selection_request);
879                    has_selection_request = False;
880                    return;
881            }
882    
883          xclip_provide_selection(&selection_request, XA_STRING, 8, data, length - 1);          xclip_provide_selection(&selection_request, selection_request.target, 8, data, length - 1);
884            has_selection_request = False;
885    
886            if (free_data)
887                    free(data);
888  }  }
889    
890  void  void
891  ui_clip_request_data(uint32 format)  ui_clip_request_data(uint32 format)
892  {  {
893          Window selectionowner;          Window primary_owner, clipboard_owner;
894    
895          DEBUG_CLIPBOARD(("Request from server for format %d\n", format));          DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
896            rdp_clipboard_request_format = format;
897    
898            xclip_clear_target_props();
899    
900          if (rdesktop_is_selection_owner)          if (rdesktop_is_selection_owner)
901          {          {
# Line 488  ui_clip_request_data(uint32 format) Line 907  ui_clip_request_data(uint32 format)
907                  return;                  return;
908          }          }
909    
910          selectionowner = XGetSelectionOwner(g_display, primary_atom);          if (auto_mode)
911          if (selectionowner != None)                  primary_owner = XGetSelectionOwner(g_display, primary_atom);
912            else
913                    primary_owner = None;
914    
915            clipboard_owner = XGetSelectionOwner(g_display, clipboard_atom);
916    
917            /* Both available */
918            if ((primary_owner != None) && (clipboard_owner != None))
919            {
920                    primary_timestamp = 0;
921                    clipboard_timestamp = 0;
922                    XConvertSelection(g_display, primary_atom, timestamp_atom,
923                                      rdesktop_primary_timestamp_target_atom, g_wnd, CurrentTime);
924                    XConvertSelection(g_display, clipboard_atom, timestamp_atom,
925                                      rdesktop_clipboard_timestamp_target_atom, g_wnd, CurrentTime);
926                    return;
927            }
928    
929            /* Just PRIMARY */
930            if (primary_owner != None)
931          {          {
932                  XConvertSelection(g_display, primary_atom, targets_atom,                  XConvertSelection(g_display, primary_atom, targets_atom,
933                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
934                  return;                  return;
935          }          }
936    
937          /* No PRIMARY, try CLIPBOARD */          /* Just CLIPBOARD */
938          selectionowner = XGetSelectionOwner(g_display, clipboard_atom);          if (clipboard_owner != None)
         if (selectionowner != None)  
939          {          {
940                  XConvertSelection(g_display, clipboard_atom, targets_atom,                  XConvertSelection(g_display, clipboard_atom, targets_atom,
941                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
# Line 506  ui_clip_request_data(uint32 format) Line 943  ui_clip_request_data(uint32 format)
943          }          }
944    
945          /* No data available */          /* No data available */
946          cliprdr_send_data(NULL, 0);          helper_cliprdr_send_empty_response();
947  }  }
948    
949  void  void
950  ui_clip_sync(void)  ui_clip_sync(void)
951  {  {
952          cliprdr_send_simple_native_format_announce(CF_TEXT);          cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
953  }  }
954    
955    void
956    ui_clip_set_mode(const char *optarg)
957    {
958            g_rdpclip = True;
959    
960            if (str_startswith(optarg, "auto") || str_startswith(optarg, "on")
961                || str_startswith(optarg, "PRIMARYCLIPBOARD"))
962                    auto_mode = True;
963            else if (str_startswith(optarg, "CLIPBOARD"))
964                    auto_mode = False;
965            else
966            {
967                    warning("Invalid clipboard mode '%s'.\n", optarg);
968                    g_rdpclip = False;
969            }
970    }
971    
972  void  void
973  xclip_init(void)  xclip_init(void)
974  {  {
975            if (!g_rdpclip)
976                    return;
977    
978          if (!cliprdr_init())          if (!cliprdr_init())
979                  return;                  return;
980    
# Line 528  xclip_init(void) Line 984  xclip_init(void)
984          timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);          timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);
985          rdesktop_clipboard_target_atom =          rdesktop_clipboard_target_atom =
986                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
987            rdesktop_primary_timestamp_target_atom =
988                    XInternAtom(g_display, "_RDESKTOP_PRIMARY_TIMESTAMP_TARGET", False);
989            rdesktop_clipboard_timestamp_target_atom =
990                    XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TIMESTAMP_TARGET", False);
991          incr_atom = XInternAtom(g_display, "INCR", False);          incr_atom = XInternAtom(g_display, "INCR", False);
992          targets[0] = targets_atom;          format_string_atom = XInternAtom(g_display, "STRING", False);
993          targets[1] = XInternAtom(g_display, "TEXT", False);          format_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False);
994          targets[2] = XInternAtom(g_display, "STRING", False);          format_unicode_atom = XInternAtom(g_display, "text/unicode", False);
995          targets[3] = XInternAtom(g_display, "text/unicode", False);          num_targets = 0;
996          targets[4] = XInternAtom(g_display, "TIMESTAMP", False);          targets[num_targets++] = targets_atom;
997          targets[5] = XA_STRING;          targets[num_targets++] = timestamp_atom;
998            targets[num_targets++] = rdesktop_clipboard_formats_atom;
999            targets[num_targets++] = format_string_atom;
1000    #ifdef USE_UNICODE_CLIPBOARD
1001            targets[num_targets++] = format_utf8_string_atom;
1002    #endif
1003            targets[num_targets++] = format_unicode_atom;
1004            targets[num_targets++] = XA_STRING;
1005    
1006          /* 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.
1007             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.1207

  ViewVC Help
Powered by ViewVC 1.1.26