/[rdesktop]/sourceforge.net/trunk/rdesktop/xclip.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /sourceforge.net/trunk/rdesktop/xclip.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1211 by ossman_, Mon Mar 27 12:29:29 2006 UTC revision 1404 by astrand, Wed Apr 25 12:44:26 2007 UTC
# Line 1  Line 1 
1  /* -*- c-basic-offset: 8 -*-  /* -*- c-basic-offset: 8 -*-
2     rdesktop: A Remote Desktop Protocol client.     rdesktop: A Remote Desktop Protocol client.
3     Protocol services - Clipboard functions     Protocol services - Clipboard functions
4     Copyright (C) Erik Forsberg <forsberg@cendio.se> 2003     Copyright (C) Erik Forsberg <forsberg@cendio.se> 2003-2007
5     Copyright (C) Matthew Chapman 2003     Copyright (C) Matthew Chapman 2003-2007
6    
7     This program is free software; you can redistribute it and/or modify     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by     it under the terms of the GNU General Public License as published by
# Line 48  Line 48 
48  #define RDP_CF_TEXT CF_TEXT  #define RDP_CF_TEXT CF_TEXT
49  #endif  #endif
50    
51  #define MAX_TARGETS 7  #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;  extern RD_BOOL g_rdpclip;
57    
58  /* Mode of operation.  /* Mode of operation.
59     - Auto: Look at both PRIMARY and CLIPBOARD and use the most recent.     - Auto: Look at both PRIMARY and CLIPBOARD and use the most recent.
60     - Non-auto: Look at just CLIPBOARD. */     - Non-auto: Look at just CLIPBOARD. */
61  static BOOL auto_mode = True;  static RD_BOOL auto_mode = True;
62  /* Atoms of the two X selections we're dealing with: CLIPBOARD (explicit-copy) and PRIMARY (selection-copy) */  /* Atoms of the two X selections we're dealing with: CLIPBOARD (explicit-copy) and PRIMARY (selection-copy) */
63  static Atom clipboard_atom, primary_atom;  static Atom clipboard_atom, primary_atom;
64  /* Atom of the TARGETS clipboard target */  /* Atom of the TARGETS clipboard target */
65  static Atom targets_atom;  static Atom targets_atom;
66  /* Atom of the TIMESTAMP clipboard target */  /* Atom of the TIMESTAMP clipboard target */
67  static Atom timestamp_atom;  static Atom timestamp_atom;
68  /* Atom _RDESKTOP_CLIPBOARD_TARGET which has multiple uses:  /* Atom _RDESKTOP_CLIPBOARD_TARGET which is used as the 'property' argument in
69     - The 'property' argument in XConvertSelection calls: This is the property of our     XConvertSelection calls: This is the property of our window into which
70       window into which XConvertSelection will store the received clipboard data.     XConvertSelection will store the received clipboard data. */
    - In a clipboard request of target _RDESKTOP_CLIPBOARD_FORMATS, an XA_INTEGER-typed  
      property carrying the Windows native (CF_...) format desired by the requestor.  
      Requestor set this property (e.g. requestor_wnd[_RDESKTOP_CLIPBOARD_TARGET] = CF_TEXT)  
      before requesting clipboard data from a fellow rdesktop using  
      the _RDESKTOP_CLIPBOARD_FORMATS target. */  
71  static Atom rdesktop_clipboard_target_atom;  static Atom rdesktop_clipboard_target_atom;
72  /* Atoms _RDESKTOP_PRIMARY_TIMESTAMP_TARGET and _RDESKTOP_CLIPBOARD_TIMESTAMP_TARGET  /* 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.     are used to store the timestamps for when a window got ownership of the selections.
# Line 80  static Atom rdesktop_clipboard_target_at Line 75  static Atom rdesktop_clipboard_target_at
75  static Atom rdesktop_primary_timestamp_target_atom, rdesktop_clipboard_timestamp_target_atom;  static Atom rdesktop_primary_timestamp_target_atom, rdesktop_clipboard_timestamp_target_atom;
76  /* Storage for timestamps since we get them in two separate notifications. */  /* Storage for timestamps since we get them in two separate notifications. */
77  static Time primary_timestamp, clipboard_timestamp;  static Time primary_timestamp, clipboard_timestamp;
78  /* Atom _RDESKTOP_CLIPBOARD_FORMATS which has multiple uses:  /* Clipboard target for getting a list of native Windows clipboard formats. The
79     - The clipboard target (X jargon for "clipboard format") for rdesktop-to-rdesktop interchange     presence of this target indicates that the selection owner is another rdesktop. */
      of Windows native clipboard data.  
      This target cannot be used standalone; the requestor must keep the  
      _RDESKTOP_CLIPBOARD_TARGET property on his window denoting  
      the Windows native clipboard format being requested.  
    - The root window property set by rdesktop when it owns the clipboard,  
      denoting all Windows native clipboard formats it offers via  
      requests of the _RDESKTOP_CLIPBOARD_FORMATS target. */  
80  static Atom rdesktop_clipboard_formats_atom;  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;  static Atom format_string_atom, format_utf8_string_atom, format_unicode_atom;
100  /* Atom of the INCR clipboard type (see ICCCM on "INCR Properties") */  /* Atom of the INCR clipboard type (see ICCCM on "INCR Properties") */
101  static Atom incr_atom;  static Atom incr_atom;
# Line 99  static Atom incr_atom; Line 105  static Atom incr_atom;
105     the context to proceed. */     the context to proceed. */
106  static XSelectionRequestEvent selection_request;  static XSelectionRequestEvent selection_request;
107  /* Denotes we have a pending selection request. */  /* Denotes we have a pending selection request. */
108  static Bool has_selection_request;  static RD_BOOL has_selection_request;
109  /* Stores the clipboard format (CF_TEXT, CF_UNICODETEXT etc.) requested in the last  /* 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).     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     When we receive this data from whatever X client offering it, this variable gives us
# Line 109  static int rdp_clipboard_request_format; Line 115  static int rdp_clipboard_request_format;
115  /* Array of offered clipboard targets that will be sent to fellow X clients upon a TARGETS request. */  /* Array of offered clipboard targets that will be sent to fellow X clients upon a TARGETS request. */
116  static Atom targets[MAX_TARGETS];  static Atom targets[MAX_TARGETS];
117  static int num_targets;  static int num_targets;
 /* Denotes that this client currently holds the PRIMARY selection. */  
 static int have_primary = 0;  
118  /* Denotes that an rdesktop (not this rdesktop) is owning the selection,  /* Denotes that an rdesktop (not this rdesktop) is owning the selection,
119     allowing us to interchange Windows native clipboard data directly. */     allowing us to interchange Windows native clipboard data directly. */
120  static int rdesktop_is_selection_owner = 0;  static RD_BOOL rdesktop_is_selection_owner = False;
121  /* Time when we acquired the selection. */  /* Time when we acquired the selection. */
122  static Time acquire_time = 0;  static Time acquire_time = 0;
123    
# Line 126  static uint8 *g_clip_buffer = 0; Line 130  static uint8 *g_clip_buffer = 0;
130  /* Denotes the size of g_clip_buffer. */  /* Denotes the size of g_clip_buffer. */
131  static uint32 g_clip_buflen = 0;  static uint32 g_clip_buflen = 0;
132    
133  /* Translate LF to CR-LF. To do this, we must allocate more memory.  /* Translates CR-LF to LF.
134     The returned string is null-terminated, as required by CF_TEXT.     Changes the string in-place.
135     Does not stop on embedded nulls.     Does not stop on embedded nulls.
136     The length is updated. */     The length is updated. */
137  static void  static void
# Line 153  utf16_lf2crlf(uint8 * data, uint32 * siz Line 157  utf16_lf2crlf(uint8 * data, uint32 * siz
157  {  {
158          uint8 *result;          uint8 *result;
159          uint16 *inptr, *outptr;          uint16 *inptr, *outptr;
160            RD_BOOL swap_endianess;
161    
162          /* Worst case: Every char is LF */          /* Worst case: Every char is LF */
163          result = xmalloc((*size * 2) + 2);          result = xmalloc((*size * 2) + 2);
# Line 163  utf16_lf2crlf(uint8 * data, uint32 * siz Line 168  utf16_lf2crlf(uint8 * data, uint32 * siz
168          outptr = (uint16 *) result;          outptr = (uint16 *) result;
169    
170          /* Check for a reversed BOM */          /* Check for a reversed BOM */
171          Bool swap_endianess = (*inptr == 0xfffe);          swap_endianess = (*inptr == 0xfffe);
172    
173            uint16 uvalue_previous = 0;     /* Kept so we'll avoid translating CR-LF to CR-CR-LF */
174          while ((uint8 *) inptr < data + *size)          while ((uint8 *) inptr < data + *size)
175          {          {
176                  uint16 uvalue = *inptr;                  uint16 uvalue = *inptr;
177                  if (swap_endianess)                  if (swap_endianess)
178                          uvalue = ((uvalue << 8) & 0xff00) + (uvalue >> 8);                          uvalue = ((uvalue << 8) & 0xff00) + (uvalue >> 8);
179                  if (uvalue == 0x0a)                  if ((uvalue == 0x0a) && (uvalue_previous != 0x0d))
180                          *outptr++ = swap_endianess ? 0x0d00 : 0x0d;                          *outptr++ = swap_endianess ? 0x0d00 : 0x0d;
181                    uvalue_previous = uvalue;
182                  *outptr++ = *inptr++;                  *outptr++ = *inptr++;
183          }          }
184          *outptr++ = 0;          /* null termination */          *outptr++ = 0;          /* null termination */
# Line 193  lf2crlf(uint8 * data, uint32 * length) Line 200  lf2crlf(uint8 * data, uint32 * length)
200          p = data;          p = data;
201          o = result;          o = result;
202    
203            uint8 previous = '\0';  /* Kept to avoid translating CR-LF to CR-CR-LF */
204          while (p < data + *length)          while (p < data + *length)
205          {          {
206                  if (*p == '\x0a')                  if ((*p == '\x0a') && (previous != '\x0d'))
207                          *o++ = '\x0d';                          *o++ = '\x0d';
208                    previous = *p;
209                  *o++ = *p++;                  *o++ = *p++;
210          }          }
211          *length = o - result;          *length = o - result;
# Line 214  xclip_provide_selection(XSelectionReques Line 223  xclip_provide_selection(XSelectionReques
223  {  {
224          XEvent xev;          XEvent xev;
225    
226            DEBUG_CLIPBOARD(("xclip_provide_selection: requestor=0x%08x, target=%s, property=%s, length=%u\n", (unsigned) req->requestor, XGetAtomName(g_display, req->target), XGetAtomName(g_display, req->property), (unsigned) length));
227    
228          XChangeProperty(g_display, req->requestor, req->property,          XChangeProperty(g_display, req->requestor, req->property,
229                          type, format, PropModeReplace, data, length);                          type, format, PropModeReplace, data, length);
230    
# Line 236  xclip_refuse_selection(XSelectionRequest Line 247  xclip_refuse_selection(XSelectionRequest
247  {  {
248          XEvent xev;          XEvent xev;
249    
250            DEBUG_CLIPBOARD(("xclip_refuse_selection: requestor=0x%08x, target=%s, property=%s\n",
251                             (unsigned) req->requestor, XGetAtomName(g_display, req->target),
252                             XGetAtomName(g_display, req->property)));
253    
254          xev.xselection.type = SelectionNotify;          xev.xselection.type = SelectionNotify;
255          xev.xselection.serial = 0;          xev.xselection.serial = 0;
256          xev.xselection.send_event = True;          xev.xselection.send_event = True;
# Line 272  helper_cliprdr_send_empty_response() Line 287  helper_cliprdr_send_empty_response()
287  /* Replies with clipboard data to RDP, converting it from the target format  /* Replies with clipboard data to RDP, converting it from the target format
288     to the expected RDP format as necessary. Returns true if data was sent.     to the expected RDP format as necessary. Returns true if data was sent.
289   */   */
290  static Bool  static RD_BOOL
291  xclip_send_data_with_convert(uint8 * source, size_t source_size, Atom target)  xclip_send_data_with_convert(uint8 * source, size_t source_size, Atom target)
292  {  {
293            DEBUG_CLIPBOARD(("xclip_send_data_with_convert: target=%s, size=%u\n",
294                             XGetAtomName(g_display, target), (unsigned) source_size));
295    
296  #ifdef USE_UNICODE_CLIPBOARD  #ifdef USE_UNICODE_CLIPBOARD
297          if (target == format_string_atom ||          if (target == format_string_atom ||
298              target == format_unicode_atom || target == format_utf8_string_atom)              target == format_unicode_atom || target == format_utf8_string_atom)
299          {          {
300                    size_t unicode_buffer_size;
301                    char *unicode_buffer;
302                    iconv_t cd;
303                    size_t unicode_buffer_size_remaining;
304                    char *unicode_buffer_remaining;
305                    char *data_remaining;
306                    size_t data_size_remaining;
307                    uint32 translated_data_size;
308                    uint8 *translated_data;
309    
310                  if (rdp_clipboard_request_format != RDP_CF_TEXT)                  if (rdp_clipboard_request_format != RDP_CF_TEXT)
311                          return False;                          return False;
312    
# Line 287  xclip_send_data_with_convert(uint8 * sou Line 315  xclip_send_data_with_convert(uint8 * sou
315                     to it, so using CF_TEXT is not safe (and is unnecessary, since all                     to it, so using CF_TEXT is not safe (and is unnecessary, since all
316                     WinNT versions are Unicode-minded).                     WinNT versions are Unicode-minded).
317                   */                   */
                 size_t unicode_buffer_size;  
                 char *unicode_buffer;  
                 iconv_t cd;  
   
318                  if (target == format_string_atom)                  if (target == format_string_atom)
319                  {                  {
320                          char *locale_charset = nl_langinfo(CODESET);                          char *locale_charset = nl_langinfo(CODESET);
# Line 329  xclip_send_data_with_convert(uint8 * sou Line 353  xclip_send_data_with_convert(uint8 * sou
353                  }                  }
354    
355                  unicode_buffer = xmalloc(unicode_buffer_size);                  unicode_buffer = xmalloc(unicode_buffer_size);
356                  size_t unicode_buffer_size_remaining = unicode_buffer_size;                  unicode_buffer_size_remaining = unicode_buffer_size;
357                  char *unicode_buffer_remaining = unicode_buffer;                  unicode_buffer_remaining = unicode_buffer;
358                  char *data_remaining = (char *) source;                  data_remaining = (char *) source;
359                  size_t data_size_remaining = source_size;                  data_size_remaining = source_size;
360                  iconv(cd, (ICONV_CONST char **) &data_remaining, &data_size_remaining,                  iconv(cd, (ICONV_CONST char **) &data_remaining, &data_size_remaining,
361                        &unicode_buffer_remaining, &unicode_buffer_size_remaining);                        &unicode_buffer_remaining, &unicode_buffer_size_remaining);
362                  iconv_close(cd);                  iconv_close(cd);
363    
364                  /* translate linebreaks */                  /* translate linebreaks */
365                  uint32 translated_data_size = unicode_buffer_size - unicode_buffer_size_remaining;                  translated_data_size = unicode_buffer_size - unicode_buffer_size_remaining;
366                  uint8 *translated_data =                  translated_data = utf16_lf2crlf((uint8 *) unicode_buffer, &translated_data_size);
                         utf16_lf2crlf((uint8 *) unicode_buffer, &translated_data_size);  
367                  if (translated_data != NULL)                  if (translated_data != NULL)
368                  {                  {
369                          DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n",                          DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n",
# Line 373  xclip_send_data_with_convert(uint8 * sou Line 396  xclip_send_data_with_convert(uint8 * sou
396                  return True;                  return True;
397          }          }
398  #endif  #endif
399          else if (target == rdesktop_clipboard_formats_atom)          else if (target == rdesktop_native_atom)
400          {          {
401                  helper_cliprdr_send_response(source, source_size + 1);                  helper_cliprdr_send_response(source, source_size + 1);
402    
# Line 393  xclip_clear_target_props() Line 416  xclip_clear_target_props()
416          XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_timestamp_target_atom);          XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_timestamp_target_atom);
417  }  }
418    
419    static void
420    xclip_notify_change()
421    {
422            XChangeProperty(g_display, DefaultRootWindow(g_display),
423                            rdesktop_selection_notify_atom, XA_INTEGER, 32, PropModeReplace, NULL, 0);
424    }
425    
426    static void
427    xclip_probe_selections()
428    {
429            Window primary_owner, clipboard_owner;
430    
431            if (probing_selections)
432            {
433                    DEBUG_CLIPBOARD(("Already probing selections. Scheduling reprobe.\n"));
434                    reprobe_selections = True;
435                    return;
436            }
437    
438            DEBUG_CLIPBOARD(("Probing selections.\n"));
439    
440            probing_selections = True;
441            reprobe_selections = False;
442    
443            xclip_clear_target_props();
444    
445            if (auto_mode)
446                    primary_owner = XGetSelectionOwner(g_display, primary_atom);
447            else
448                    primary_owner = None;
449    
450            clipboard_owner = XGetSelectionOwner(g_display, clipboard_atom);
451    
452            /* If we own all relevant selections then don't do anything. */
453            if (((primary_owner == g_wnd) || !auto_mode) && (clipboard_owner == g_wnd))
454                    goto end;
455    
456            /* Both available */
457            if ((primary_owner != None) && (clipboard_owner != None))
458            {
459                    primary_timestamp = 0;
460                    clipboard_timestamp = 0;
461                    XConvertSelection(g_display, primary_atom, timestamp_atom,
462                                      rdesktop_primary_timestamp_target_atom, g_wnd, CurrentTime);
463                    XConvertSelection(g_display, clipboard_atom, timestamp_atom,
464                                      rdesktop_clipboard_timestamp_target_atom, g_wnd, CurrentTime);
465                    return;
466            }
467    
468            /* Just PRIMARY */
469            if (primary_owner != None)
470            {
471                    XConvertSelection(g_display, primary_atom, targets_atom,
472                                      rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
473                    return;
474            }
475    
476            /* Just CLIPBOARD */
477            if (clipboard_owner != None)
478            {
479                    XConvertSelection(g_display, clipboard_atom, targets_atom,
480                                      rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
481                    return;
482            }
483    
484            DEBUG_CLIPBOARD(("No owner of any selection.\n"));
485    
486            /* FIXME:
487               Without XFIXES, we cannot reliably know the formats offered by an
488               upcoming selection owner, so we just lie about him offering
489               RDP_CF_TEXT. */
490            cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
491    
492          end:
493            probing_selections = False;
494    }
495    
496  /* This function is called for SelectionNotify events.  /* This function is called for SelectionNotify events.
497     The SelectionNotify event is sent from the clipboard owner to the requestor     The SelectionNotify event is sent from the clipboard owner to the requestor
498     after his request was satisfied.     after his request was satisfied.
# Line 422  xclip_handle_SelectionNotify(XSelectionE Line 522  xclip_handle_SelectionNotify(XSelectionE
522                  {                  {
523                          res = XGetWindowProperty(g_display, g_wnd,                          res = XGetWindowProperty(g_display, g_wnd,
524                                                   rdesktop_primary_timestamp_target_atom, 0,                                                   rdesktop_primary_timestamp_target_atom, 0,
525                                                   XMaxRequestSize(g_display), False, XA_INTEGER,                                                   XMaxRequestSize(g_display), False, AnyPropertyType,
526                                                   &type, &format, &nitems, &bytes_left, &data);                                                   &type, &format, &nitems, &bytes_left, &data);
527                  }                  }
528                  else                  else
529                  {                  {
530                          res = XGetWindowProperty(g_display, g_wnd,                          res = XGetWindowProperty(g_display, g_wnd,
531                                                   rdesktop_clipboard_timestamp_target_atom, 0,                                                   rdesktop_clipboard_timestamp_target_atom, 0,
532                                                   XMaxRequestSize(g_display), False, XA_INTEGER,                                                   XMaxRequestSize(g_display), False, AnyPropertyType,
533                                                   &type, &format, &nitems, &bytes_left, &data);                                                   &type, &format, &nitems, &bytes_left, &data);
534                  }                  }
535    
536    
537                  if ((res != Success) || (nitems != 1))                  if ((res != Success) || (nitems != 1) || (format != 32))
538                  {                  {
539                          DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));                          DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
540                          goto fail;                          goto fail;
# Line 482  xclip_handle_SelectionNotify(XSelectionE Line 582  xclip_handle_SelectionNotify(XSelectionE
582                  return;                  return;
583          }          }
584    
585            if (probing_selections && reprobe_selections)
586            {
587                    probing_selections = False;
588                    xclip_probe_selections();
589                    return;
590            }
591    
592          res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,          res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
593                                   0, XMaxRequestSize(g_display), False, AnyPropertyType,                                   0, XMaxRequestSize(g_display), False, AnyPropertyType,
594                                   &type, &format, &nitems, &bytes_left, &data);                                   &type, &format, &nitems, &bytes_left, &data);
# Line 506  xclip_handle_SelectionNotify(XSelectionE Line 613  xclip_handle_SelectionNotify(XSelectionE
613                  XFree(data);                  XFree(data);
614                  g_incr_target = event->target;                  g_incr_target = event->target;
615                  g_waiting_for_INCR = 1;                  g_waiting_for_INCR = 1;
616                  return;                  goto end;
617          }          }
618    
619          /* Negotiate target format */          /* Negotiate target format */
# Line 554  xclip_handle_SelectionNotify(XSelectionE Line 661  xclip_handle_SelectionNotify(XSelectionE
661                                          }                                          }
662                                  }                                  }
663  #endif  #endif
664                                    else if (supported_targets[i] == rdesktop_clipboard_formats_atom)
665                                    {
666                                            if (probing_selections && (text_target_satisfaction < 4))
667                                            {
668                                                    DEBUG_CLIPBOARD(("Other party supports native formats, choosing that as best_target\n"));
669                                                    best_text_target = supported_targets[i];
670                                                    text_target_satisfaction = 4;
671                                            }
672                                    }
673                          }                          }
674                  }                  }
675    
676                  /* Kickstarting the next step in the process of satisfying RDP's                  /* Kickstarting the next step in the process of satisfying RDP's
677                     clipboard request -- specifically, requesting the actual clipboard data.                     clipboard request -- specifically, requesting the actual clipboard data.
678                   */                   */
679                  if (best_text_target != 0)                  if ((best_text_target != 0)
680                        && (!probing_selections
681                            || (best_text_target == rdesktop_clipboard_formats_atom)))
682                  {                  {
683                          XConvertSelection(g_display, event->selection, best_text_target,                          XConvertSelection(g_display, event->selection, best_text_target,
684                                            rdesktop_clipboard_target_atom, g_wnd, event->time);                                            rdesktop_clipboard_target_atom, g_wnd, event->time);
685                          return;                          goto end;
686                  }                  }
687                  else                  else
688                  {                  {
# Line 574  xclip_handle_SelectionNotify(XSelectionE Line 692  xclip_handle_SelectionNotify(XSelectionE
692          }          }
693          else          else
694          {          {
695                  if (!xclip_send_data_with_convert(data, nitems, event->target))                  if (probing_selections)
696                    {
697                            Window primary_owner, clipboard_owner;
698    
699                            /* FIXME:
700                               Without XFIXES, we must make sure that the other
701                               rdesktop owns all relevant selections or we might try
702                               to get a native format from non-rdesktop window later
703                               on. */
704    
705                            clipboard_owner = XGetSelectionOwner(g_display, clipboard_atom);
706    
707                            if (auto_mode)
708                                    primary_owner = XGetSelectionOwner(g_display, primary_atom);
709                            else
710                                    primary_owner = clipboard_owner;
711    
712                            if (primary_owner != clipboard_owner)
713                                    goto fail;
714    
715                            DEBUG_CLIPBOARD(("Got fellow rdesktop formats\n"));
716                            probing_selections = False;
717                            rdesktop_is_selection_owner = True;
718                            cliprdr_send_native_format_announce(data, nitems);
719                    }
720                    else if ((!nitems) || (!xclip_send_data_with_convert(data, nitems, event->target)))
721                  {                  {
722                          goto fail;                          goto fail;
723                  }                  }
724          }          }
725    
726          XFree(data);        end:
727            if (data)
728                    XFree(data);
729    
730          return;          return;
731    
732        fail:        fail:
733          xclip_clear_target_props();          xclip_clear_target_props();
734          if (data)          if (probing_selections)
735                  XFree(data);          {
736          helper_cliprdr_send_empty_response();                  DEBUG_CLIPBOARD(("Unable to find suitable target. Using default text format.\n"));
737                    probing_selections = False;
738                    rdesktop_is_selection_owner = False;
739    
740                    /* FIXME:
741                       Without XFIXES, we cannot reliably know the formats offered by an
742                       upcoming selection owner, so we just lie about him offering
743                       RDP_CF_TEXT. */
744                    cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
745            }
746            else
747            {
748                    helper_cliprdr_send_empty_response();
749            }
750            goto end;
751  }  }
752    
753  /* This function is called for SelectionRequest events.  /* This function is called for SelectionRequest events.
# Line 599  void Line 758  void
758  xclip_handle_SelectionRequest(XSelectionRequestEvent * event)  xclip_handle_SelectionRequest(XSelectionRequestEvent * event)
759  {  {
760          unsigned long nitems, bytes_left;          unsigned long nitems, bytes_left;
761          unsigned char *prop_return;          unsigned char *prop_return = NULL;
         uint32 *wanted_format;  
762          int format, res;          int format, res;
763          Atom type;          Atom type;
764    
# Line 619  xclip_handle_SelectionRequest(XSelection Line 777  xclip_handle_SelectionRequest(XSelection
777                  xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & acquire_time, 1);                  xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & acquire_time, 1);
778                  return;                  return;
779          }          }
780            else if (event->target == rdesktop_clipboard_formats_atom)
781            {
782                    xclip_provide_selection(event, XA_STRING, 8, formats_data, formats_data_length);
783            }
784          else          else
785          {          {
786                  /* All the following targets require an async operation with the RDP server                  /* All the following targets require an async operation with the RDP server
# Line 630  xclip_handle_SelectionRequest(XSelection Line 792  xclip_handle_SelectionRequest(XSelection
792                          xclip_refuse_selection(event);                          xclip_refuse_selection(event);
793                          return;                          return;
794                  }                  }
795                  if (event->target == rdesktop_clipboard_formats_atom)                  if (event->target == rdesktop_native_atom)
796                  {                  {
797                          /* Before the requestor makes a request for the _RDESKTOP_CLIPBOARD_FORMATS target,                          /* Before the requestor makes a request for the _RDESKTOP_NATIVE target,
798                             he should declare requestor[_RDESKTOP_CLIPBOARD_TARGET] = CF_SOMETHING.                             he should declare requestor[property] = CF_SOMETHING. */
                            Otherwise, we default to RDP_CF_TEXT.  
                          */  
799                          res = XGetWindowProperty(g_display, event->requestor,                          res = XGetWindowProperty(g_display, event->requestor,
800                                                   rdesktop_clipboard_target_atom, 0, 1, True,                                                   event->property, 0, 1, True,
801                                                   XA_INTEGER, &type, &format, &nitems, &bytes_left,                                                   XA_INTEGER, &type, &format, &nitems, &bytes_left,
802                                                   &prop_return);                                                   &prop_return);
803                          wanted_format = (uint32 *) prop_return;                          if (res != Success || (!prop_return))
804                          format = (res == Success) ? *wanted_format : RDP_CF_TEXT;                          {
805                                    DEBUG_CLIPBOARD(("Requested native format but didn't specifiy which.\n"));
806                                    xclip_refuse_selection(event);
807                                    return;
808                            }
809    
810                            format = *(uint32 *) prop_return;
811                          XFree(prop_return);                          XFree(prop_return);
812                  }                  }
813                  else if (event->target == format_string_atom || event->target == XA_STRING)                  else if (event->target == format_string_atom || event->target == XA_STRING)
# Line 689  void Line 855  void
855  xclip_handle_SelectionClear(void)  xclip_handle_SelectionClear(void)
856  {  {
857          DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));          DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
858          have_primary = 0;          xclip_notify_change();
859          XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);          xclip_probe_selections();
         /* FIXME:  
            Without XFIXES, we cannot reliably know the formats offered by the  
            new owner of the X11 clipboard, so we just lie about him  
            offering RDP_CF_TEXT. */  
         cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);  
860  }  }
861    
862  /* Called when any property changes in our window or the root window. */  /* Called when any property changes in our window or the root window. */
# Line 705  xclip_handle_PropertyNotify(XPropertyEve Line 866  xclip_handle_PropertyNotify(XPropertyEve
866          unsigned long nitems;          unsigned long nitems;
867          unsigned long offset = 0;          unsigned long offset = 0;
868          unsigned long bytes_left = 1;          unsigned long bytes_left = 1;
869          int format, res;          int format;
870          XWindowAttributes wa;          XWindowAttributes wa;
871          uint8 *data;          uint8 *data;
872          Atom type;          Atom type;
# Line 763  xclip_handle_PropertyNotify(XPropertyEve Line 924  xclip_handle_PropertyNotify(XPropertyEve
924                  return;                  return;
925          }          }
926    
927          if ((event->atom == rdesktop_clipboard_formats_atom) &&          if ((event->atom == rdesktop_selection_notify_atom) &&
928              (event->window == DefaultRootWindow(g_display)) &&              (event->window == DefaultRootWindow(g_display)))
929              !have_primary /* not interested in our own events */ )                  xclip_probe_selections();
         {  
                 if (event->state == PropertyNewValue)  
                 {  
                         DEBUG_CLIPBOARD(("xclip_handle_PropertyNotify: getting fellow rdesktop formats\n"));  
   
                         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;  
                         }  
                 }  
   
                 /* For some reason, we couldn't announce the native formats */  
                 cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);  
                 rdesktop_is_selection_owner = 0;  
         }  
930  }  }
931  #endif  #endif
932    
# Line 804  ui_clip_format_announce(uint8 * data, ui Line 943  ui_clip_format_announce(uint8 * data, ui
943    
944          XSetSelectionOwner(g_display, primary_atom, g_wnd, acquire_time);          XSetSelectionOwner(g_display, primary_atom, g_wnd, acquire_time);
945          if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)          if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
         {  
946                  warning("Failed to aquire ownership of PRIMARY clipboard\n");                  warning("Failed to aquire ownership of PRIMARY clipboard\n");
                 return;  
         }  
   
         have_primary = 1;  
         XChangeProperty(g_display, DefaultRootWindow(g_display),  
                         rdesktop_clipboard_formats_atom, XA_STRING, 8, PropModeReplace, data,  
                         length);  
947    
948          XSetSelectionOwner(g_display, clipboard_atom, g_wnd, acquire_time);          XSetSelectionOwner(g_display, clipboard_atom, g_wnd, acquire_time);
949          if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)          if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)
950                  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");                  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
951    
952            if (formats_data)
953                    xfree(formats_data);
954            formats_data = xmalloc(length);
955            memcpy(formats_data, data, length);
956            formats_data_length = length;
957    
958            xclip_notify_change();
959  }  }
960    
961  /* Called when the RDP server responds with clipboard data (after we've requested it). */  /* Called when the RDP server responds with clipboard data (after we've requested it). */
962  void  void
963  ui_clip_handle_data(uint8 * data, uint32 length)  ui_clip_handle_data(uint8 * data, uint32 length)
964  {  {
965          BOOL free_data = False;          RD_BOOL free_data = False;
966    
967          if (length == 0)          if (length == 0)
968          {          {
# Line 871  ui_clip_handle_data(uint8 * data, uint32 Line 1010  ui_clip_handle_data(uint8 * data, uint32
1010                          free_data = True;                          free_data = True;
1011                          data = (uint8 *) utf8_data;                          data = (uint8 *) utf8_data;
1012                          length = utf8_length - utf8_length_remaining;                          length = utf8_length - utf8_length_remaining;
1013                            /* translate linebreaks (works just as well on UTF-8) */
1014                            crlf2lf(data, &length);
1015                  }                  }
1016          }          }
1017          else if (selection_request.target == format_unicode_atom)          else if (selection_request.target == format_unicode_atom)
# Line 880  ui_clip_handle_data(uint8 * data, uint32 Line 1021  ui_clip_handle_data(uint8 * data, uint32
1021                     for further conversions. */                     for further conversions. */
1022          }          }
1023  #endif  #endif
1024          else if (selection_request.target == rdesktop_clipboard_formats_atom)          else if (selection_request.target == rdesktop_native_atom)
1025          {          {
1026                  /* Pass as-is */                  /* Pass as-is */
1027          }          }
1028          else          else
1029          {          {
1030                    DEBUG_CLIPBOARD(("ui_clip_handle_data: BUG! I don't know how to convert selection target %s!\n", XGetAtomName(g_display, selection_request.target)));
1031                  xclip_refuse_selection(&selection_request);                  xclip_refuse_selection(&selection_request);
1032                  has_selection_request = False;                  has_selection_request = False;
1033                  return;                  return;
# Line 913  ui_clip_request_data(uint32 format) Line 1055  ui_clip_request_data(uint32 format)
1055          DEBUG_CLIPBOARD(("Request from server for format %d\n", format));          DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
1056          rdp_clipboard_request_format = format;          rdp_clipboard_request_format = format;
1057    
1058            if (probing_selections)
1059            {
1060                    DEBUG_CLIPBOARD(("ui_clip_request_data: Selection probe in progress. Cannot handle request.\n"));
1061                    helper_cliprdr_send_empty_response();
1062                    return;
1063            }
1064    
1065          xclip_clear_target_props();          xclip_clear_target_props();
1066    
1067          if (rdesktop_is_selection_owner)          if (rdesktop_is_selection_owner)
# Line 920  ui_clip_request_data(uint32 format) Line 1069  ui_clip_request_data(uint32 format)
1069                  XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,                  XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
1070                                  XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);                                  XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
1071    
1072                  XConvertSelection(g_display, primary_atom, rdesktop_clipboard_formats_atom,                  XConvertSelection(g_display, primary_atom, rdesktop_native_atom,
1073                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
1074                  return;                  return;
1075          }          }
# Line 967  ui_clip_request_data(uint32 format) Line 1116  ui_clip_request_data(uint32 format)
1116  void  void
1117  ui_clip_sync(void)  ui_clip_sync(void)
1118  {  {
1119          cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);          xclip_probe_selections();
1120  }  }
1121    
1122  void  void
# Line 975  ui_clip_set_mode(const char *optarg) Line 1124  ui_clip_set_mode(const char *optarg)
1124  {  {
1125          g_rdpclip = True;          g_rdpclip = True;
1126    
1127          if (str_startswith(optarg, "auto") || str_startswith(optarg, "on")          if (str_startswith(optarg, "PRIMARYCLIPBOARD"))
             || str_startswith(optarg, "PRIMARYCLIPBOARD"))  
1128                  auto_mode = True;                  auto_mode = True;
1129          else if (str_startswith(optarg, "CLIPBOARD"))          else if (str_startswith(optarg, "CLIPBOARD"))
1130                  auto_mode = False;                  auto_mode = False;
# Line 1011  xclip_init(void) Line 1159  xclip_init(void)
1159          format_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False);          format_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False);
1160          format_unicode_atom = XInternAtom(g_display, "text/unicode", False);          format_unicode_atom = XInternAtom(g_display, "text/unicode", False);
1161    
1162          /* rdesktop sets _RDESKTOP_CLIPBOARD_FORMATS on the root window when acquiring the clipboard.          /* rdesktop sets _RDESKTOP_SELECTION_NOTIFY on the root window when acquiring the clipboard.
1163             Other interested rdesktops can use this to notify their server of the available formats. */             Other interested rdesktops can use this to notify their server of the available formats. */
1164            rdesktop_selection_notify_atom =
1165                    XInternAtom(g_display, "_RDESKTOP_SELECTION_NOTIFY", False);
1166            XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);
1167            probing_selections = False;
1168    
1169            rdesktop_native_atom = XInternAtom(g_display, "_RDESKTOP_NATIVE", False);
1170          rdesktop_clipboard_formats_atom =          rdesktop_clipboard_formats_atom =
1171                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
1172          XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);          rdesktop_primary_owner_atom = XInternAtom(g_display, "_RDESKTOP_PRIMARY_OWNER", False);
1173            rdesktop_clipboard_owner_atom = XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_OWNER", False);
1174    
1175          num_targets = 0;          num_targets = 0;
1176          targets[num_targets++] = targets_atom;          targets[num_targets++] = targets_atom;
1177          targets[num_targets++] = timestamp_atom;          targets[num_targets++] = timestamp_atom;
1178            targets[num_targets++] = rdesktop_native_atom;
1179          targets[num_targets++] = rdesktop_clipboard_formats_atom;          targets[num_targets++] = rdesktop_clipboard_formats_atom;
1180  #ifdef USE_UNICODE_CLIPBOARD  #ifdef USE_UNICODE_CLIPBOARD
1181          targets[num_targets++] = format_utf8_string_atom;          targets[num_targets++] = format_utf8_string_atom;
# Line 1028  xclip_init(void) Line 1184  xclip_init(void)
1184          targets[num_targets++] = format_string_atom;          targets[num_targets++] = format_string_atom;
1185          targets[num_targets++] = XA_STRING;          targets[num_targets++] = XA_STRING;
1186  }  }
1187    
1188    void
1189    xclip_deinit(void)
1190    {
1191            if (XGetSelectionOwner(g_display, primary_atom) == g_wnd)
1192                    XSetSelectionOwner(g_display, primary_atom, None, acquire_time);
1193            if (XGetSelectionOwner(g_display, clipboard_atom) == g_wnd)
1194                    XSetSelectionOwner(g_display, clipboard_atom, None, acquire_time);
1195            xclip_notify_change();
1196    }

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

  ViewVC Help
Powered by ViewVC 1.1.26