/[rdesktop]/jpeg/rdesktop/trunk/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 /jpeg/rdesktop/trunk/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 1398 by stargo, Sun Mar 4 11:32:09 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  /* Translate LF to CR-LF. To do this, we must allocate more memory.
134     in-place. The length is updated. Handles embedded nulls */     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  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            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. */     The length is updated. */
190  static uint8 *  static uint8 *
191  lf2crlf(uint8 * data, uint32 * length)  lf2crlf(uint8 * data, uint32 * length)
# Line 82  lf2crlf(uint8 * data, uint32 * length) Line 211  lf2crlf(uint8 * data, uint32 * length)
211    
212          return result;          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 90  xclip_provide_selection(XSelectionReques Line 219  xclip_provide_selection(XSelectionReques
219  {  {
220          XEvent xev;          XEvent xev;
221    
222            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,          XChangeProperty(g_display, req->requestor, req->property,
225                          type, format, PropModeReplace, data, length);                          type, format, PropModeReplace, data, length);
226    
# Line 104  xclip_provide_selection(XSelectionReques Line 235  xclip_provide_selection(XSelectionReques
235          XSendEvent(g_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 RD_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            if ((!source) || source_size == 0)
293                    return False;
294    
295    #ifdef USE_UNICODE_CLIPBOARD
296            if (target == format_string_atom ||
297                target == format_unicode_atom || target == format_utf8_string_atom)
298            {
299                    size_t unicode_buffer_size;
300                    char *unicode_buffer;
301                    iconv_t cd;
302                    size_t unicode_buffer_size_remaining;
303                    char *unicode_buffer_remaining;
304                    char *data_remaining;
305                    size_t data_size_remaining;
306                    uint32 translated_data_size;
307                    uint8 *translated_data;
308    
309                    if (rdp_clipboard_request_format != RDP_CF_TEXT)
310                            return False;
311    
312                    /* Make an attempt to convert any string we send to Unicode.
313                       We don't know what the RDP server's ANSI Codepage is, or how to convert
314                       to it, so using CF_TEXT is not safe (and is unnecessary, since all
315                       WinNT versions are Unicode-minded).
316                     */
317                    if (target == format_string_atom)
318                    {
319                            char *locale_charset = nl_langinfo(CODESET);
320                            cd = iconv_open(WINDOWS_CODEPAGE, locale_charset);
321                            if (cd == (iconv_t) - 1)
322                            {
323                                    DEBUG_CLIPBOARD(("Locale charset %s not found in iconv. Unable to convert clipboard text.\n", locale_charset));
324                                    return False;
325                            }
326                            unicode_buffer_size = source_size * 4;
327                    }
328                    else if (target == format_unicode_atom)
329                    {
330                            cd = iconv_open(WINDOWS_CODEPAGE, "UCS-2");
331                            if (cd == (iconv_t) - 1)
332                            {
333                                    return False;
334                            }
335                            unicode_buffer_size = source_size;
336                    }
337                    else if (target == format_utf8_string_atom)
338                    {
339                            cd = iconv_open(WINDOWS_CODEPAGE, "UTF-8");
340                            if (cd == (iconv_t) - 1)
341                            {
342                                    return False;
343                            }
344                            /* UTF-8 is guaranteed to be less or equally compact
345                               as UTF-16 for all Unicode chars >=2 bytes.
346                             */
347                            unicode_buffer_size = source_size * 2;
348                    }
349                    else
350                    {
351                            return False;
352                    }
353    
354                    unicode_buffer = xmalloc(unicode_buffer_size);
355                    unicode_buffer_size_remaining = unicode_buffer_size;
356                    unicode_buffer_remaining = unicode_buffer;
357                    data_remaining = (char *) source;
358                    data_size_remaining = source_size;
359                    iconv(cd, (ICONV_CONST char **) &data_remaining, &data_size_remaining,
360                          &unicode_buffer_remaining, &unicode_buffer_size_remaining);
361                    iconv_close(cd);
362    
363                    /* translate linebreaks */
364                    translated_data_size = unicode_buffer_size - unicode_buffer_size_remaining;
365                    translated_data = utf16_lf2crlf((uint8 *) unicode_buffer, &translated_data_size);
366                    if (translated_data != NULL)
367                    {
368                            DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n",
369                                             translated_data_size));
370                            helper_cliprdr_send_response(translated_data, translated_data_size);
371                            xfree(translated_data); /* Not the same thing as XFree! */
372                    }
373    
374                    xfree(unicode_buffer);
375    
376                    return True;
377            }
378    #else
379            if (target == format_string_atom)
380            {
381                    uint8 *translated_data;
382                    uint32 length = source_size;
383    
384                    if (rdp_clipboard_request_format != RDP_CF_TEXT)
385                            return False;
386    
387                    DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
388                    translated_data = lf2crlf(source, &length);
389                    if (translated_data != NULL)
390                    {
391                            helper_cliprdr_send_response(translated_data, length);
392                            xfree(translated_data); /* Not the same thing as XFree! */
393                    }
394    
395                    return True;
396            }
397    #endif
398            else if (target == rdesktop_native_atom)
399            {
400                    helper_cliprdr_send_response(source, source_size + 1);
401    
402                    return True;
403            }
404            else
405            {
406                    return False;
407            }
408    }
409    
410    static void
411    xclip_clear_target_props()
412    {
413            XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
414            XDeleteProperty(g_display, g_wnd, rdesktop_primary_timestamp_target_atom);
415            XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_timestamp_target_atom);
416    }
417    
418    static void
419    xclip_notify_change()
420    {
421            XChangeProperty(g_display, DefaultRootWindow(g_display),
422                            rdesktop_selection_notify_atom, XA_INTEGER, 32, PropModeReplace, NULL, 0);
423    }
424    
425    static void
426    xclip_probe_selections()
427    {
428            Window primary_owner, clipboard_owner;
429    
430            if (probing_selections)
431            {
432                    DEBUG_CLIPBOARD(("Already probing selections. Scheduling reprobe.\n"));
433                    reprobe_selections = True;
434                    return;
435            }
436    
437            DEBUG_CLIPBOARD(("Probing selections.\n"));
438    
439            probing_selections = True;
440            reprobe_selections = False;
441    
442            xclip_clear_target_props();
443    
444            if (auto_mode)
445                    primary_owner = XGetSelectionOwner(g_display, primary_atom);
446            else
447                    primary_owner = None;
448    
449            clipboard_owner = XGetSelectionOwner(g_display, clipboard_atom);
450    
451            /* If we own all relevant selections then don't do anything. */
452            if (((primary_owner == g_wnd) || !auto_mode) && (clipboard_owner == g_wnd))
453                    goto end;
454    
455            /* Both available */
456            if ((primary_owner != None) && (clipboard_owner != None))
457            {
458                    primary_timestamp = 0;
459                    clipboard_timestamp = 0;
460                    XConvertSelection(g_display, primary_atom, timestamp_atom,
461                                      rdesktop_primary_timestamp_target_atom, g_wnd, CurrentTime);
462                    XConvertSelection(g_display, clipboard_atom, timestamp_atom,
463                                      rdesktop_clipboard_timestamp_target_atom, g_wnd, CurrentTime);
464                    return;
465            }
466    
467            /* Just PRIMARY */
468            if (primary_owner != None)
469            {
470                    XConvertSelection(g_display, primary_atom, targets_atom,
471                                      rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
472                    return;
473            }
474    
475            /* Just CLIPBOARD */
476            if (clipboard_owner != None)
477            {
478                    XConvertSelection(g_display, clipboard_atom, targets_atom,
479                                      rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
480                    return;
481            }
482    
483            DEBUG_CLIPBOARD(("No owner of any selection.\n"));
484    
485            /* FIXME:
486               Without XFIXES, we cannot reliably know the formats offered by an
487               upcoming selection owner, so we just lie about him offering
488               RDP_CF_TEXT. */
489            cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
490    
491          end:
492            probing_selections = False;
493    }
494    
495    /* This function is called for SelectionNotify events.
496       The SelectionNotify event is sent from the clipboard owner to the requestor
497       after his request was satisfied.
498       If this function is called, we're the requestor side. */
499    #ifndef MAKE_PROTO
500  void  void
501  xclip_handle_SelectionNotify(XSelectionEvent * event)  xclip_handle_SelectionNotify(XSelectionEvent * event)
502  {  {
503          unsigned long nitems, bytes_left;          unsigned long nitems, bytes_left;
504          XWindowAttributes wa;          XWindowAttributes wa;
505          Atom type, best_target, text_target;          Atom type;
506          Atom *supported_targets;          Atom *supported_targets;
507          int res, i, format;          int res, i, format;
508          uint8 *data;          uint8 *data = NULL;
509    
510          if (event->property == None)          if (event->property == None)
511                  goto fail;                  goto fail;
# Line 122  xclip_handle_SelectionNotify(XSelectionE Line 515  xclip_handle_SelectionNotify(XSelectionE
515                           XGetAtomName(g_display, event->target),                           XGetAtomName(g_display, event->target),
516                           XGetAtomName(g_display, event->property)));                           XGetAtomName(g_display, event->property)));
517    
518          if (event->property == None)          if (event->target == timestamp_atom)
519                  goto fail;          {
520                    if (event->selection == primary_atom)
521                    {
522                            res = XGetWindowProperty(g_display, g_wnd,
523                                                     rdesktop_primary_timestamp_target_atom, 0,
524                                                     XMaxRequestSize(g_display), False, AnyPropertyType,
525                                                     &type, &format, &nitems, &bytes_left, &data);
526                    }
527                    else
528                    {
529                            res = XGetWindowProperty(g_display, g_wnd,
530                                                     rdesktop_clipboard_timestamp_target_atom, 0,
531                                                     XMaxRequestSize(g_display), False, AnyPropertyType,
532                                                     &type, &format, &nitems, &bytes_left, &data);
533                    }
534    
535    
536                    if ((res != Success) || (nitems != 1) || (format != 32))
537                    {
538                            DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
539                            goto fail;
540                    }
541    
542                    if (event->selection == primary_atom)
543                    {
544                            primary_timestamp = *(Time *) data;
545                            if (primary_timestamp == 0)
546                                    primary_timestamp++;
547                            XDeleteProperty(g_display, g_wnd, rdesktop_primary_timestamp_target_atom);
548                            DEBUG_CLIPBOARD(("Got PRIMARY timestamp: %u\n",
549                                             (unsigned) primary_timestamp));
550                    }
551                    else
552                    {
553                            clipboard_timestamp = *(Time *) data;
554                            if (clipboard_timestamp == 0)
555                                    clipboard_timestamp++;
556                            XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_timestamp_target_atom);
557                            DEBUG_CLIPBOARD(("Got CLIPBOARD timestamp: %u\n",
558                                             (unsigned) clipboard_timestamp));
559                    }
560    
561                    XFree(data);
562    
563                    if (primary_timestamp && clipboard_timestamp)
564                    {
565                            if (primary_timestamp > clipboard_timestamp)
566                            {
567                                    DEBUG_CLIPBOARD(("PRIMARY is most recent selection.\n"));
568                                    XConvertSelection(g_display, primary_atom, targets_atom,
569                                                      rdesktop_clipboard_target_atom, g_wnd,
570                                                      event->time);
571                            }
572                            else
573                            {
574                                    DEBUG_CLIPBOARD(("CLIPBOARD is most recent selection.\n"));
575                                    XConvertSelection(g_display, clipboard_atom, targets_atom,
576                                                      rdesktop_clipboard_target_atom, g_wnd,
577                                                      event->time);
578                            }
579                    }
580    
581                    return;
582            }
583    
584            if (probing_selections && reprobe_selections)
585            {
586                    probing_selections = False;
587                    xclip_probe_selections();
588                    return;
589            }
590    
591          res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,          res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
592                                   0, XMaxRequestSize(g_display), True, AnyPropertyType,                                   0, XMaxRequestSize(g_display), False, AnyPropertyType,
593                                   &type, &format, &nitems, &bytes_left, &data);                                   &type, &format, &nitems, &bytes_left, &data);
594    
595            xclip_clear_target_props();
596    
597          if (res != Success)          if (res != Success)
598          {          {
599                  DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));                  DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
600                  goto fail;                  goto fail;
601          }          }
602    
603            if (type == incr_atom)
604            {
605                    DEBUG_CLIPBOARD(("Received INCR.\n"));
606    
607                    XGetWindowAttributes(g_display, g_wnd, &wa);
608                    if ((wa.your_event_mask | PropertyChangeMask) != wa.your_event_mask)
609                    {
610                            XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));
611                    }
612                    XFree(data);
613                    g_incr_target = event->target;
614                    g_waiting_for_INCR = 1;
615                    goto end;
616            }
617    
618          /* Negotiate target format */          /* Negotiate target format */
619          if (event->target == targets_atom)          if (event->target == targets_atom)
620          {          {
621                  /* FIXME: We should choose format here based on what the server wanted */                  /* Determine the best of text targets that we have available:
622                  best_target = XA_STRING;                     Prefer UTF8_STRING > text/unicode (unspecified encoding) > STRING
623                       (ignore TEXT and COMPOUND_TEXT because we don't have code to handle them)
624                     */
625                    int text_target_satisfaction = 0;
626                    Atom best_text_target = 0;      /* measures how much we're satisfied with what we found */
627                  if (type != None)                  if (type != None)
628                  {                  {
629                          supported_targets = (Atom *) data;                          supported_targets = (Atom *) data;
                         text_target = XInternAtom(g_display, "TEXT", False);  
630                          for (i = 0; i < nitems; i++)                          for (i = 0; i < nitems; i++)
631                          {                          {
632                                  DEBUG_CLIPBOARD(("Target %d: %s\n", i,                                  DEBUG_CLIPBOARD(("Target %d: %s\n", i,
633                                                   XGetAtomName(g_display, supported_targets[i])));                                                   XGetAtomName(g_display, supported_targets[i])));
634                                  if (supported_targets[i] == text_target)                                  if (supported_targets[i] == format_string_atom)
635                                    {
636                                            if (text_target_satisfaction < 1)
637                                            {
638                                                    DEBUG_CLIPBOARD(("Other party supports STRING, choosing that as best_target\n"));
639                                                    best_text_target = supported_targets[i];
640                                                    text_target_satisfaction = 1;
641                                            }
642                                    }
643    #ifdef USE_UNICODE_CLIPBOARD
644                                    else if (supported_targets[i] == format_unicode_atom)
645                                    {
646                                            if (text_target_satisfaction < 2)
647                                            {
648                                                    DEBUG_CLIPBOARD(("Other party supports text/unicode, choosing that as best_target\n"));
649                                                    best_text_target = supported_targets[i];
650                                                    text_target_satisfaction = 2;
651                                            }
652                                    }
653                                    else if (supported_targets[i] == format_utf8_string_atom)
654                                  {                                  {
655                                          DEBUG_CLIPBOARD(("Other party supports TEXT, choosing that as best_target\n"));                                          if (text_target_satisfaction < 3)
656                                          best_target = text_target;                                          {
657                                          break;                                                  DEBUG_CLIPBOARD(("Other party supports UTF8_STRING, choosing that as best_target\n"));
658                                                    best_text_target = supported_targets[i];
659                                                    text_target_satisfaction = 3;
660                                            }
661                                    }
662    #endif
663                                    else if (supported_targets[i] == rdesktop_clipboard_formats_atom)
664                                    {
665                                            if (probing_selections && (text_target_satisfaction < 4))
666                                            {
667                                                    DEBUG_CLIPBOARD(("Other party supports native formats, choosing that as best_target\n"));
668                                                    best_text_target = supported_targets[i];
669                                                    text_target_satisfaction = 4;
670                                            }
671                                  }                                  }
672                          }                          }
                         XFree(data);  
673                  }                  }
674    
675                  XConvertSelection(g_display, primary_atom, best_target,                  /* Kickstarting the next step in the process of satisfying RDP's
676                                    rdesktop_clipboard_target_atom, g_wnd, event->time);                     clipboard request -- specifically, requesting the actual clipboard data.
677                  return;                   */
678          }                  if ((best_text_target != 0)
679                        && (!probing_selections
680          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)  
681                  {                  {
682                          XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));                          XConvertSelection(g_display, event->selection, best_text_target,
683                                              rdesktop_clipboard_target_atom, g_wnd, event->time);
684                            goto end;
685                  }                  }
686                  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))  
687                  {                  {
688                          DEBUG_CLIPBOARD(("XGetWindowProperty failed.\n"));                          DEBUG_CLIPBOARD(("Unable to find a textual target to satisfy RDP clipboard text request\n"));
689                          goto fail;                          goto fail;
690                  }                  }
691                  else          }
692            else
693            {
694                    if (probing_selections)
695                  {                  {
696                          uint8 *translated_data;                          Window primary_owner, clipboard_owner;
                         uint32 length = nitems;  
   
                         translated_data = lf2crlf(data, &length);  
697    
698                          g_clip_buffer = (uint8 *) xmalloc(length);                          /* FIXME:
699                          strncpy((char *) g_clip_buffer, (char *) translated_data, length);                             Without XFIXES, we must make sure that the other
700                          xfree(translated_data);                             rdesktop owns all relevant selections or we might try
701                          g_clip_buflen = length;                             to get a native format from non-rdesktop window later
702                               on. */
703                          XFree(data);  
704                          return;                          clipboard_owner = XGetSelectionOwner(g_display, clipboard_atom);
705    
706                            if (auto_mode)
707                                    primary_owner = XGetSelectionOwner(g_display, primary_atom);
708                            else
709                                    primary_owner = clipboard_owner;
710    
711                            if (primary_owner != clipboard_owner)
712                                    goto fail;
713    
714                            DEBUG_CLIPBOARD(("Got fellow rdesktop formats\n"));
715                            probing_selections = False;
716                            rdesktop_is_selection_owner = True;
717                            cliprdr_send_native_format_announce(data, nitems);
718                    }
719                    else if (!xclip_send_data_with_convert(data, nitems, event->target))
720                    {
721                            goto fail;
722                  }                  }
723          }          }
724    
725          /* Translate linebreaks, but only if not getting data from        end:
726             other rdesktop instance */          if (data)
727          if (event->target != rdesktop_clipboard_formats_atom)                  XFree(data);
         {  
                 uint8 *translated_data;  
                 uint32 length = nitems;  
728    
729                  DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));          return;
730                  translated_data = lf2crlf(data, &length);  
731                  cliprdr_send_data(translated_data, length + 1);        fail:
732                  xfree(translated_data); /* Not the same thing as XFree! */          xclip_clear_target_props();
733            if (probing_selections)
734            {
735                    DEBUG_CLIPBOARD(("Unable to find suitable target. Using default text format.\n"));
736                    probing_selections = False;
737                    rdesktop_is_selection_owner = False;
738    
739                    /* FIXME:
740                       Without XFIXES, we cannot reliably know the formats offered by an
741                       upcoming selection owner, so we just lie about him offering
742                       RDP_CF_TEXT. */
743                    cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
744          }          }
745          else          else
746          {          {
747                  cliprdr_send_data(data, nitems + 1);                  helper_cliprdr_send_empty_response();
748          }          }
749          XFree(data);          goto end;
   
         if (!rdesktop_is_selection_owner)  
                 cliprdr_send_text_format_announce();  
         return;  
   
       fail:  
         cliprdr_send_data(NULL, 0);  
750  }  }
751    
752    /* This function is called for SelectionRequest events.
753       The SelectionRequest event is sent from the requestor to the clipboard owner
754       to request clipboard data.
755     */
756  void  void
757  xclip_handle_SelectionRequest(XSelectionRequestEvent * event)  xclip_handle_SelectionRequest(XSelectionRequestEvent * event)
758  {  {
759          unsigned long nitems, bytes_left;          unsigned long nitems, bytes_left;
760          unsigned char *prop_return;          unsigned char *prop_return = NULL;
         uint32 *wanted_format;  
761          int format, res;          int format, res;
762          Atom type;          Atom type;
763    
# Line 242  xclip_handle_SelectionRequest(XSelection Line 768  xclip_handle_SelectionRequest(XSelection
768    
769          if (event->target == targets_atom)          if (event->target == targets_atom)
770          {          {
771                  xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, NUM_TARGETS);                  xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, num_targets);
772                  return;                  return;
773          }          }
774          else if (event->target == timestamp_atom)          else if (event->target == timestamp_atom)
775          {          {
776                  xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);                  xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & acquire_time, 1);
777                  return;                  return;
778          }          }
779          else if (event->target == rdesktop_clipboard_formats_atom)          else if (event->target == rdesktop_clipboard_formats_atom)
780          {          {
781                  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? */  
782          }          }
783          else          else
784          {          {
785                  format = CF_TEXT;                  /* All the following targets require an async operation with the RDP server
786          }                     and currently we don't do X clipboard request queueing so we can only
787                       handle one such request at a time. */
788                    if (has_selection_request)
789                    {
790                            DEBUG_CLIPBOARD(("Error: Another clipboard request was already sent to the RDP server and not yet responded. Refusing this request.\n"));
791                            xclip_refuse_selection(event);
792                            return;
793                    }
794                    if (event->target == rdesktop_native_atom)
795                    {
796                            /* Before the requestor makes a request for the _RDESKTOP_NATIVE target,
797                               he should declare requestor[property] = CF_SOMETHING. */
798                            res = XGetWindowProperty(g_display, event->requestor,
799                                                     event->property, 0, 1, True,
800                                                     XA_INTEGER, &type, &format, &nitems, &bytes_left,
801                                                     &prop_return);
802                            if (res != Success || (!prop_return))
803                            {
804                                    DEBUG_CLIPBOARD(("Requested native format but didn't specifiy which.\n"));
805                                    xclip_refuse_selection(event);
806                                    return;
807                            }
808    
809                            format = *(uint32 *) prop_return;
810                            XFree(prop_return);
811                    }
812                    else if (event->target == format_string_atom || event->target == XA_STRING)
813                    {
814                            /* STRING and XA_STRING are defined to be ISO8859-1 */
815                            format = CF_TEXT;
816                    }
817                    else if (event->target == format_utf8_string_atom)
818                    {
819    #ifdef USE_UNICODE_CLIPBOARD
820                            format = CF_UNICODETEXT;
821    #else
822                            DEBUG_CLIPBOARD(("Requested target unavailable due to lack of Unicode support. (It was not in TARGETS, so why did you ask for it?!)\n"));
823                            xclip_refuse_selection(event);
824                            return;
825    #endif
826                    }
827                    else if (event->target == format_unicode_atom)
828                    {
829                            /* Assuming text/unicode to be UTF-16 */
830                            format = CF_UNICODETEXT;
831                    }
832                    else
833                    {
834                            DEBUG_CLIPBOARD(("Requested target unavailable. (It was not in TARGETS, so why did you ask for it?!)\n"));
835                            xclip_refuse_selection(event);
836                            return;
837                    }
838    
839          cliprdr_send_data_request(format);                  cliprdr_send_data_request(format);
840          selection_request = *event;                  selection_request = *event;
841          /* wait for data */                  has_selection_request = True;
842                    return;         /* wait for data */
843            }
844  }  }
845    
846    /* While this rdesktop holds ownership over the clipboard, it means the clipboard data
847       is offered by the RDP server (and when it is pasted inside RDP, there's no network
848       roundtrip).
849    
850       This event (SelectionClear) symbolizes this rdesktop lost onwership of the clipboard
851       to some other X client. We should find out what clipboard formats this other
852       client offers and announce that to RDP. */
853  void  void
854  xclip_handle_SelectionClear(void)  xclip_handle_SelectionClear(void)
855  {  {
856          DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));          DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
857          have_primary = 0;          xclip_notify_change();
858          XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);          xclip_probe_selections();
         cliprdr_send_text_format_announce();  
859  }  }
860    
861    /* Called when any property changes in our window or the root window. */
862  void  void
863  xclip_handle_PropertyNotify(XPropertyEvent * event)  xclip_handle_PropertyNotify(XPropertyEvent * event)
864  {  {
865          unsigned long nitems, bytes_left;          unsigned long nitems;
866          int format, res;          unsigned long offset = 0;
867            unsigned long bytes_left = 1;
868            int format;
869          XWindowAttributes wa;          XWindowAttributes wa;
870          uint8 *data;          uint8 *data;
871          Atom type;          Atom type;
# Line 290  xclip_handle_PropertyNotify(XPropertyEve Line 873  xclip_handle_PropertyNotify(XPropertyEve
873          if (event->state == PropertyNewValue && g_waiting_for_INCR)          if (event->state == PropertyNewValue && g_waiting_for_INCR)
874          {          {
875                  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;  
                 }  
876    
877                  if (nitems == 0)                  while (bytes_left > 0)
878                  {                  {
879                          XGetWindowAttributes(g_display, g_wnd, &wa);                          /* Unlike the specification, we don't set the 'delete' arugment to True
880                          XSelectInput(g_display, g_wnd, (wa.your_event_mask ^ PropertyChangeMask));                             since we slurp the INCR's chunks in even-smaller chunks of 4096 bytes. */
881                          XFree(data);                          if ((XGetWindowProperty
882                          g_waiting_for_INCR = 0;                               (g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,
883                                  False, AnyPropertyType, &type, &format, &nitems, &bytes_left,
884                          if (g_clip_buflen > 0)                                &data) != Success))
885                          {                          {
886                                  cliprdr_send_data(g_clip_buffer, g_clip_buflen + 1);                                  XFree(data);
887                                    return;
                                 if (!rdesktop_is_selection_owner)  
                                         cliprdr_send_text_format_announce();  
   
                                 xfree(g_clip_buffer);  
                                 g_clip_buffer = 0;  
                                 g_clip_buflen = 0;  
888                          }                          }
                 }  
                 else  
                 {  
                         uint8 *translated_data;  
                         uint32 length = nitems;  
                         uint8 *tmp;  
889    
890                          DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));                          if (nitems == 0)
891                          translated_data = lf2crlf(data, &length);                          {
892                                    /* INCR transfer finished */
893                          tmp = xmalloc(length + g_clip_buflen);                                  XGetWindowAttributes(g_display, g_wnd, &wa);
894                          strncpy((char *) tmp, (char *) g_clip_buffer, g_clip_buflen);                                  XSelectInput(g_display, g_wnd,
895                          xfree(g_clip_buffer);                                               (wa.your_event_mask ^ PropertyChangeMask));
896                                    XFree(data);
897                          strncpy((char *) (tmp + g_clip_buflen), (char *) translated_data, length);                                  g_waiting_for_INCR = 0;
                         xfree(translated_data);  
898    
899                          g_clip_buffer = tmp;                                  if (g_clip_buflen > 0)
900                          g_clip_buflen += length;                                  {
901                                            if (!xclip_send_data_with_convert
902                                                (g_clip_buffer, g_clip_buflen, g_incr_target))
903                                            {
904                                                    helper_cliprdr_send_empty_response();
905                                            }
906                                            xfree(g_clip_buffer);
907                                            g_clip_buffer = NULL;
908                                            g_clip_buflen = 0;
909                                    }
910                            }
911                            else
912                            {
913                                    /* Another chunk in the INCR transfer */
914                                    offset += (nitems / 4); /* offset at which to begin the next slurp */
915                                    g_clip_buffer = xrealloc(g_clip_buffer, g_clip_buflen + nitems);
916                                    memcpy(g_clip_buffer + g_clip_buflen, data, nitems);
917                                    g_clip_buflen += nitems;
918    
919                          XFree(data);                                  XFree(data);
920                          return;                          }
921                  }                  }
922          }                  XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
   
         if (event->atom != rdesktop_clipboard_formats_atom)  
923                  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;  
                 }  
924          }          }
925    
926          /* PropertyDelete, or XGetWindowProperty failed */          if ((event->atom == rdesktop_selection_notify_atom) &&
927          cliprdr_send_text_format_announce();              (event->window == DefaultRootWindow(g_display)))
928          rdesktop_is_selection_owner = 0;                  xclip_probe_selections();
929  }  }
930    #endif
931    
932    
933    /* Called when the RDP server announces new clipboard data formats.
934       In response, we:
935       - take ownership over the clipboard
936       - declare those formats in their Windows native form
937         to other rdesktop instances on this X server */
938  void  void
939  ui_clip_format_announce(uint8 * data, uint32 length)  ui_clip_format_announce(uint8 * data, uint32 length)
940  {  {
941          XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime);          acquire_time = g_last_gesturetime;
942    
943            XSetSelectionOwner(g_display, primary_atom, g_wnd, acquire_time);
944          if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)          if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
         {  
945                  warning("Failed to aquire ownership of PRIMARY clipboard\n");                  warning("Failed to aquire ownership of PRIMARY clipboard\n");
                 return;  
         }  
946    
947          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);  
948          if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)          if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)
949                  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");                  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
 }  
950    
951            if (formats_data)
952                    xfree(formats_data);
953            formats_data = xmalloc(length);
954            memcpy(formats_data, data, length);
955            formats_data_length = length;
956    
957            xclip_notify_change();
958    }
959    
960    /* Called when the RDP server responds with clipboard data (after we've requested it). */
961  void  void
962  ui_clip_handle_data(uint8 * data, uint32 length)  ui_clip_handle_data(uint8 * data, uint32 length)
963  {  {
964          if (selection_request.target != rdesktop_clipboard_formats_atom)          RD_BOOL free_data = False;
965    
966            if (length == 0)
967          {          {
968                    xclip_refuse_selection(&selection_request);
969                    has_selection_request = False;
970                    return;
971            }
972    
973            if (selection_request.target == format_string_atom || selection_request.target == XA_STRING)
974            {
975                    /* We're expecting a CF_TEXT response */
976                  uint8 *firstnull;                  uint8 *firstnull;
977    
978                  /* translate linebreaks */                  /* translate linebreaks */
# Line 406  ui_clip_handle_data(uint8 * data, uint32 Line 985  ui_clip_handle_data(uint8 * data, uint32
985                          length = firstnull - data + 1;                          length = firstnull - data + 1;
986                  }                  }
987          }          }
988    #ifdef USE_UNICODE_CLIPBOARD
989            else if (selection_request.target == format_utf8_string_atom)
990            {
991                    /* We're expecting a CF_UNICODETEXT response */
992                    iconv_t cd = iconv_open("UTF-8", WINDOWS_CODEPAGE);
993                    if (cd != (iconv_t) - 1)
994                    {
995                            size_t utf8_length = length * 2;
996                            char *utf8_data = malloc(utf8_length);
997                            size_t utf8_length_remaining = utf8_length;
998                            char *utf8_data_remaining = utf8_data;
999                            char *data_remaining = (char *) data;
1000                            size_t length_remaining = (size_t) length;
1001                            if (utf8_data == NULL)
1002                            {
1003                                    iconv_close(cd);
1004                                    return;
1005                            }
1006                            iconv(cd, (ICONV_CONST char **) &data_remaining, &length_remaining,
1007                                  &utf8_data_remaining, &utf8_length_remaining);
1008                            iconv_close(cd);
1009                            free_data = True;
1010                            data = (uint8 *) utf8_data;
1011                            length = utf8_length - utf8_length_remaining;
1012                    }
1013            }
1014            else if (selection_request.target == format_unicode_atom)
1015            {
1016                    /* We're expecting a CF_UNICODETEXT response, so what we're
1017                       receiving matches our requirements and there's no need
1018                       for further conversions. */
1019            }
1020    #endif
1021            else if (selection_request.target == rdesktop_native_atom)
1022            {
1023                    /* Pass as-is */
1024            }
1025            else
1026            {
1027                    DEBUG_CLIPBOARD(("ui_clip_handle_data: BUG! I don't know how to convert selection target %s!\n", XGetAtomName(g_display, selection_request.target)));
1028                    xclip_refuse_selection(&selection_request);
1029                    has_selection_request = False;
1030                    return;
1031            }
1032    
1033            xclip_provide_selection(&selection_request, selection_request.target, 8, data, length - 1);
1034            has_selection_request = False;
1035    
1036          xclip_provide_selection(&selection_request, XA_STRING, 8, data, length - 1);          if (free_data)
1037                    free(data);
1038    }
1039    
1040    void
1041    ui_clip_request_failed()
1042    {
1043            xclip_refuse_selection(&selection_request);
1044            has_selection_request = False;
1045  }  }
1046    
1047  void  void
1048  ui_clip_request_data(uint32 format)  ui_clip_request_data(uint32 format)
1049  {  {
1050          Window selectionowner;          Window primary_owner, clipboard_owner;
1051    
1052          DEBUG_CLIPBOARD(("Request from server for format %d\n", format));          DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
1053            rdp_clipboard_request_format = format;
1054    
1055            if (probing_selections)
1056            {
1057                    DEBUG_CLIPBOARD(("ui_clip_request_data: Selection probe in progress. Cannot handle request.\n"));
1058                    helper_cliprdr_send_empty_response();
1059                    return;
1060            }
1061    
1062            xclip_clear_target_props();
1063    
1064          if (rdesktop_is_selection_owner)          if (rdesktop_is_selection_owner)
1065          {          {
1066                  XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,                  XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
1067                                  XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);                                  XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
1068    
1069                  XConvertSelection(g_display, primary_atom, rdesktop_clipboard_formats_atom,                  XConvertSelection(g_display, primary_atom, rdesktop_native_atom,
1070                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
1071                  return;                  return;
1072          }          }
1073    
1074          selectionowner = XGetSelectionOwner(g_display, primary_atom);          if (auto_mode)
1075          if (selectionowner != None)                  primary_owner = XGetSelectionOwner(g_display, primary_atom);
1076            else
1077                    primary_owner = None;
1078    
1079            clipboard_owner = XGetSelectionOwner(g_display, clipboard_atom);
1080    
1081            /* Both available */
1082            if ((primary_owner != None) && (clipboard_owner != None))
1083            {
1084                    primary_timestamp = 0;
1085                    clipboard_timestamp = 0;
1086                    XConvertSelection(g_display, primary_atom, timestamp_atom,
1087                                      rdesktop_primary_timestamp_target_atom, g_wnd, CurrentTime);
1088                    XConvertSelection(g_display, clipboard_atom, timestamp_atom,
1089                                      rdesktop_clipboard_timestamp_target_atom, g_wnd, CurrentTime);
1090                    return;
1091            }
1092    
1093            /* Just PRIMARY */
1094            if (primary_owner != None)
1095          {          {
1096                  XConvertSelection(g_display, primary_atom, targets_atom,                  XConvertSelection(g_display, primary_atom, targets_atom,
1097                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
1098                  return;                  return;
1099          }          }
1100    
1101          /* No PRIMARY, try CLIPBOARD */          /* Just CLIPBOARD */
1102          selectionowner = XGetSelectionOwner(g_display, clipboard_atom);          if (clipboard_owner != None)
         if (selectionowner != None)  
1103          {          {
1104                  XConvertSelection(g_display, clipboard_atom, targets_atom,                  XConvertSelection(g_display, clipboard_atom, targets_atom,
1105                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
# Line 445  ui_clip_request_data(uint32 format) Line 1107  ui_clip_request_data(uint32 format)
1107          }          }
1108    
1109          /* No data available */          /* No data available */
1110          cliprdr_send_data(NULL, 0);          helper_cliprdr_send_empty_response();
1111  }  }
1112    
1113  void  void
1114  ui_clip_sync(void)  ui_clip_sync(void)
1115  {  {
1116          cliprdr_send_text_format_announce();          xclip_probe_selections();
1117  }  }
1118    
1119    void
1120    ui_clip_set_mode(const char *optarg)
1121    {
1122            g_rdpclip = True;
1123    
1124            if (str_startswith(optarg, "PRIMARYCLIPBOARD"))
1125                    auto_mode = True;
1126            else if (str_startswith(optarg, "CLIPBOARD"))
1127                    auto_mode = False;
1128            else
1129            {
1130                    warning("Invalid clipboard mode '%s'.\n", optarg);
1131                    g_rdpclip = False;
1132            }
1133    }
1134    
1135  void  void
1136  xclip_init(void)  xclip_init(void)
1137  {  {
1138            if (!g_rdpclip)
1139                    return;
1140    
1141          if (!cliprdr_init())          if (!cliprdr_init())
1142                  return;                  return;
1143    
# Line 467  xclip_init(void) Line 1147  xclip_init(void)
1147          timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);          timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);
1148          rdesktop_clipboard_target_atom =          rdesktop_clipboard_target_atom =
1149                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
1150            rdesktop_primary_timestamp_target_atom =
1151                    XInternAtom(g_display, "_RDESKTOP_PRIMARY_TIMESTAMP_TARGET", False);
1152            rdesktop_clipboard_timestamp_target_atom =
1153                    XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TIMESTAMP_TARGET", False);
1154          incr_atom = XInternAtom(g_display, "INCR", False);          incr_atom = XInternAtom(g_display, "INCR", False);
1155          targets[0] = targets_atom;          format_string_atom = XInternAtom(g_display, "STRING", False);
1156          targets[1] = XInternAtom(g_display, "TEXT", False);          format_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False);
1157          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;  
1158    
1159          /* 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.
1160             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. */
1161            rdesktop_selection_notify_atom =
1162                    XInternAtom(g_display, "_RDESKTOP_SELECTION_NOTIFY", False);
1163            XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);
1164            probing_selections = False;
1165    
1166            rdesktop_native_atom = XInternAtom(g_display, "_RDESKTOP_NATIVE", False);
1167          rdesktop_clipboard_formats_atom =          rdesktop_clipboard_formats_atom =
1168                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
1169          XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);          rdesktop_primary_owner_atom = XInternAtom(g_display, "_RDESKTOP_PRIMARY_OWNER", False);
1170            rdesktop_clipboard_owner_atom = XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_OWNER", False);
1171    
1172            num_targets = 0;
1173            targets[num_targets++] = targets_atom;
1174            targets[num_targets++] = timestamp_atom;
1175            targets[num_targets++] = rdesktop_native_atom;
1176            targets[num_targets++] = rdesktop_clipboard_formats_atom;
1177    #ifdef USE_UNICODE_CLIPBOARD
1178            targets[num_targets++] = format_utf8_string_atom;
1179    #endif
1180            targets[num_targets++] = format_unicode_atom;
1181            targets[num_targets++] = format_string_atom;
1182            targets[num_targets++] = XA_STRING;
1183    }
1184    
1185    void
1186    xclip_deinit(void)
1187    {
1188            if (XGetSelectionOwner(g_display, primary_atom) == g_wnd)
1189                    XSetSelectionOwner(g_display, primary_atom, None, acquire_time);
1190            if (XGetSelectionOwner(g_display, clipboard_atom) == g_wnd)
1191                    XSetSelectionOwner(g_display, clipboard_atom, None, acquire_time);
1192            xclip_notify_change();
1193  }  }

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

  ViewVC Help
Powered by ViewVC 1.1.26