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

Legend:
Removed from v.435  
changed lines
  Added in v.1223

  ViewVC Help
Powered by ViewVC 1.1.26