/[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 941 by astrand, Tue Aug 2 09:19:24 2005 UTC revision 1404 by astrand, Wed Apr 25 12:44:26 2007 UTC
# Line 1  Line 1 
1  /* -*- c-basic-offset: 8 -*-  /* -*- c-basic-offset: 8 -*-
2     rdesktop: A Remote Desktop Protocol client.     rdesktop: A Remote Desktop Protocol client.
3     Protocol services - Clipboard functions     Protocol services - Clipboard functions
4     Copyright (C) Erik Forsberg <forsberg@cendio.se> 2003     Copyright (C) Erik Forsberg <forsberg@cendio.se> 2003-2007
5     Copyright (C) Matthew Chapman 2003     Copyright (C) Matthew Chapman 2003-2007
6    
7     This program is free software; you can redistribute it and/or modify     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by     it under the terms of the GNU General Public License as published by
# 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 8
52    
53  extern Display *g_display;  extern Display *g_display;
54  extern Window g_wnd;  extern Window g_wnd;
55  extern Time g_last_gesturetime;  extern Time g_last_gesturetime;
56    extern RD_BOOL g_rdpclip;
57    
58  static Atom clipboard_atom, primary_atom, targets_atom, timestamp_atom;  /* Mode of operation.
59  static Atom rdesktop_clipboard_target_atom, rdesktop_clipboard_formats_atom, incr_atom;     - Auto: Look at both PRIMARY and CLIPBOARD and use the most recent.
60       - Non-auto: Look at just CLIPBOARD. */
61    static RD_BOOL auto_mode = True;
62    /* Atoms of the two X selections we're dealing with: CLIPBOARD (explicit-copy) and PRIMARY (selection-copy) */
63    static Atom clipboard_atom, primary_atom;
64    /* Atom of the TARGETS clipboard target */
65    static Atom targets_atom;
66    /* Atom of the TIMESTAMP clipboard target */
67    static Atom timestamp_atom;
68    /* Atom _RDESKTOP_CLIPBOARD_TARGET which is used as the 'property' argument in
69       XConvertSelection calls: This is the property of our window into which
70       XConvertSelection will store the received clipboard data. */
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    /* Clipboard target for getting a list of native Windows clipboard formats. The
79       presence of this target indicates that the selection owner is another rdesktop. */
80    static Atom rdesktop_clipboard_formats_atom;
81    /* The clipboard target (X jargon for "clipboard format") for rdesktop-to-rdesktop
82       interchange of Windows native clipboard data. The requestor must supply the
83       desired native Windows clipboard format in the associated property. */
84    static Atom rdesktop_native_atom;
85    /* Local copy of the list of native Windows clipboard formats. */
86    static uint8 *formats_data = NULL;
87    static uint32 formats_data_length = 0;
88    /* We need to know when another rdesktop process gets or loses ownership of a
89       selection. Without XFixes we do this by touching a property on the root window
90       which will generate PropertyNotify notifications. */
91    static Atom rdesktop_selection_notify_atom;
92    /* State variables that indicate if we're currently probing the targets of the
93       selection owner. reprobe_selections indicate that the ownership changed in
94       the middle of the current probe so it should be restarted. */
95    static RD_BOOL probing_selections, reprobe_selections;
96    /* Atoms _RDESKTOP_PRIMARY_OWNER and _RDESKTOP_CLIPBOARD_OWNER. Used as properties
97       on the root window to indicate which selections that are owned by rdesktop. */
98    static Atom rdesktop_primary_owner_atom, rdesktop_clipboard_owner_atom;
99    static Atom format_string_atom, format_utf8_string_atom, format_unicode_atom;
100    /* Atom of the INCR clipboard type (see ICCCM on "INCR Properties") */
101    static Atom incr_atom;
102    /* Stores the last "selection request" (= another X client requesting clipboard data from us).
103       To satisfy such a request, we request the clipboard data from the RDP server.
104       When we receive the response from the RDP server (asynchronously), this variable gives us
105       the context to proceed. */
106  static XSelectionRequestEvent selection_request;  static XSelectionRequestEvent selection_request;
107  static Atom targets[NUM_TARGETS];  /* Denotes we have a pending selection request. */
108  static int have_primary = 0;  static RD_BOOL has_selection_request;
109  static int rdesktop_is_selection_owner = 0;  /* Stores the clipboard format (CF_TEXT, CF_UNICODETEXT etc.) requested in the last
110       CLIPDR_DATA_REQUEST (= the RDP server requesting clipboard data from us).
111       When we receive this data from whatever X client offering it, this variable gives us
112       the context to proceed.
113     */
114    static int rdp_clipboard_request_format;
115    /* Array of offered clipboard targets that will be sent to fellow X clients upon a TARGETS request. */
116    static Atom targets[MAX_TARGETS];
117    static int num_targets;
118    /* Denotes that an rdesktop (not this rdesktop) is owning the selection,
119       allowing us to interchange Windows native clipboard data directly. */
120    static RD_BOOL rdesktop_is_selection_owner = False;
121    /* Time when we acquired the selection. */
122    static Time acquire_time = 0;
123    
124    /* Denotes that an INCR ("chunked") transfer is in progress. */
125  static int g_waiting_for_INCR = 0;  static int g_waiting_for_INCR = 0;
126    /* Denotes the target format of the ongoing INCR ("chunked") transfer. */
127    static Atom g_incr_target = 0;
128    /* Buffers an INCR transfer. */
129  static uint8 *g_clip_buffer = 0;  static uint8 *g_clip_buffer = 0;
130    /* Denotes the size of g_clip_buffer. */
131  static uint32 g_clip_buflen = 0;  static uint32 g_clip_buflen = 0;
132    
133  /* Replace CR-LF to LF (well, rather removing all CR:s) This is done  /* Translates CR-LF to LF.
134     in-place. The length is updated. Handles embedded nulls */     Changes the string in-place.
135       Does not stop on embedded nulls.
136       The length is updated. */
137  static void  static void
138  crlf2lf(uint8 * data, uint32 * length)  crlf2lf(uint8 * data, uint32 * length)
139  {  {
# Line 56  crlf2lf(uint8 * data, uint32 * length) Line 148  crlf2lf(uint8 * data, uint32 * length)
148          *length = dst - data;          *length = dst - data;
149  }  }
150    
151  /* Translate LF to CR-LF. To do this, we must allocate more memory.    #ifdef USE_UNICODE_CLIPBOARD
152    /* Translate LF to CR-LF. To do this, we must allocate more memory.
153       The returned string is null-terminated, as required by CF_UNICODETEXT.
154       The size is updated. */
155    static uint8 *
156    utf16_lf2crlf(uint8 * data, uint32 * size)
157    {
158            uint8 *result;
159            uint16 *inptr, *outptr;
160            RD_BOOL swap_endianess;
161    
162            /* Worst case: Every char is LF */
163            result = xmalloc((*size * 2) + 2);
164            if (result == NULL)
165                    return NULL;
166    
167            inptr = (uint16 *) data;
168            outptr = (uint16 *) result;
169    
170            /* Check for a reversed BOM */
171            swap_endianess = (*inptr == 0xfffe);
172    
173            uint16 uvalue_previous = 0;     /* Kept so we'll avoid translating CR-LF to CR-CR-LF */
174            while ((uint8 *) inptr < data + *size)
175            {
176                    uint16 uvalue = *inptr;
177                    if (swap_endianess)
178                            uvalue = ((uvalue << 8) & 0xff00) + (uvalue >> 8);
179                    if ((uvalue == 0x0a) && (uvalue_previous != 0x0d))
180                            *outptr++ = swap_endianess ? 0x0d00 : 0x0d;
181                    uvalue_previous = uvalue;
182                    *outptr++ = *inptr++;
183            }
184            *outptr++ = 0;          /* null termination */
185            *size = (uint8 *) outptr - result;
186    
187            return result;
188    }
189    #else
190    /* Translate LF to CR-LF. To do this, we must allocate more memory.
191     The length is updated. */     The length is updated. */
192  static uint8 *  static uint8 *
193  lf2crlf(uint8 * data, uint32 * length)  lf2crlf(uint8 * data, uint32 * length)
# Line 69  lf2crlf(uint8 * data, uint32 * length) Line 200  lf2crlf(uint8 * data, uint32 * length)
200          p = data;          p = data;
201          o = result;          o = result;
202    
203            uint8 previous = '\0';  /* Kept to avoid translating CR-LF to CR-CR-LF */
204          while (p < data + *length)          while (p < data + *length)
205          {          {
206                  if (*p == '\x0a')                  if ((*p == '\x0a') && (previous != '\x0d'))
207                          *o++ = '\x0d';                          *o++ = '\x0d';
208                    previous = *p;
209                  *o++ = *p++;                  *o++ = *p++;
210          }          }
211          *length = o - result;          *length = o - result;
# Line 82  lf2crlf(uint8 * data, uint32 * length) Line 215  lf2crlf(uint8 * data, uint32 * length)
215    
216          return result;          return result;
217  }  }
218    #endif
219    
220  static void  static void
221  xclip_provide_selection(XSelectionRequestEvent * req, Atom type, unsigned int format, uint8 * data,  xclip_provide_selection(XSelectionRequestEvent * req, Atom type, unsigned int format, uint8 * data,
# Line 90  xclip_provide_selection(XSelectionReques Line 223  xclip_provide_selection(XSelectionReques
223  {  {
224          XEvent xev;          XEvent xev;
225    
226            DEBUG_CLIPBOARD(("xclip_provide_selection: requestor=0x%08x, target=%s, property=%s, length=%u\n", (unsigned) req->requestor, XGetAtomName(g_display, req->target), XGetAtomName(g_display, req->property), (unsigned) length));
227    
228          XChangeProperty(g_display, req->requestor, req->property,          XChangeProperty(g_display, req->requestor, req->property,
229                          type, format, PropModeReplace, data, length);                          type, format, PropModeReplace, data, length);
230    
# Line 104  xclip_provide_selection(XSelectionReques Line 239  xclip_provide_selection(XSelectionReques
239          XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);          XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);
240  }  }
241    
242    /* Replies a clipboard requestor, telling that we're unable to satisfy his request for whatever reason.
243       This has the benefit of finalizing the clipboard negotiation and thus not leaving our requestor
244       lingering (and, potentially, stuck). */
245    static void
246    xclip_refuse_selection(XSelectionRequestEvent * req)
247    {
248            XEvent xev;
249    
250            DEBUG_CLIPBOARD(("xclip_refuse_selection: requestor=0x%08x, target=%s, property=%s\n",
251                             (unsigned) req->requestor, XGetAtomName(g_display, req->target),
252                             XGetAtomName(g_display, req->property)));
253    
254            xev.xselection.type = SelectionNotify;
255            xev.xselection.serial = 0;
256            xev.xselection.send_event = True;
257            xev.xselection.requestor = req->requestor;
258            xev.xselection.selection = req->selection;
259            xev.xselection.target = req->target;
260            xev.xselection.property = None;
261            xev.xselection.time = req->time;
262            XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);
263    }
264    
265    /* Wrapper for cliprdr_send_data which also cleans the request state. */
266    static void
267    helper_cliprdr_send_response(uint8 * data, uint32 length)
268    {
269            if (rdp_clipboard_request_format != 0)
270            {
271                    cliprdr_send_data(data, length);
272                    rdp_clipboard_request_format = 0;
273                    if (!rdesktop_is_selection_owner)
274                            cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
275            }
276    }
277    
278    /* Last resort, when we have to provide clipboard data but for whatever
279       reason couldn't get any.
280     */
281    static void
282    helper_cliprdr_send_empty_response()
283    {
284            helper_cliprdr_send_response(NULL, 0);
285    }
286    
287    /* Replies with clipboard data to RDP, converting it from the target format
288       to the expected RDP format as necessary. Returns true if data was sent.
289     */
290    static RD_BOOL
291    xclip_send_data_with_convert(uint8 * source, size_t source_size, Atom target)
292    {
293            DEBUG_CLIPBOARD(("xclip_send_data_with_convert: target=%s, size=%u\n",
294                             XGetAtomName(g_display, target), (unsigned) source_size));
295    
296    #ifdef USE_UNICODE_CLIPBOARD
297            if (target == format_string_atom ||
298                target == format_unicode_atom || target == format_utf8_string_atom)
299            {
300                    size_t unicode_buffer_size;
301                    char *unicode_buffer;
302                    iconv_t cd;
303                    size_t unicode_buffer_size_remaining;
304                    char *unicode_buffer_remaining;
305                    char *data_remaining;
306                    size_t data_size_remaining;
307                    uint32 translated_data_size;
308                    uint8 *translated_data;
309    
310                    if (rdp_clipboard_request_format != RDP_CF_TEXT)
311                            return False;
312    
313                    /* Make an attempt to convert any string we send to Unicode.
314                       We don't know what the RDP server's ANSI Codepage is, or how to convert
315                       to it, so using CF_TEXT is not safe (and is unnecessary, since all
316                       WinNT versions are Unicode-minded).
317                     */
318                    if (target == format_string_atom)
319                    {
320                            char *locale_charset = nl_langinfo(CODESET);
321                            cd = iconv_open(WINDOWS_CODEPAGE, locale_charset);
322                            if (cd == (iconv_t) - 1)
323                            {
324                                    DEBUG_CLIPBOARD(("Locale charset %s not found in iconv. Unable to convert clipboard text.\n", locale_charset));
325                                    return False;
326                            }
327                            unicode_buffer_size = source_size * 4;
328                    }
329                    else if (target == format_unicode_atom)
330                    {
331                            cd = iconv_open(WINDOWS_CODEPAGE, "UCS-2");
332                            if (cd == (iconv_t) - 1)
333                            {
334                                    return False;
335                            }
336                            unicode_buffer_size = source_size;
337                    }
338                    else if (target == format_utf8_string_atom)
339                    {
340                            cd = iconv_open(WINDOWS_CODEPAGE, "UTF-8");
341                            if (cd == (iconv_t) - 1)
342                            {
343                                    return False;
344                            }
345                            /* UTF-8 is guaranteed to be less or equally compact
346                               as UTF-16 for all Unicode chars >=2 bytes.
347                             */
348                            unicode_buffer_size = source_size * 2;
349                    }
350                    else
351                    {
352                            return False;
353                    }
354    
355                    unicode_buffer = xmalloc(unicode_buffer_size);
356                    unicode_buffer_size_remaining = unicode_buffer_size;
357                    unicode_buffer_remaining = unicode_buffer;
358                    data_remaining = (char *) source;
359                    data_size_remaining = source_size;
360                    iconv(cd, (ICONV_CONST char **) &data_remaining, &data_size_remaining,
361                          &unicode_buffer_remaining, &unicode_buffer_size_remaining);
362                    iconv_close(cd);
363    
364                    /* translate linebreaks */
365                    translated_data_size = unicode_buffer_size - unicode_buffer_size_remaining;
366                    translated_data = utf16_lf2crlf((uint8 *) unicode_buffer, &translated_data_size);
367                    if (translated_data != NULL)
368                    {
369                            DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n",
370                                             translated_data_size));
371                            helper_cliprdr_send_response(translated_data, translated_data_size);
372                            xfree(translated_data); /* Not the same thing as XFree! */
373                    }
374    
375                    xfree(unicode_buffer);
376    
377                    return True;
378            }
379    #else
380            if (target == format_string_atom)
381            {
382                    uint8 *translated_data;
383                    uint32 length = source_size;
384    
385                    if (rdp_clipboard_request_format != RDP_CF_TEXT)
386                            return False;
387    
388                    DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
389                    translated_data = lf2crlf(source, &length);
390                    if (translated_data != NULL)
391                    {
392                            helper_cliprdr_send_response(translated_data, length);
393                            xfree(translated_data); /* Not the same thing as XFree! */
394                    }
395    
396                    return True;
397            }
398    #endif
399            else if (target == rdesktop_native_atom)
400            {
401                    helper_cliprdr_send_response(source, source_size + 1);
402    
403                    return True;
404            }
405            else
406            {
407                    return False;
408            }
409    }
410    
411    static void
412    xclip_clear_target_props()
413    {
414            XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
415            XDeleteProperty(g_display, g_wnd, rdesktop_primary_timestamp_target_atom);
416            XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_timestamp_target_atom);
417    }
418    
419    static void
420    xclip_notify_change()
421    {
422            XChangeProperty(g_display, DefaultRootWindow(g_display),
423                            rdesktop_selection_notify_atom, XA_INTEGER, 32, PropModeReplace, NULL, 0);
424    }
425    
426    static void
427    xclip_probe_selections()
428    {
429            Window primary_owner, clipboard_owner;
430    
431            if (probing_selections)
432            {
433                    DEBUG_CLIPBOARD(("Already probing selections. Scheduling reprobe.\n"));
434                    reprobe_selections = True;
435                    return;
436            }
437    
438            DEBUG_CLIPBOARD(("Probing selections.\n"));
439    
440            probing_selections = True;
441            reprobe_selections = False;
442    
443            xclip_clear_target_props();
444    
445            if (auto_mode)
446                    primary_owner = XGetSelectionOwner(g_display, primary_atom);
447            else
448                    primary_owner = None;
449    
450            clipboard_owner = XGetSelectionOwner(g_display, clipboard_atom);
451    
452            /* If we own all relevant selections then don't do anything. */
453            if (((primary_owner == g_wnd) || !auto_mode) && (clipboard_owner == g_wnd))
454                    goto end;
455    
456            /* Both available */
457            if ((primary_owner != None) && (clipboard_owner != None))
458            {
459                    primary_timestamp = 0;
460                    clipboard_timestamp = 0;
461                    XConvertSelection(g_display, primary_atom, timestamp_atom,
462                                      rdesktop_primary_timestamp_target_atom, g_wnd, CurrentTime);
463                    XConvertSelection(g_display, clipboard_atom, timestamp_atom,
464                                      rdesktop_clipboard_timestamp_target_atom, g_wnd, CurrentTime);
465                    return;
466            }
467    
468            /* Just PRIMARY */
469            if (primary_owner != None)
470            {
471                    XConvertSelection(g_display, primary_atom, targets_atom,
472                                      rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
473                    return;
474            }
475    
476            /* Just CLIPBOARD */
477            if (clipboard_owner != None)
478            {
479                    XConvertSelection(g_display, clipboard_atom, targets_atom,
480                                      rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
481                    return;
482            }
483    
484            DEBUG_CLIPBOARD(("No owner of any selection.\n"));
485    
486            /* FIXME:
487               Without XFIXES, we cannot reliably know the formats offered by an
488               upcoming selection owner, so we just lie about him offering
489               RDP_CF_TEXT. */
490            cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
491    
492          end:
493            probing_selections = False;
494    }
495    
496    /* This function is called for SelectionNotify events.
497       The SelectionNotify event is sent from the clipboard owner to the requestor
498       after his request was satisfied.
499       If this function is called, we're the requestor side. */
500    #ifndef MAKE_PROTO
501  void  void
502  xclip_handle_SelectionNotify(XSelectionEvent * event)  xclip_handle_SelectionNotify(XSelectionEvent * event)
503  {  {
504          unsigned long nitems, bytes_left;          unsigned long nitems, bytes_left;
505          XWindowAttributes wa;          XWindowAttributes wa;
506          Atom type, best_target, text_target;          Atom type;
507          Atom *supported_targets;          Atom *supported_targets;
508          int res, i, format;          int res, i, format;
509          uint8 *data;          uint8 *data = NULL;
510    
511          if (event->property == None)          if (event->property == None)
512                  goto fail;                  goto fail;
# Line 122  xclip_handle_SelectionNotify(XSelectionE Line 516  xclip_handle_SelectionNotify(XSelectionE
516                           XGetAtomName(g_display, event->target),                           XGetAtomName(g_display, event->target),
517                           XGetAtomName(g_display, event->property)));                           XGetAtomName(g_display, event->property)));
518    
519          if (event->property == None)          if (event->target == timestamp_atom)
520                  goto fail;          {
521                    if (event->selection == primary_atom)
522                    {
523                            res = XGetWindowProperty(g_display, g_wnd,
524                                                     rdesktop_primary_timestamp_target_atom, 0,
525                                                     XMaxRequestSize(g_display), False, AnyPropertyType,
526                                                     &type, &format, &nitems, &bytes_left, &data);
527                    }
528                    else
529                    {
530                            res = XGetWindowProperty(g_display, g_wnd,
531                                                     rdesktop_clipboard_timestamp_target_atom, 0,
532                                                     XMaxRequestSize(g_display), False, AnyPropertyType,
533                                                     &type, &format, &nitems, &bytes_left, &data);
534                    }
535    
536    
537                    if ((res != Success) || (nitems != 1) || (format != 32))
538                    {
539                            DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
540                            goto fail;
541                    }
542    
543                    if (event->selection == primary_atom)
544                    {
545                            primary_timestamp = *(Time *) data;
546                            if (primary_timestamp == 0)
547                                    primary_timestamp++;
548                            XDeleteProperty(g_display, g_wnd, rdesktop_primary_timestamp_target_atom);
549                            DEBUG_CLIPBOARD(("Got PRIMARY timestamp: %u\n",
550                                             (unsigned) primary_timestamp));
551                    }
552                    else
553                    {
554                            clipboard_timestamp = *(Time *) data;
555                            if (clipboard_timestamp == 0)
556                                    clipboard_timestamp++;
557                            XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_timestamp_target_atom);
558                            DEBUG_CLIPBOARD(("Got CLIPBOARD timestamp: %u\n",
559                                             (unsigned) clipboard_timestamp));
560                    }
561    
562                    XFree(data);
563    
564                    if (primary_timestamp && clipboard_timestamp)
565                    {
566                            if (primary_timestamp > clipboard_timestamp)
567                            {
568                                    DEBUG_CLIPBOARD(("PRIMARY is most recent selection.\n"));
569                                    XConvertSelection(g_display, primary_atom, targets_atom,
570                                                      rdesktop_clipboard_target_atom, g_wnd,
571                                                      event->time);
572                            }
573                            else
574                            {
575                                    DEBUG_CLIPBOARD(("CLIPBOARD is most recent selection.\n"));
576                                    XConvertSelection(g_display, clipboard_atom, targets_atom,
577                                                      rdesktop_clipboard_target_atom, g_wnd,
578                                                      event->time);
579                            }
580                    }
581    
582                    return;
583            }
584    
585            if (probing_selections && reprobe_selections)
586            {
587                    probing_selections = False;
588                    xclip_probe_selections();
589                    return;
590            }
591    
592          res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,          res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
593                                   0, XMaxRequestSize(g_display), True, AnyPropertyType,                                   0, XMaxRequestSize(g_display), False, AnyPropertyType,
594                                   &type, &format, &nitems, &bytes_left, &data);                                   &type, &format, &nitems, &bytes_left, &data);
595    
596            xclip_clear_target_props();
597    
598          if (res != Success)          if (res != Success)
599          {          {
600                  DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));                  DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
601                  goto fail;                  goto fail;
602          }          }
603    
604            if (type == incr_atom)
605            {
606                    DEBUG_CLIPBOARD(("Received INCR.\n"));
607    
608                    XGetWindowAttributes(g_display, g_wnd, &wa);
609                    if ((wa.your_event_mask | PropertyChangeMask) != wa.your_event_mask)
610                    {
611                            XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));
612                    }
613                    XFree(data);
614                    g_incr_target = event->target;
615                    g_waiting_for_INCR = 1;
616                    goto end;
617            }
618    
619          /* Negotiate target format */          /* Negotiate target format */
620          if (event->target == targets_atom)          if (event->target == targets_atom)
621          {          {
622                  /* FIXME: We should choose format here based on what the server wanted */                  /* Determine the best of text targets that we have available:
623                  best_target = XA_STRING;                     Prefer UTF8_STRING > text/unicode (unspecified encoding) > STRING
624                       (ignore TEXT and COMPOUND_TEXT because we don't have code to handle them)
625                     */
626                    int text_target_satisfaction = 0;
627                    Atom best_text_target = 0;      /* measures how much we're satisfied with what we found */
628                  if (type != None)                  if (type != None)
629                  {                  {
630                          supported_targets = (Atom *) data;                          supported_targets = (Atom *) data;
                         text_target = XInternAtom(g_display, "TEXT", False);  
631                          for (i = 0; i < nitems; i++)                          for (i = 0; i < nitems; i++)
632                          {                          {
633                                  DEBUG_CLIPBOARD(("Target %d: %s\n", i,                                  DEBUG_CLIPBOARD(("Target %d: %s\n", i,
634                                                   XGetAtomName(g_display, supported_targets[i])));                                                   XGetAtomName(g_display, supported_targets[i])));
635                                  if (supported_targets[i] == text_target)                                  if (supported_targets[i] == format_string_atom)
636                                    {
637                                            if (text_target_satisfaction < 1)
638                                            {
639                                                    DEBUG_CLIPBOARD(("Other party supports STRING, choosing that as best_target\n"));
640                                                    best_text_target = supported_targets[i];
641                                                    text_target_satisfaction = 1;
642                                            }
643                                    }
644    #ifdef USE_UNICODE_CLIPBOARD
645                                    else if (supported_targets[i] == format_unicode_atom)
646                                    {
647                                            if (text_target_satisfaction < 2)
648                                            {
649                                                    DEBUG_CLIPBOARD(("Other party supports text/unicode, choosing that as best_target\n"));
650                                                    best_text_target = supported_targets[i];
651                                                    text_target_satisfaction = 2;
652                                            }
653                                    }
654                                    else if (supported_targets[i] == format_utf8_string_atom)
655                                  {                                  {
656                                          DEBUG_CLIPBOARD(("Other party supports TEXT, choosing that as best_target\n"));                                          if (text_target_satisfaction < 3)
657                                          best_target = text_target;                                          {
658                                          break;                                                  DEBUG_CLIPBOARD(("Other party supports UTF8_STRING, choosing that as best_target\n"));
659                                                    best_text_target = supported_targets[i];
660                                                    text_target_satisfaction = 3;
661                                            }
662                                    }
663    #endif
664                                    else if (supported_targets[i] == rdesktop_clipboard_formats_atom)
665                                    {
666                                            if (probing_selections && (text_target_satisfaction < 4))
667                                            {
668                                                    DEBUG_CLIPBOARD(("Other party supports native formats, choosing that as best_target\n"));
669                                                    best_text_target = supported_targets[i];
670                                                    text_target_satisfaction = 4;
671                                            }
672                                  }                                  }
673                          }                          }
                         XFree(data);  
674                  }                  }
675    
676                  XConvertSelection(g_display, primary_atom, best_target,                  /* Kickstarting the next step in the process of satisfying RDP's
677                                    rdesktop_clipboard_target_atom, g_wnd, event->time);                     clipboard request -- specifically, requesting the actual clipboard data.
678                  return;                   */
679          }                  if ((best_text_target != 0)
680                        && (!probing_selections
681          if (type == incr_atom)                          || (best_text_target == rdesktop_clipboard_formats_atom)))
         {  
                 DEBUG_CLIPBOARD(("Received INCR.\n"));  
   
                 XGetWindowAttributes(g_display, g_wnd, &wa);  
                 if ((wa.your_event_mask | PropertyChangeMask) != wa.your_event_mask)  
682                  {                  {
683                          XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));                          XConvertSelection(g_display, event->selection, best_text_target,
684                                              rdesktop_clipboard_target_atom, g_wnd, event->time);
685                            goto end;
686                  }                  }
687                  XDeleteProperty(g_display, g_wnd, type);                  else
                 XFree(data);  
                 g_waiting_for_INCR = 1;  
   
                 if ((XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom, 0,  
                                         4096L, True, AnyPropertyType,  
                                         &type, &format, &nitems, &bytes_left, &data) != Success))  
688                  {                  {
689                          DEBUG_CLIPBOARD(("XGetWindowProperty failed.\n"));                          DEBUG_CLIPBOARD(("Unable to find a textual target to satisfy RDP clipboard text request\n"));
690                          goto fail;                          goto fail;
691                  }                  }
692                  else          }
693            else
694            {
695                    if (probing_selections)
696                  {                  {
697                          uint8 *translated_data;                          Window primary_owner, clipboard_owner;
                         uint32 length = nitems;  
   
                         translated_data = lf2crlf(data, &length);  
698    
699                          g_clip_buffer = (uint8 *) xmalloc(length);                          /* FIXME:
700                          strncpy((char *) g_clip_buffer, (char *) translated_data, length);                             Without XFIXES, we must make sure that the other
701                          xfree(translated_data);                             rdesktop owns all relevant selections or we might try
702                          g_clip_buflen = length;                             to get a native format from non-rdesktop window later
703                               on. */
704                          XFree(data);  
705                          return;                          clipboard_owner = XGetSelectionOwner(g_display, clipboard_atom);
706    
707                            if (auto_mode)
708                                    primary_owner = XGetSelectionOwner(g_display, primary_atom);
709                            else
710                                    primary_owner = clipboard_owner;
711    
712                            if (primary_owner != clipboard_owner)
713                                    goto fail;
714    
715                            DEBUG_CLIPBOARD(("Got fellow rdesktop formats\n"));
716                            probing_selections = False;
717                            rdesktop_is_selection_owner = True;
718                            cliprdr_send_native_format_announce(data, nitems);
719                    }
720                    else if ((!nitems) || (!xclip_send_data_with_convert(data, nitems, event->target)))
721                    {
722                            goto fail;
723                  }                  }
724          }          }
725    
726          /* Translate linebreaks, but only if not getting data from        end:
727             other rdesktop instance */          if (data)
728          if (event->target != rdesktop_clipboard_formats_atom)                  XFree(data);
         {  
                 uint8 *translated_data;  
                 uint32 length = nitems;  
729    
730                  DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));          return;
731                  translated_data = lf2crlf(data, &length);  
732                  cliprdr_send_data(translated_data, length + 1);        fail:
733                  xfree(translated_data); /* Not the same thing as XFree! */          xclip_clear_target_props();
734            if (probing_selections)
735            {
736                    DEBUG_CLIPBOARD(("Unable to find suitable target. Using default text format.\n"));
737                    probing_selections = False;
738                    rdesktop_is_selection_owner = False;
739    
740                    /* FIXME:
741                       Without XFIXES, we cannot reliably know the formats offered by an
742                       upcoming selection owner, so we just lie about him offering
743                       RDP_CF_TEXT. */
744                    cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
745          }          }
746          else          else
747          {          {
748                  cliprdr_send_data(data, nitems + 1);                  helper_cliprdr_send_empty_response();
749          }          }
750          XFree(data);          goto end;
   
         if (!rdesktop_is_selection_owner)  
                 cliprdr_send_text_format_announce();  
         return;  
   
       fail:  
         cliprdr_send_data(NULL, 0);  
751  }  }
752    
753    /* This function is called for SelectionRequest events.
754       The SelectionRequest event is sent from the requestor to the clipboard owner
755       to request clipboard data.
756     */
757  void  void
758  xclip_handle_SelectionRequest(XSelectionRequestEvent * event)  xclip_handle_SelectionRequest(XSelectionRequestEvent * event)
759  {  {
760          unsigned long nitems, bytes_left;          unsigned long nitems, bytes_left;
761          unsigned char *prop_return;          unsigned char *prop_return = NULL;
         uint32 *wanted_format;  
762          int format, res;          int format, res;
763          Atom type;          Atom type;
764    
# Line 242  xclip_handle_SelectionRequest(XSelection Line 769  xclip_handle_SelectionRequest(XSelection
769    
770          if (event->target == targets_atom)          if (event->target == targets_atom)
771          {          {
772                  xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, NUM_TARGETS);                  xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, num_targets);
773                  return;                  return;
774          }          }
775          else if (event->target == timestamp_atom)          else if (event->target == timestamp_atom)
776          {          {
777                  xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);                  xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & acquire_time, 1);
778                  return;                  return;
779          }          }
780          else if (event->target == rdesktop_clipboard_formats_atom)          else if (event->target == rdesktop_clipboard_formats_atom)
781          {          {
782                  res = XGetWindowProperty(g_display, event->requestor,                  xclip_provide_selection(event, XA_STRING, 8, formats_data, formats_data_length);
                                          rdesktop_clipboard_target_atom, 0, 1, True, XA_INTEGER,  
                                          &type, &format, &nitems, &bytes_left, &prop_return);  
                 wanted_format = (uint32 *) prop_return;  
                 format = (res == Success) ? *wanted_format : CF_TEXT;  
                 /* FIXME: Need to free returned data? */  
783          }          }
784          else          else
785          {          {
786                  format = CF_TEXT;                  /* All the following targets require an async operation with the RDP server
787          }                     and currently we don't do X clipboard request queueing so we can only
788                       handle one such request at a time. */
789                    if (has_selection_request)
790                    {
791                            DEBUG_CLIPBOARD(("Error: Another clipboard request was already sent to the RDP server and not yet responded. Refusing this request.\n"));
792                            xclip_refuse_selection(event);
793                            return;
794                    }
795                    if (event->target == rdesktop_native_atom)
796                    {
797                            /* Before the requestor makes a request for the _RDESKTOP_NATIVE target,
798                               he should declare requestor[property] = CF_SOMETHING. */
799                            res = XGetWindowProperty(g_display, event->requestor,
800                                                     event->property, 0, 1, True,
801                                                     XA_INTEGER, &type, &format, &nitems, &bytes_left,
802                                                     &prop_return);
803                            if (res != Success || (!prop_return))
804                            {
805                                    DEBUG_CLIPBOARD(("Requested native format but didn't specifiy which.\n"));
806                                    xclip_refuse_selection(event);
807                                    return;
808                            }
809    
810                            format = *(uint32 *) prop_return;
811                            XFree(prop_return);
812                    }
813                    else if (event->target == format_string_atom || event->target == XA_STRING)
814                    {
815                            /* STRING and XA_STRING are defined to be ISO8859-1 */
816                            format = CF_TEXT;
817                    }
818                    else if (event->target == format_utf8_string_atom)
819                    {
820    #ifdef USE_UNICODE_CLIPBOARD
821                            format = CF_UNICODETEXT;
822    #else
823                            DEBUG_CLIPBOARD(("Requested target unavailable due to lack of Unicode support. (It was not in TARGETS, so why did you ask for it?!)\n"));
824                            xclip_refuse_selection(event);
825                            return;
826    #endif
827                    }
828                    else if (event->target == format_unicode_atom)
829                    {
830                            /* Assuming text/unicode to be UTF-16 */
831                            format = CF_UNICODETEXT;
832                    }
833                    else
834                    {
835                            DEBUG_CLIPBOARD(("Requested target unavailable. (It was not in TARGETS, so why did you ask for it?!)\n"));
836                            xclip_refuse_selection(event);
837                            return;
838                    }
839    
840          cliprdr_send_data_request(format);                  cliprdr_send_data_request(format);
841          selection_request = *event;                  selection_request = *event;
842          /* wait for data */                  has_selection_request = True;
843                    return;         /* wait for data */
844            }
845  }  }
846    
847    /* While this rdesktop holds ownership over the clipboard, it means the clipboard data
848       is offered by the RDP server (and when it is pasted inside RDP, there's no network
849       roundtrip).
850    
851       This event (SelectionClear) symbolizes this rdesktop lost onwership of the clipboard
852       to some other X client. We should find out what clipboard formats this other
853       client offers and announce that to RDP. */
854  void  void
855  xclip_handle_SelectionClear(void)  xclip_handle_SelectionClear(void)
856  {  {
857          DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));          DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
858          have_primary = 0;          xclip_notify_change();
859          XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);          xclip_probe_selections();
         cliprdr_send_text_format_announce();  
860  }  }
861    
862    /* Called when any property changes in our window or the root window. */
863  void  void
864  xclip_handle_PropertyNotify(XPropertyEvent * event)  xclip_handle_PropertyNotify(XPropertyEvent * event)
865  {  {
866          unsigned long nitems, bytes_left;          unsigned long nitems;
867          int format, res;          unsigned long offset = 0;
868            unsigned long bytes_left = 1;
869            int format;
870          XWindowAttributes wa;          XWindowAttributes wa;
871          uint8 *data;          uint8 *data;
872          Atom type;          Atom type;
# Line 290  xclip_handle_PropertyNotify(XPropertyEve Line 874  xclip_handle_PropertyNotify(XPropertyEve
874          if (event->state == PropertyNewValue && g_waiting_for_INCR)          if (event->state == PropertyNewValue && g_waiting_for_INCR)
875          {          {
876                  DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: g_waiting_for_INCR != 0\n"));                  DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: g_waiting_for_INCR != 0\n"));
                 if ((XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom, 0,  
                                         4096L, True, AnyPropertyType,  
                                         &type, &format, &nitems, &bytes_left, &data) != Success))  
                 {  
                         XFree(data);  
                         return;  
                 }  
877    
878                  if (nitems == 0)                  while (bytes_left > 0)
879                  {                  {
880                          XGetWindowAttributes(g_display, g_wnd, &wa);                          /* Unlike the specification, we don't set the 'delete' arugment to True
881                          XSelectInput(g_display, g_wnd, (wa.your_event_mask ^ PropertyChangeMask));                             since we slurp the INCR's chunks in even-smaller chunks of 4096 bytes. */
882                          XFree(data);                          if ((XGetWindowProperty
883                          g_waiting_for_INCR = 0;                               (g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,
884                                  False, AnyPropertyType, &type, &format, &nitems, &bytes_left,
885                          if (g_clip_buflen > 0)                                &data) != Success))
886                          {                          {
887                                  cliprdr_send_data(g_clip_buffer, g_clip_buflen + 1);                                  XFree(data);
888                                    return;
                                 if (!rdesktop_is_selection_owner)  
                                         cliprdr_send_text_format_announce();  
   
                                 xfree(g_clip_buffer);  
                                 g_clip_buffer = 0;  
                                 g_clip_buflen = 0;  
889                          }                          }
                 }  
                 else  
                 {  
                         uint8 *translated_data;  
                         uint32 length = nitems;  
                         uint8 *tmp;  
890    
891                          DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));                          if (nitems == 0)
892                          translated_data = lf2crlf(data, &length);                          {
893                                    /* INCR transfer finished */
894                          tmp = xmalloc(length + g_clip_buflen);                                  XGetWindowAttributes(g_display, g_wnd, &wa);
895                          strncpy((char *) tmp, (char *) g_clip_buffer, g_clip_buflen);                                  XSelectInput(g_display, g_wnd,
896                          xfree(g_clip_buffer);                                               (wa.your_event_mask ^ PropertyChangeMask));
897                                    XFree(data);
898                          strncpy((char *) (tmp + g_clip_buflen), (char *) translated_data, length);                                  g_waiting_for_INCR = 0;
                         xfree(translated_data);  
899    
900                          g_clip_buffer = tmp;                                  if (g_clip_buflen > 0)
901                          g_clip_buflen += length;                                  {
902                                            if (!xclip_send_data_with_convert
903                                                (g_clip_buffer, g_clip_buflen, g_incr_target))
904                                            {
905                                                    helper_cliprdr_send_empty_response();
906                                            }
907                                            xfree(g_clip_buffer);
908                                            g_clip_buffer = NULL;
909                                            g_clip_buflen = 0;
910                                    }
911                            }
912                            else
913                            {
914                                    /* Another chunk in the INCR transfer */
915                                    offset += (nitems / 4); /* offset at which to begin the next slurp */
916                                    g_clip_buffer = xrealloc(g_clip_buffer, g_clip_buflen + nitems);
917                                    memcpy(g_clip_buffer + g_clip_buflen, data, nitems);
918                                    g_clip_buflen += nitems;
919    
920                          XFree(data);                                  XFree(data);
921                          return;                          }
922                  }                  }
923          }                  XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
   
         if (event->atom != rdesktop_clipboard_formats_atom)  
924                  return;                  return;
   
         if (have_primary)       /* from us */  
                 return;  
   
         if (event->state == PropertyNewValue)  
         {  
                 res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),  
                                          rdesktop_clipboard_formats_atom, 0,  
                                          XMaxRequestSize(g_display), False, XA_STRING, &type,  
                                          &format, &nitems, &bytes_left, &data);  
   
                 if ((res == Success) && (nitems > 0))  
                 {  
                         cliprdr_send_native_format_announce(data, nitems);  
                         rdesktop_is_selection_owner = 1;  
                         return;  
                 }  
925          }          }
926    
927          /* PropertyDelete, or XGetWindowProperty failed */          if ((event->atom == rdesktop_selection_notify_atom) &&
928          cliprdr_send_text_format_announce();              (event->window == DefaultRootWindow(g_display)))
929          rdesktop_is_selection_owner = 0;                  xclip_probe_selections();
930  }  }
931    #endif
932    
933    
934    /* Called when the RDP server announces new clipboard data formats.
935       In response, we:
936       - take ownership over the clipboard
937       - declare those formats in their Windows native form
938         to other rdesktop instances on this X server */
939  void  void
940  ui_clip_format_announce(uint8 * data, uint32 length)  ui_clip_format_announce(uint8 * data, uint32 length)
941  {  {
942          XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime);          acquire_time = g_last_gesturetime;
943    
944            XSetSelectionOwner(g_display, primary_atom, g_wnd, acquire_time);
945          if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)          if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
         {  
946                  warning("Failed to aquire ownership of PRIMARY clipboard\n");                  warning("Failed to aquire ownership of PRIMARY clipboard\n");
                 return;  
         }  
947    
948          have_primary = 1;          XSetSelectionOwner(g_display, clipboard_atom, g_wnd, acquire_time);
         XChangeProperty(g_display, DefaultRootWindow(g_display),  
                         rdesktop_clipboard_formats_atom, XA_STRING, 8, PropModeReplace, data,  
                         length);  
   
         XSetSelectionOwner(g_display, clipboard_atom, g_wnd, g_last_gesturetime);  
949          if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)          if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)
950                  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");                  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
 }  
951    
952            if (formats_data)
953                    xfree(formats_data);
954            formats_data = xmalloc(length);
955            memcpy(formats_data, data, length);
956            formats_data_length = length;
957    
958            xclip_notify_change();
959    }
960    
961    /* Called when the RDP server responds with clipboard data (after we've requested it). */
962  void  void
963  ui_clip_handle_data(uint8 * data, uint32 length)  ui_clip_handle_data(uint8 * data, uint32 length)
964  {  {
965          if (selection_request.target != rdesktop_clipboard_formats_atom)          RD_BOOL free_data = False;
966    
967            if (length == 0)
968          {          {
969                    xclip_refuse_selection(&selection_request);
970                    has_selection_request = False;
971                    return;
972            }
973    
974            if (selection_request.target == format_string_atom || selection_request.target == XA_STRING)
975            {
976                    /* We're expecting a CF_TEXT response */
977                  uint8 *firstnull;                  uint8 *firstnull;
978    
979                  /* translate linebreaks */                  /* translate linebreaks */
# Line 406  ui_clip_handle_data(uint8 * data, uint32 Line 986  ui_clip_handle_data(uint8 * data, uint32
986                          length = firstnull - data + 1;                          length = firstnull - data + 1;
987                  }                  }
988          }          }
989    #ifdef USE_UNICODE_CLIPBOARD
990            else if (selection_request.target == format_utf8_string_atom)
991            {
992                    /* We're expecting a CF_UNICODETEXT response */
993                    iconv_t cd = iconv_open("UTF-8", WINDOWS_CODEPAGE);
994                    if (cd != (iconv_t) - 1)
995                    {
996                            size_t utf8_length = length * 2;
997                            char *utf8_data = malloc(utf8_length);
998                            size_t utf8_length_remaining = utf8_length;
999                            char *utf8_data_remaining = utf8_data;
1000                            char *data_remaining = (char *) data;
1001                            size_t length_remaining = (size_t) length;
1002                            if (utf8_data == NULL)
1003                            {
1004                                    iconv_close(cd);
1005                                    return;
1006                            }
1007                            iconv(cd, (ICONV_CONST char **) &data_remaining, &length_remaining,
1008                                  &utf8_data_remaining, &utf8_length_remaining);
1009                            iconv_close(cd);
1010                            free_data = True;
1011                            data = (uint8 *) utf8_data;
1012                            length = utf8_length - utf8_length_remaining;
1013                            /* translate linebreaks (works just as well on UTF-8) */
1014                            crlf2lf(data, &length);
1015                    }
1016            }
1017            else if (selection_request.target == format_unicode_atom)
1018            {
1019                    /* We're expecting a CF_UNICODETEXT response, so what we're
1020                       receiving matches our requirements and there's no need
1021                       for further conversions. */
1022            }
1023    #endif
1024            else if (selection_request.target == rdesktop_native_atom)
1025            {
1026                    /* Pass as-is */
1027            }
1028            else
1029            {
1030                    DEBUG_CLIPBOARD(("ui_clip_handle_data: BUG! I don't know how to convert selection target %s!\n", XGetAtomName(g_display, selection_request.target)));
1031                    xclip_refuse_selection(&selection_request);
1032                    has_selection_request = False;
1033                    return;
1034            }
1035    
1036            xclip_provide_selection(&selection_request, selection_request.target, 8, data, length - 1);
1037            has_selection_request = False;
1038    
1039          xclip_provide_selection(&selection_request, XA_STRING, 8, data, length - 1);          if (free_data)
1040                    free(data);
1041    }
1042    
1043    void
1044    ui_clip_request_failed()
1045    {
1046            xclip_refuse_selection(&selection_request);
1047            has_selection_request = False;
1048  }  }
1049    
1050  void  void
1051  ui_clip_request_data(uint32 format)  ui_clip_request_data(uint32 format)
1052  {  {
1053          Window selectionowner;          Window primary_owner, clipboard_owner;
1054    
1055          DEBUG_CLIPBOARD(("Request from server for format %d\n", format));          DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
1056            rdp_clipboard_request_format = format;
1057    
1058            if (probing_selections)
1059            {
1060                    DEBUG_CLIPBOARD(("ui_clip_request_data: Selection probe in progress. Cannot handle request.\n"));
1061                    helper_cliprdr_send_empty_response();
1062                    return;
1063            }
1064    
1065            xclip_clear_target_props();
1066    
1067          if (rdesktop_is_selection_owner)          if (rdesktop_is_selection_owner)
1068          {          {
1069                  XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,                  XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
1070                                  XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);                                  XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
1071    
1072                  XConvertSelection(g_display, primary_atom, rdesktop_clipboard_formats_atom,                  XConvertSelection(g_display, primary_atom, rdesktop_native_atom,
1073                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
1074                  return;                  return;
1075          }          }
1076    
1077          selectionowner = XGetSelectionOwner(g_display, primary_atom);          if (auto_mode)
1078          if (selectionowner != None)                  primary_owner = XGetSelectionOwner(g_display, primary_atom);
1079            else
1080                    primary_owner = None;
1081    
1082            clipboard_owner = XGetSelectionOwner(g_display, clipboard_atom);
1083    
1084            /* Both available */
1085            if ((primary_owner != None) && (clipboard_owner != None))
1086            {
1087                    primary_timestamp = 0;
1088                    clipboard_timestamp = 0;
1089                    XConvertSelection(g_display, primary_atom, timestamp_atom,
1090                                      rdesktop_primary_timestamp_target_atom, g_wnd, CurrentTime);
1091                    XConvertSelection(g_display, clipboard_atom, timestamp_atom,
1092                                      rdesktop_clipboard_timestamp_target_atom, g_wnd, CurrentTime);
1093                    return;
1094            }
1095    
1096            /* Just PRIMARY */
1097            if (primary_owner != None)
1098          {          {
1099                  XConvertSelection(g_display, primary_atom, targets_atom,                  XConvertSelection(g_display, primary_atom, targets_atom,
1100                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
1101                  return;                  return;
1102          }          }
1103    
1104          /* No PRIMARY, try CLIPBOARD */          /* Just CLIPBOARD */
1105          selectionowner = XGetSelectionOwner(g_display, clipboard_atom);          if (clipboard_owner != None)
         if (selectionowner != None)  
1106          {          {
1107                  XConvertSelection(g_display, clipboard_atom, targets_atom,                  XConvertSelection(g_display, clipboard_atom, targets_atom,
1108                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
# Line 445  ui_clip_request_data(uint32 format) Line 1110  ui_clip_request_data(uint32 format)
1110          }          }
1111    
1112          /* No data available */          /* No data available */
1113          cliprdr_send_data(NULL, 0);          helper_cliprdr_send_empty_response();
1114  }  }
1115    
1116  void  void
1117  ui_clip_sync(void)  ui_clip_sync(void)
1118  {  {
1119          cliprdr_send_text_format_announce();          xclip_probe_selections();
1120  }  }
1121    
1122    void
1123    ui_clip_set_mode(const char *optarg)
1124    {
1125            g_rdpclip = True;
1126    
1127            if (str_startswith(optarg, "PRIMARYCLIPBOARD"))
1128                    auto_mode = True;
1129            else if (str_startswith(optarg, "CLIPBOARD"))
1130                    auto_mode = False;
1131            else
1132            {
1133                    warning("Invalid clipboard mode '%s'.\n", optarg);
1134                    g_rdpclip = False;
1135            }
1136    }
1137    
1138  void  void
1139  xclip_init(void)  xclip_init(void)
1140  {  {
1141            if (!g_rdpclip)
1142                    return;
1143    
1144          if (!cliprdr_init())          if (!cliprdr_init())
1145                  return;                  return;
1146    
# Line 467  xclip_init(void) Line 1150  xclip_init(void)
1150          timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);          timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);
1151          rdesktop_clipboard_target_atom =          rdesktop_clipboard_target_atom =
1152                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
1153            rdesktop_primary_timestamp_target_atom =
1154                    XInternAtom(g_display, "_RDESKTOP_PRIMARY_TIMESTAMP_TARGET", False);
1155            rdesktop_clipboard_timestamp_target_atom =
1156                    XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TIMESTAMP_TARGET", False);
1157          incr_atom = XInternAtom(g_display, "INCR", False);          incr_atom = XInternAtom(g_display, "INCR", False);
1158          targets[0] = targets_atom;          format_string_atom = XInternAtom(g_display, "STRING", False);
1159          targets[1] = XInternAtom(g_display, "TEXT", False);          format_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False);
1160          targets[2] = XInternAtom(g_display, "STRING", False);          format_unicode_atom = XInternAtom(g_display, "text/unicode", False);
         targets[3] = XInternAtom(g_display, "text/unicode", False);  
         targets[4] = XInternAtom(g_display, "TIMESTAMP", False);  
         targets[5] = XA_STRING;  
1161    
1162          /* rdesktop sets _RDESKTOP_CLIPBOARD_FORMATS on the root window when acquiring the clipboard.          /* rdesktop sets _RDESKTOP_SELECTION_NOTIFY on the root window when acquiring the clipboard.
1163             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. */
1164            rdesktop_selection_notify_atom =
1165                    XInternAtom(g_display, "_RDESKTOP_SELECTION_NOTIFY", False);
1166            XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);
1167            probing_selections = False;
1168    
1169            rdesktop_native_atom = XInternAtom(g_display, "_RDESKTOP_NATIVE", False);
1170          rdesktop_clipboard_formats_atom =          rdesktop_clipboard_formats_atom =
1171                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
1172          XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);          rdesktop_primary_owner_atom = XInternAtom(g_display, "_RDESKTOP_PRIMARY_OWNER", False);
1173            rdesktop_clipboard_owner_atom = XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_OWNER", False);
1174    
1175            num_targets = 0;
1176            targets[num_targets++] = targets_atom;
1177            targets[num_targets++] = timestamp_atom;
1178            targets[num_targets++] = rdesktop_native_atom;
1179            targets[num_targets++] = rdesktop_clipboard_formats_atom;
1180    #ifdef USE_UNICODE_CLIPBOARD
1181            targets[num_targets++] = format_utf8_string_atom;
1182    #endif
1183            targets[num_targets++] = format_unicode_atom;
1184            targets[num_targets++] = format_string_atom;
1185            targets[num_targets++] = XA_STRING;
1186    }
1187    
1188    void
1189    xclip_deinit(void)
1190    {
1191            if (XGetSelectionOwner(g_display, primary_atom) == g_wnd)
1192                    XSetSelectionOwner(g_display, primary_atom, None, acquire_time);
1193            if (XGetSelectionOwner(g_display, clipboard_atom) == g_wnd)
1194                    XSetSelectionOwner(g_display, clipboard_atom, None, acquire_time);
1195            xclip_notify_change();
1196  }  }

Legend:
Removed from v.941  
changed lines
  Added in v.1404

  ViewVC Help
Powered by ViewVC 1.1.26