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

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

  ViewVC Help
Powered by ViewVC 1.1.26