/[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 462 by astrand, Tue Sep 2 10:42:16 2003 UTC revision 1038 by astrand, Thu Jan 5 11:56:57 2006 UTC
# Line 23  Line 23 
23  #include <X11/Xatom.h>  #include <X11/Xatom.h>
24  #include "rdesktop.h"  #include "rdesktop.h"
25    
26  #define NUM_TARGETS 6  /*
27      To gain better understanding of this code, one could be assisted by the following documents:
28      - Inter-Client Communication Conventions Manual (ICCCM)
29        HTML: http://tronche.com/gui/x/icccm/
30        PDF:  http://ftp.xfree86.org/pub/XFree86/4.5.0/doc/PDF/icccm.pdf
31      - MSDN: Clipboard Formats
32        http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/dataexchange/clipboard/clipboardformats.asp
33    */
34    
35    #ifdef HAVE_ICONV
36    #ifdef HAVE_LANGINFO_H
37    #ifdef HAVE_ICONV_H
38    #include <langinfo.h>
39    #include <iconv.h>
40    #define USE_UNICODE_CLIPBOARD
41    #endif
42    #endif
43    #endif
44    
45    #ifdef USE_UNICODE_CLIPBOARD
46    #define RDP_CF_TEXT CF_UNICODETEXT
47    #else
48    #define RDP_CF_TEXT CF_TEXT
49    #endif
50    
51    #define MAX_TARGETS 7
52    
53  extern Display *g_display;  extern Display *g_display;
54  extern Window g_wnd;  extern Window g_wnd;
55  extern Time g_last_gesturetime;  extern Time g_last_gesturetime;
56    
57  static Atom clipboard_atom, primary_atom, targets_atom, timestamp_atom;  /* Atoms of the two X selections we're dealing with: CLIPBOARD (explicit-copy) and PRIMARY (selection-copy) */
58  static Atom rdesktop_clipboard_target_atom, rdesktop_clipboard_formats_atom, incr_atom;  static Atom clipboard_atom, primary_atom;
59    /* Atom of the TARGETS clipboard target */
60    static Atom targets_atom;
61    /* Atom of the TIMESTAMP clipboard target */
62    static Atom timestamp_atom;
63    /* Atom _RDESKTOP_CLIPBOARD_TARGET which has multiple uses:
64       - The 'property' argument in XConvertSelection calls: This is the property of our
65         window into which XConvertSelection will store the received clipboard data.
66       - In a clipboard request of target _RDESKTOP_CLIPBOARD_FORMATS, an XA_INTEGER-typed
67         property carrying the Windows native (CF_...) format desired by the requestor.
68         Requestor set this property (e.g. requestor_wnd[_RDESKTOP_CLIPBOARD_TARGET] = CF_TEXT)
69         before requesting clipboard data from a fellow rdesktop using
70         the _RDESKTOP_CLIPBOARD_FORMATS target. */
71    static Atom rdesktop_clipboard_target_atom;
72    /* Atom _RDESKTOP_CLIPBOARD_FORMATS which has multiple uses:
73       - The clipboard target (X jargon for "clipboard format") for rdesktop-to-rdesktop interchange
74         of Windows native clipboard data.
75         This target cannot be used standalone; the requestor must keep the
76         _RDESKTOP_CLIPBOARD_TARGET property on his window denoting
77         the Windows native clipboard format being requested.
78       - The root window property set by rdesktop when it owns the clipboard,
79         denoting all Windows native clipboard formats it offers via
80         requests of the _RDESKTOP_CLIPBOARD_FORMATS target. */
81    static Atom rdesktop_clipboard_formats_atom;
82    static Atom format_string_atom, format_utf8_string_atom, format_unicode_atom;
83    /* Atom of the INCR clipboard type (see ICCCM on "INCR Properties") */
84    static Atom incr_atom;
85    /* Stores the last "selection request" (= another X client requesting clipboard data from us).
86       To satisfy such a request, we request the clipboard data from the RDP server.
87       When we receive the response from the RDP server (asynchronously), this variable gives us
88       the context to proceed. */
89  static XSelectionRequestEvent selection_request;  static XSelectionRequestEvent selection_request;
90  static Atom targets[NUM_TARGETS];  /* Denotes we have a pending selection request. */
91    static Bool has_selection_request;
92    /* Stores the clipboard format (CF_TEXT, CF_UNICODETEXT etc.) requested in the last
93       CLIPDR_DATA_REQUEST (= the RDP server requesting clipboard data from us).
94       When we receive this data from whatever X client offering it, this variable gives us
95       the context to proceed.
96     */
97    static int rdp_clipboard_request_format;
98    /* Array of offered clipboard targets that will be sent to fellow X clients upon a TARGETS request. */
99    static Atom targets[MAX_TARGETS];
100    static int num_targets;
101    /* Denotes that this client currently holds the PRIMARY selection. */
102  static int have_primary = 0;  static int have_primary = 0;
103    /* Denotes that an rdesktop (not this rdesktop) is owning the selection,
104       allowing us to interchange Windows native clipboard data directly. */
105  static int rdesktop_is_selection_owner = 0;  static int rdesktop_is_selection_owner = 0;
106    
107    /* Denotes that an INCR ("chunked") transfer is in progress. */
108    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. */
112    static uint8 *g_clip_buffer = 0;
113    /* Denotes the size of g_clip_buffer. */
114    static uint32 g_clip_buflen = 0;
115    
116    /* Translate LF to CR-LF. To do this, we must allocate more memory.
117       The returned string is null-terminated, as required by CF_TEXT.
118       Does not stop on embedded nulls.
119       The length is updated. */
120    static void
121    crlf2lf(uint8 * data, uint32 * length)
122    {
123            uint8 *dst, *src;
124            src = dst = data;
125            while (src < data + *length)
126            {
127                    if (*src != '\x0d')
128                            *dst++ = *src;
129                    src++;
130            }
131            *length = dst - data;
132    }
133    
134    #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. */
172    static uint8 *
173    lf2crlf(uint8 * data, uint32 * length)
174    {
175            uint8 *result, *p, *o;
176    
177            /* Worst case: Every char is LF */
178            result = xmalloc(*length * 2);
179    
180            p = data;
181            o = result;
182    
183            while (p < data + *length)
184            {
185                    if (*p == '\x0a')
186                            *o++ = '\x0d';
187                    *o++ = *p++;
188            }
189            *length = o - result;
190    
191            /* Convenience */
192            *o++ = '\0';
193    
194            return result;
195    }
196    #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,
200                          uint32 length)                          uint32 length)
# Line 56  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, &data_remaining, &data_size_remaining, &unicode_buffer_remaining,
324                          &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                            cliprdr_send_data(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                            cliprdr_send_data(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.
376       The SelectionNotify event is sent from the clipboard owner to the requestor
377       after his request was satisfied.
378       If this function is called, we're the requestor side. */
379    #ifndef MAKE_PROTO
380  void  void
381  xclip_handle_SelectionNotify(XSelectionEvent * event)  xclip_handle_SelectionNotify(XSelectionEvent * event)
382  {  {
383          unsigned long nitems, bytes_left;          unsigned long nitems, bytes_left;
384          Atom type, best_target, text_target;          XWindowAttributes wa;
385            Atom type;
386          Atom *supported_targets;          Atom *supported_targets;
387          int res, i, format;          int res, i, format;
388          uint8 *data;          uint8 *data;
# Line 77  xclip_handle_SelectionNotify(XSelectionE Line 399  xclip_handle_SelectionNotify(XSelectionE
399                  goto fail;                  goto fail;
400    
401          res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,          res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
402                                   0, XMaxRequestSize(g_display), True, AnyPropertyType,                                   0, XMaxRequestSize(g_display), False, AnyPropertyType,
403                                   &type, &format, &nitems, &bytes_left, &data);                                   &type, &format, &nitems, &bytes_left, &data);
404    
405          if (res != Success)          if (res != Success)
# Line 86  xclip_handle_SelectionNotify(XSelectionE Line 408  xclip_handle_SelectionNotify(XSelectionE
408                  goto fail;                  goto fail;
409          }          }
410    
411            if (type == incr_atom)
412            {
413                    DEBUG_CLIPBOARD(("Received INCR.\n"));
414    
415                    XGetWindowAttributes(g_display, g_wnd, &wa);
416                    if ((wa.your_event_mask | PropertyChangeMask) != wa.your_event_mask)
417                    {
418                            XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));
419                    }
420                    XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
421                    XFree(data);
422                    g_incr_target = event->target;
423                    g_waiting_for_INCR = 1;
424                    return;
425            }
426    
427            XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
428    
429            /* 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                                            if (text_target_satisfaction < 1)
448                                            {
449                                                    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                                          DEBUG_CLIPBOARD(("Other party supports TEXT, choosing that as best_target\n"));                                          if (text_target_satisfaction < 3)
467                                          best_target = text_target;                                          {
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                            XConvertSelection(g_display, clipboard_atom, best_text_target,
483                                              rdesktop_clipboard_target_atom, g_wnd, event->time);
484                            return;
485                    }
486                    else
487                    {
488                            DEBUG_CLIPBOARD(("Unable to find a textual target to satisfy RDP clipboard text request\n"));
489                            goto fail;
490                    }
491          }          }
492            else
         if (type == incr_atom)  
493          {          {
494                  warning("We don't support INCR transfers at this time. Try cutting less data.\n");                  if (!xclip_send_data_with_convert(data, nitems, event->target))
495                  goto fail;                  {
496                            goto fail;
497                    }
498          }          }
499    
         cliprdr_send_data(data, nitems + 1);  
500          XFree(data);          XFree(data);
501    
         if (!rdesktop_is_selection_owner)  
                 cliprdr_send_text_format_announce();  
502          return;          return;
503    
504        fail:        fail:
505          cliprdr_send_data(NULL, 0);          XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
506            XFree(data);
507            helper_cliprdr_send_empty_response();
508  }  }
509    
510    /* This function is called for SelectionRequest events.
511       The SelectionRequest event is sent from the requestor to the clipboard owner
512       to request clipboard data.
513     */
514  void  void
515  xclip_handle_SelectionRequest(XSelectionRequestEvent * event)  xclip_handle_SelectionRequest(XSelectionRequestEvent * event)
516  {  {
517          unsigned long nitems, bytes_left;          unsigned long nitems, bytes_left;
518            unsigned char *prop_return;
519          uint32 *wanted_format;          uint32 *wanted_format;
520          int format, res;          int format, res;
521          Atom type;          Atom type;
# Line 144  xclip_handle_SelectionRequest(XSelection Line 527  xclip_handle_SelectionRequest(XSelection
527    
528          if (event->target == targets_atom)          if (event->target == targets_atom)
529          {          {
530                  xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, NUM_TARGETS);                  xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, num_targets);
531                  return;                  return;
532          }          }
533          else if (event->target == timestamp_atom)          else if (event->target == timestamp_atom)
# Line 152  xclip_handle_SelectionRequest(XSelection Line 535  xclip_handle_SelectionRequest(XSelection
535                  xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);                  xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);
536                  return;                  return;
537          }          }
         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,  
                                          (unsigned char **) &wanted_format);  
                 format = (res == Success) ? *wanted_format : CF_TEXT;  
         }  
538          else          else
539          {          {
540                  format = CF_TEXT;                  /* All the following targets require an async operation with the RDP server
541          }                     and currently we don't do X clipboard request queueing so we can only
542                       handle one such request at a time. */
543                    if (has_selection_request)
544                    {
545                            DEBUG_CLIPBOARD(("Error: Another clipboard request was already sent to the RDP server and not yet responded. Refusing this request.\n"));
546                            xclip_refuse_selection(event);
547                            return;
548                    }
549                    if (event->target == rdesktop_clipboard_formats_atom)
550                    {
551                            /* Before the requestor makes a request for the _RDESKTOP_CLIPBOARD_FORMATS target,
552                               he should declare requestor[_RDESKTOP_CLIPBOARD_TARGET] = CF_SOMETHING.
553                               Otherwise, we default to RDP_CF_TEXT.
554                             */
555                            res = XGetWindowProperty(g_display, event->requestor,
556                                                     rdesktop_clipboard_target_atom, 0, 1, True,
557                                                     XA_INTEGER, &type, &format, &nitems, &bytes_left,
558                                                     &prop_return);
559                            wanted_format = (uint32 *) prop_return;
560                            format = (res == Success) ? *wanted_format : RDP_CF_TEXT;
561                            XFree(prop_return);
562                    }
563                    else if (event->target == format_string_atom || event->target == XA_STRING)
564                    {
565                            /* STRING and XA_STRING are defined to be ISO8859-1 */
566                            format = CF_TEXT;
567                    }
568                    else if (event->target == format_utf8_string_atom)
569                    {
570    #ifdef USE_UNICODE_CLIPBOARD
571                            format = CF_UNICODETEXT;
572    #else
573                            DEBUG_CLIPBOARD(("Requested target unavailable due to lack of Unicode support. (It was not in TARGETS, so why did you ask for it?!)\n"));
574                            xclip_refuse_selection(event);
575                            return;
576    #endif
577                    }
578                    else if (event->target == format_unicode_atom)
579                    {
580                            /* Assuming text/unicode to be UTF-16 */
581                            format = CF_UNICODETEXT;
582                    }
583                    else
584                    {
585                            DEBUG_CLIPBOARD(("Requested target unavailable. (It was not in TARGETS, so why did you ask for it?!)\n"));
586                            xclip_refuse_selection(event);
587                            return;
588                    }
589    
590          cliprdr_send_data_request(format);                  cliprdr_send_data_request(format);
591          selection_request = *event;                  selection_request = *event;
592          /* wait for data */                  has_selection_request = True;
593                    return;         /* wait for data */
594            }
595  }  }
596    
597    /* While this rdesktop holds ownership over the clipboard, it means the clipboard data
598       is offered by the RDP server (and when it is pasted inside RDP, there's no network
599       roundtrip).
600    
601       This event (SelectionClear) symbolizes this rdesktop lost onwership of the clipboard
602       to some other X client. We should find out what clipboard formats this other
603       client offers and announce that to RDP. */
604  void  void
605  xclip_handle_SelectionClear(void)  xclip_handle_SelectionClear(void)
606  {  {
607          DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));          DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
608          have_primary = 0;          have_primary = 0;
609          XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);          XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);
610          cliprdr_send_text_format_announce();          /* FIXME:
611               Without XFIXES, we cannot reliably know the formats offered by the
612               new owner of the X11 clipboard, so we just lie about him
613               offering RDP_CF_TEXT. */
614            cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
615  }  }
616    
617    /* Called when any property changes in our window or the root window. */
618  void  void
619  xclip_handle_PropertyNotify(XPropertyEvent * event)  xclip_handle_PropertyNotify(XPropertyEvent * event)
620  {  {
621          unsigned long nitems, bytes_left;          unsigned long nitems;
622            unsigned long offset = 0;
623            unsigned long bytes_left = 1;
624          int format, res;          int format, res;
625            XWindowAttributes wa;
626          uint8 *data;          uint8 *data;
627          Atom type;          Atom type;
628    
629          if (event->atom != rdesktop_clipboard_formats_atom)          if (event->state == PropertyNewValue && g_waiting_for_INCR)
630                  return;          {
631                    DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: g_waiting_for_INCR != 0\n"));
632    
633          if (have_primary)       /* from us */                  while (bytes_left > 0)
634                    {
635                            /* Unlike the specification, we don't set the 'delete' arugment to True
636                               since we slurp the INCR's chunks in even-smaller chunks of 4096 bytes. */
637                            if ((XGetWindowProperty
638                                 (g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,
639                                  False, AnyPropertyType, &type, &format, &nitems, &bytes_left,
640                                  &data) != Success))
641                            {
642                                    XFree(data);
643                                    return;
644                            }
645    
646                            if (nitems == 0)
647                            {
648                                    /* INCR transfer finished */
649                                    XGetWindowAttributes(g_display, g_wnd, &wa);
650                                    XSelectInput(g_display, g_wnd,
651                                                 (wa.your_event_mask ^ PropertyChangeMask));
652                                    XFree(data);
653                                    g_waiting_for_INCR = 0;
654    
655                                    if (g_clip_buflen > 0)
656                                    {
657                                            if (!xclip_send_data_with_convert
658                                                (g_clip_buffer, g_clip_buflen, g_incr_target))
659                                            {
660                                                    helper_cliprdr_send_empty_response();
661                                            }
662                                            xfree(g_clip_buffer);
663                                            g_clip_buffer = NULL;
664                                            g_clip_buflen = 0;
665                                    }
666                            }
667                            else
668                            {
669                                    /* Another chunk in the INCR transfer */
670                                    offset += (nitems / 4); /* offset at which to begin the next slurp */
671                                    g_clip_buffer = xrealloc(g_clip_buffer, g_clip_buflen + nitems);
672                                    memcpy(g_clip_buffer + g_clip_buflen, data, nitems);
673                                    g_clip_buflen += nitems;
674    
675                                    XFree(data);
676                            }
677                    }
678                    XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
679                  return;                  return;
680            }
681    
682          if (event->state == PropertyNewValue)          if ((event->atom == rdesktop_clipboard_formats_atom) &&
683                (event->window == DefaultRootWindow(g_display)) &&
684                !have_primary /* not interested in our own events */ )
685          {          {
686                  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))  
687                  {                  {
688                          cliprdr_send_native_format_announce(data, nitems);                          DEBUG_CLIPBOARD(("xclip_handle_PropertyNotify: getting fellow rdesktop formats\n"));
689                          rdesktop_is_selection_owner = 1;  
690                          return;                          res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),
691                                                     rdesktop_clipboard_formats_atom, 0,
692                                                     XMaxRequestSize(g_display), False, XA_STRING,
693                                                     &type, &format, &nitems, &bytes_left, &data);
694    
695                            if ((res == Success) && (nitems > 0))
696                            {
697                                    cliprdr_send_native_format_announce(data, nitems);
698                                    rdesktop_is_selection_owner = 1;
699                                    return;
700                            }
701                  }                  }
         }  
702    
703          /* PropertyDelete, or XGetWindowProperty failed */                  /* For some reason, we couldn't announce the native formats */
704          cliprdr_send_text_format_announce();                  cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
705          rdesktop_is_selection_owner = 0;                  rdesktop_is_selection_owner = 0;
706            }
707  }  }
708    #endif
709    
710    
711    /* Called when the RDP server announces new clipboard data formats.
712       In response, we:
713       - take ownership over the clipboard
714       - declare those formats in their Windows native form
715         to other rdesktop instances on this X server */
716  void  void
717  ui_clip_format_announce(char *data, uint32 length)  ui_clip_format_announce(uint8 * data, uint32 length)
718  {  {
719          XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime);          XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime);
720          if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)          if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
# Line 234  ui_clip_format_announce(char *data, uint Line 733  ui_clip_format_announce(char *data, uint
733                  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");                  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
734  }  }
735    
736    /* Called when the RDP server responds with clipboard data (after we've requested it). */
737  void  void
738  ui_clip_handle_data(char *data, uint32 length)  ui_clip_handle_data(uint8 * data, uint32 length)
739  {  {
740          xclip_provide_selection(&selection_request, XA_STRING, 8, data, length - 1);          BOOL free_data = False;
741    
742            if (selection_request.target == format_string_atom || selection_request.target == XA_STRING)
743            {
744                    /* We're expecting a CF_TEXT response */
745                    uint8 *firstnull;
746    
747                    /* translate linebreaks */
748                    crlf2lf(data, &length);
749    
750                    /* Only send data up to null byte, if any */
751                    firstnull = (uint8 *) strchr((char *) data, '\0');
752                    if (firstnull)
753                    {
754                            length = firstnull - data + 1;
755                    }
756            }
757    #ifdef USE_UNICODE_CLIPBOARD
758            else if (selection_request.target == format_utf8_string_atom)
759            {
760                    /* We're expecting a CF_UNICODETEXT response */
761                    iconv_t cd = iconv_open("UTF-8", WINDOWS_CODEPAGE);
762                    if (cd != (iconv_t) - 1)
763                    {
764                            size_t utf8_length = length * 2;
765                            char *utf8_data = malloc(utf8_length);
766                            size_t utf8_length_remaining = utf8_length;
767                            char *utf8_data_remaining = utf8_data;
768                            char *data_remaining = (char *) data;
769                            size_t length_remaining = (size_t) length;
770                            if (utf8_data == NULL)
771                            {
772                                    iconv_close(cd);
773                                    return;
774                            }
775                            iconv(cd, &data_remaining, &length_remaining, &utf8_data_remaining,
776                                  &utf8_length_remaining);
777                            iconv_close(cd);
778                            free_data = True;
779                            data = (uint8 *) utf8_data;
780                            length = utf8_length - utf8_length_remaining;
781                    }
782            }
783            else if (selection_request.target == format_unicode_atom)
784            {
785                    /* We're expecting a CF_UNICODETEXT response, so what we're
786                       receiving matches our requirements and there's no need
787                       for further conversions. */
788            }
789    #endif
790            else if (selection_request.target == rdesktop_clipboard_formats_atom)
791            {
792                    /* Pass as-is */
793            }
794            else
795            {
796                    xclip_refuse_selection(&selection_request);
797                    has_selection_request = False;
798                    return;
799            }
800    
801            xclip_provide_selection(&selection_request, selection_request.target, 8, data, length - 1);
802            has_selection_request = False;
803    
804            if (free_data)
805                    free(data);
806  }  }
807    
808  void  void
# Line 247  ui_clip_request_data(uint32 format) Line 811  ui_clip_request_data(uint32 format)
811          Window selectionowner;          Window selectionowner;
812    
813          DEBUG_CLIPBOARD(("Request from server for format %d\n", format));          DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
814            rdp_clipboard_request_format = format;
815    
816          if (rdesktop_is_selection_owner)          if (rdesktop_is_selection_owner)
817          {          {
# Line 282  ui_clip_request_data(uint32 format) Line 847  ui_clip_request_data(uint32 format)
847  void  void
848  ui_clip_sync(void)  ui_clip_sync(void)
849  {  {
850          cliprdr_send_text_format_announce();          cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
851  }  }
852    
853    
# Line 299  xclip_init(void) Line 864  xclip_init(void)
864          rdesktop_clipboard_target_atom =          rdesktop_clipboard_target_atom =
865                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
866          incr_atom = XInternAtom(g_display, "INCR", False);          incr_atom = XInternAtom(g_display, "INCR", False);
867          targets[0] = targets_atom;          format_string_atom = XInternAtom(g_display, "STRING", False);
868          targets[1] = XInternAtom(g_display, "TEXT", False);          format_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False);
869          targets[2] = XInternAtom(g_display, "UTF8_STRING", False);          format_unicode_atom = XInternAtom(g_display, "text/unicode", False);
870          targets[3] = XInternAtom(g_display, "text/unicode", False);          num_targets = 0;
871          targets[4] = XInternAtom(g_display, "TIMESTAMP", False);          targets[num_targets++] = targets_atom;
872          targets[5] = XA_STRING;          targets[num_targets++] = timestamp_atom;
873            targets[num_targets++] = rdesktop_clipboard_formats_atom;
874            targets[num_targets++] = format_string_atom;
875    #ifdef USE_UNICODE_CLIPBOARD
876            targets[num_targets++] = format_utf8_string_atom;
877    #endif
878            targets[num_targets++] = format_unicode_atom;
879            targets[num_targets++] = XA_STRING;
880    
881          /* 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.
882             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.462  
changed lines
  Added in v.1038

  ViewVC Help
Powered by ViewVC 1.1.26