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

Legend:
Removed from v.432  
changed lines
  Added in v.1206

  ViewVC Help
Powered by ViewVC 1.1.26