/[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 1038 by astrand, Thu Jan 5 11:56:57 2006 UTC revision 1365 by jsorg71, Thu Jan 4 05:39:39 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;
57    
58    /* Mode of operation.
59       - Auto: Look at both PRIMARY and CLIPBOARD and use the most recent.
60       - Non-auto: Look at just CLIPBOARD. */
61    static BOOL auto_mode = True;
62  /* Atoms of the two X selections we're dealing with: CLIPBOARD (explicit-copy) and PRIMARY (selection-copy) */  /* 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  /* Atom _RDESKTOP_CLIPBOARD_FORMATS which has multiple uses:  /* Atoms _RDESKTOP_PRIMARY_TIMESTAMP_TARGET and _RDESKTOP_CLIPBOARD_TIMESTAMP_TARGET
73     - The clipboard target (X jargon for "clipboard format") for rdesktop-to-rdesktop interchange     are used to store the timestamps for when a window got ownership of the selections.
74       of Windows native clipboard data.     We use these to determine which is more recent and should be used. */
75       This target cannot be used standalone; the requestor must keep the  static Atom rdesktop_primary_timestamp_target_atom, rdesktop_clipboard_timestamp_target_atom;
76       _RDESKTOP_CLIPBOARD_TARGET property on his window denoting  /* Storage for timestamps since we get them in two separate notifications. */
77       the Windows native clipboard format being requested.  static Time primary_timestamp, clipboard_timestamp;
78     - The root window property set by rdesktop when it owns the clipboard,  /* Clipboard target for getting a list of native Windows clipboard formats. The
79       denoting all Windows native clipboard formats it offers via     presence of this target indicates that the selection owner is another rdesktop. */
      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 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 98  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 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. */  /* Denotes that an INCR ("chunked") transfer is in progress. */
125  static int g_waiting_for_INCR = 0;  static int g_waiting_for_INCR = 0;
# Line 140  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            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 150  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          while ((uint8 *) inptr < data + *size)          while ((uint8 *) inptr < data + *size)
174          {          {
# Line 201  xclip_provide_selection(XSelectionReques Line 219  xclip_provide_selection(XSelectionReques
219  {  {
220          XEvent xev;          XEvent xev;
221    
222            DEBUG_CLIPBOARD(("xclip_provide_selection: requestor=0x%08x, target=%s, property=%s, length=%u\n", (unsigned) req->requestor, XGetAtomName(g_display, req->target), XGetAtomName(g_display, req->property), (unsigned) length));
223    
224          XChangeProperty(g_display, req->requestor, req->property,          XChangeProperty(g_display, req->requestor, req->property,
225                          type, format, PropModeReplace, data, length);                          type, format, PropModeReplace, data, length);
226    
# Line 223  xclip_refuse_selection(XSelectionRequest Line 243  xclip_refuse_selection(XSelectionRequest
243  {  {
244          XEvent xev;          XEvent xev;
245    
246            DEBUG_CLIPBOARD(("xclip_refuse_selection: requestor=0x%08x, target=%s, property=%s\n",
247                             (unsigned) req->requestor, XGetAtomName(g_display, req->target),
248                             XGetAtomName(g_display, req->property)));
249    
250          xev.xselection.type = SelectionNotify;          xev.xselection.type = SelectionNotify;
251          xev.xselection.serial = 0;          xev.xselection.serial = 0;
252          xev.xselection.send_event = True;          xev.xselection.send_event = True;
# Line 262  helper_cliprdr_send_empty_response() Line 286  helper_cliprdr_send_empty_response()
286  static Bool  static Bool
287  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)
288  {  {
289            DEBUG_CLIPBOARD(("xclip_send_data_with_convert: target=%s, size=%u\n",
290                             XGetAtomName(g_display, target), (unsigned) source_size));
291    
292  #ifdef USE_UNICODE_CLIPBOARD  #ifdef USE_UNICODE_CLIPBOARD
293          if (target == format_string_atom ||          if (target == format_string_atom ||
294              target == format_unicode_atom || target == format_utf8_string_atom)              target == format_unicode_atom || target == format_utf8_string_atom)
295          {          {
296                    size_t unicode_buffer_size;
297                    char *unicode_buffer;
298                    iconv_t cd;
299                    size_t unicode_buffer_size_remaining;
300                    char *unicode_buffer_remaining;
301                    char *data_remaining;
302                    size_t data_size_remaining;
303                    uint32 translated_data_size;
304                    uint8 *translated_data;
305    
306                  if (rdp_clipboard_request_format != RDP_CF_TEXT)                  if (rdp_clipboard_request_format != RDP_CF_TEXT)
307                          return False;                          return False;
308    
# Line 274  xclip_send_data_with_convert(uint8 * sou Line 311  xclip_send_data_with_convert(uint8 * sou
311                     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
312                     WinNT versions are Unicode-minded).                     WinNT versions are Unicode-minded).
313                   */                   */
                 size_t unicode_buffer_size;  
                 char *unicode_buffer;  
                 iconv_t cd;  
   
314                  if (target == format_string_atom)                  if (target == format_string_atom)
315                  {                  {
316                          char *locale_charset = nl_langinfo(CODESET);                          char *locale_charset = nl_langinfo(CODESET);
# Line 316  xclip_send_data_with_convert(uint8 * sou Line 349  xclip_send_data_with_convert(uint8 * sou
349                  }                  }
350    
351                  unicode_buffer = xmalloc(unicode_buffer_size);                  unicode_buffer = xmalloc(unicode_buffer_size);
352                  size_t unicode_buffer_size_remaining = unicode_buffer_size;                  unicode_buffer_size_remaining = unicode_buffer_size;
353                  char *unicode_buffer_remaining = unicode_buffer;                  unicode_buffer_remaining = unicode_buffer;
354                  char *data_remaining = (char *) source;                  data_remaining = (char *) source;
355                  size_t data_size_remaining = source_size;                  data_size_remaining = source_size;
356                  iconv(cd, &data_remaining, &data_size_remaining, &unicode_buffer_remaining,                  iconv(cd, (ICONV_CONST char **) &data_remaining, &data_size_remaining,
357                        &unicode_buffer_size_remaining);                        &unicode_buffer_remaining, &unicode_buffer_size_remaining);
358                  iconv_close(cd);                  iconv_close(cd);
359    
360                  /* translate linebreaks */                  /* translate linebreaks */
361                  uint32 translated_data_size = unicode_buffer_size - unicode_buffer_size_remaining;                  translated_data_size = unicode_buffer_size - unicode_buffer_size_remaining;
362                  uint8 *translated_data =                  translated_data = utf16_lf2crlf((uint8 *) unicode_buffer, &translated_data_size);
                         utf16_lf2crlf((uint8 *) unicode_buffer, &translated_data_size);  
363                  if (translated_data != NULL)                  if (translated_data != NULL)
364                  {                  {
365                          DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n",                          DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n",
366                                           translated_data_size));                                           translated_data_size));
367                          cliprdr_send_data(translated_data, translated_data_size);                          helper_cliprdr_send_response(translated_data, translated_data_size);
368                          xfree(translated_data); /* Not the same thing as XFree! */                          xfree(translated_data); /* Not the same thing as XFree! */
369                  }                  }
370    
# Line 353  xclip_send_data_with_convert(uint8 * sou Line 385  xclip_send_data_with_convert(uint8 * sou
385                  translated_data = lf2crlf(source, &length);                  translated_data = lf2crlf(source, &length);
386                  if (translated_data != NULL)                  if (translated_data != NULL)
387                  {                  {
388                          cliprdr_send_data(translated_data, length);                          helper_cliprdr_send_response(translated_data, length);
389                          xfree(translated_data); /* Not the same thing as XFree! */                          xfree(translated_data); /* Not the same thing as XFree! */
390                  }                  }
391    
392                  return True;                  return True;
393          }          }
394  #endif  #endif
395          else if (target == rdesktop_clipboard_formats_atom)          else if (target == rdesktop_native_atom)
396          {          {
397                  helper_cliprdr_send_response(source, source_size + 1);                  helper_cliprdr_send_response(source, source_size + 1);
398    
# Line 372  xclip_send_data_with_convert(uint8 * sou Line 404  xclip_send_data_with_convert(uint8 * sou
404          }          }
405  }  }
406    
407    static void
408    xclip_clear_target_props()
409    {
410            XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
411            XDeleteProperty(g_display, g_wnd, rdesktop_primary_timestamp_target_atom);
412            XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_timestamp_target_atom);
413    }
414    
415    static void
416    xclip_notify_change()
417    {
418            XChangeProperty(g_display, DefaultRootWindow(g_display),
419                            rdesktop_selection_notify_atom, XA_INTEGER, 32, PropModeReplace, NULL, 0);
420    }
421    
422    static void
423    xclip_probe_selections()
424    {
425            Window primary_owner, clipboard_owner;
426    
427            if (probing_selections)
428            {
429                    DEBUG_CLIPBOARD(("Already probing selections. Scheduling reprobe.\n"));
430                    reprobe_selections = True;
431                    return;
432            }
433    
434            DEBUG_CLIPBOARD(("Probing selections.\n"));
435    
436            probing_selections = True;
437            reprobe_selections = False;
438    
439            xclip_clear_target_props();
440    
441            if (auto_mode)
442                    primary_owner = XGetSelectionOwner(g_display, primary_atom);
443            else
444                    primary_owner = None;
445    
446            clipboard_owner = XGetSelectionOwner(g_display, clipboard_atom);
447    
448            /* If we own all relevant selections then don't do anything. */
449            if (((primary_owner == g_wnd) || !auto_mode) && (clipboard_owner == g_wnd))
450                    goto end;
451    
452            /* Both available */
453            if ((primary_owner != None) && (clipboard_owner != None))
454            {
455                    primary_timestamp = 0;
456                    clipboard_timestamp = 0;
457                    XConvertSelection(g_display, primary_atom, timestamp_atom,
458                                      rdesktop_primary_timestamp_target_atom, g_wnd, CurrentTime);
459                    XConvertSelection(g_display, clipboard_atom, timestamp_atom,
460                                      rdesktop_clipboard_timestamp_target_atom, g_wnd, CurrentTime);
461                    return;
462            }
463    
464            /* Just PRIMARY */
465            if (primary_owner != None)
466            {
467                    XConvertSelection(g_display, primary_atom, targets_atom,
468                                      rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
469                    return;
470            }
471    
472            /* Just CLIPBOARD */
473            if (clipboard_owner != None)
474            {
475                    XConvertSelection(g_display, clipboard_atom, targets_atom,
476                                      rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
477                    return;
478            }
479    
480            DEBUG_CLIPBOARD(("No owner of any selection.\n"));
481    
482            /* FIXME:
483               Without XFIXES, we cannot reliably know the formats offered by an
484               upcoming selection owner, so we just lie about him offering
485               RDP_CF_TEXT. */
486            cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
487    
488          end:
489            probing_selections = False;
490    }
491    
492  /* This function is called for SelectionNotify events.  /* This function is called for SelectionNotify events.
493     The SelectionNotify event is sent from the clipboard owner to the requestor     The SelectionNotify event is sent from the clipboard owner to the requestor
494     after his request was satisfied.     after his request was satisfied.
# Line 385  xclip_handle_SelectionNotify(XSelectionE Line 502  xclip_handle_SelectionNotify(XSelectionE
502          Atom type;          Atom type;
503          Atom *supported_targets;          Atom *supported_targets;
504          int res, i, format;          int res, i, format;
505          uint8 *data;          uint8 *data = NULL;
506    
507          if (event->property == None)          if (event->property == None)
508                  goto fail;                  goto fail;
# Line 395  xclip_handle_SelectionNotify(XSelectionE Line 512  xclip_handle_SelectionNotify(XSelectionE
512                           XGetAtomName(g_display, event->target),                           XGetAtomName(g_display, event->target),
513                           XGetAtomName(g_display, event->property)));                           XGetAtomName(g_display, event->property)));
514    
515          if (event->property == None)          if (event->target == timestamp_atom)
516                  goto fail;          {
517                    if (event->selection == primary_atom)
518                    {
519                            res = XGetWindowProperty(g_display, g_wnd,
520                                                     rdesktop_primary_timestamp_target_atom, 0,
521                                                     XMaxRequestSize(g_display), False, AnyPropertyType,
522                                                     &type, &format, &nitems, &bytes_left, &data);
523                    }
524                    else
525                    {
526                            res = XGetWindowProperty(g_display, g_wnd,
527                                                     rdesktop_clipboard_timestamp_target_atom, 0,
528                                                     XMaxRequestSize(g_display), False, AnyPropertyType,
529                                                     &type, &format, &nitems, &bytes_left, &data);
530                    }
531    
532    
533                    if ((res != Success) || (nitems != 1) || (format != 32))
534                    {
535                            DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
536                            goto fail;
537                    }
538    
539                    if (event->selection == primary_atom)
540                    {
541                            primary_timestamp = *(Time *) data;
542                            if (primary_timestamp == 0)
543                                    primary_timestamp++;
544                            XDeleteProperty(g_display, g_wnd, rdesktop_primary_timestamp_target_atom);
545                            DEBUG_CLIPBOARD(("Got PRIMARY timestamp: %u\n",
546                                             (unsigned) primary_timestamp));
547                    }
548                    else
549                    {
550                            clipboard_timestamp = *(Time *) data;
551                            if (clipboard_timestamp == 0)
552                                    clipboard_timestamp++;
553                            XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_timestamp_target_atom);
554                            DEBUG_CLIPBOARD(("Got CLIPBOARD timestamp: %u\n",
555                                             (unsigned) clipboard_timestamp));
556                    }
557    
558                    XFree(data);
559    
560                    if (primary_timestamp && clipboard_timestamp)
561                    {
562                            if (primary_timestamp > clipboard_timestamp)
563                            {
564                                    DEBUG_CLIPBOARD(("PRIMARY is most recent selection.\n"));
565                                    XConvertSelection(g_display, primary_atom, targets_atom,
566                                                      rdesktop_clipboard_target_atom, g_wnd,
567                                                      event->time);
568                            }
569                            else
570                            {
571                                    DEBUG_CLIPBOARD(("CLIPBOARD is most recent selection.\n"));
572                                    XConvertSelection(g_display, clipboard_atom, targets_atom,
573                                                      rdesktop_clipboard_target_atom, g_wnd,
574                                                      event->time);
575                            }
576                    }
577    
578                    return;
579            }
580    
581            if (probing_selections && reprobe_selections)
582            {
583                    probing_selections = False;
584                    xclip_probe_selections();
585                    return;
586            }
587    
588          res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,          res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
589                                   0, XMaxRequestSize(g_display), False, AnyPropertyType,                                   0, XMaxRequestSize(g_display), False, AnyPropertyType,
590                                   &type, &format, &nitems, &bytes_left, &data);                                   &type, &format, &nitems, &bytes_left, &data);
591    
592            xclip_clear_target_props();
593    
594          if (res != Success)          if (res != Success)
595          {          {
596                  DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));                  DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
# Line 417  xclip_handle_SelectionNotify(XSelectionE Line 606  xclip_handle_SelectionNotify(XSelectionE
606                  {                  {
607                          XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));                          XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));
608                  }                  }
                 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);  
609                  XFree(data);                  XFree(data);
610                  g_incr_target = event->target;                  g_incr_target = event->target;
611                  g_waiting_for_INCR = 1;                  g_waiting_for_INCR = 1;
612                  return;                  goto end;
613          }          }
614    
         XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);  
   
615          /* Negotiate target format */          /* Negotiate target format */
616          if (event->target == targets_atom)          if (event->target == targets_atom)
617          {          {
# Line 471  xclip_handle_SelectionNotify(XSelectionE Line 657  xclip_handle_SelectionNotify(XSelectionE
657                                          }                                          }
658                                  }                                  }
659  #endif  #endif
660                                    else if (supported_targets[i] == rdesktop_clipboard_formats_atom)
661                                    {
662                                            if (probing_selections && (text_target_satisfaction < 4))
663                                            {
664                                                    DEBUG_CLIPBOARD(("Other party supports native formats, choosing that as best_target\n"));
665                                                    best_text_target = supported_targets[i];
666                                                    text_target_satisfaction = 4;
667                                            }
668                                    }
669                          }                          }
670                  }                  }
671    
672                  /* Kickstarting the next step in the process of satisfying RDP's                  /* Kickstarting the next step in the process of satisfying RDP's
673                     clipboard request -- specifically, requesting the actual clipboard data.                     clipboard request -- specifically, requesting the actual clipboard data.
674                   */                   */
675                  if (best_text_target != 0)                  if ((best_text_target != 0)
676                        && (!probing_selections
677                            || (best_text_target == rdesktop_clipboard_formats_atom)))
678                  {                  {
679                          XConvertSelection(g_display, clipboard_atom, best_text_target,                          XConvertSelection(g_display, event->selection, best_text_target,
680                                            rdesktop_clipboard_target_atom, g_wnd, event->time);                                            rdesktop_clipboard_target_atom, g_wnd, event->time);
681                          return;                          goto end;
682                  }                  }
683                  else                  else
684                  {                  {
# Line 491  xclip_handle_SelectionNotify(XSelectionE Line 688  xclip_handle_SelectionNotify(XSelectionE
688          }          }
689          else          else
690          {          {
691                  if (!xclip_send_data_with_convert(data, nitems, event->target))                  if (probing_selections)
692                    {
693                            Window primary_owner, clipboard_owner;
694    
695                            /* FIXME:
696                               Without XFIXES, we must make sure that the other
697                               rdesktop owns all relevant selections or we might try
698                               to get a native format from non-rdesktop window later
699                               on. */
700    
701                            clipboard_owner = XGetSelectionOwner(g_display, clipboard_atom);
702    
703                            if (auto_mode)
704                                    primary_owner = XGetSelectionOwner(g_display, primary_atom);
705                            else
706                                    primary_owner = clipboard_owner;
707    
708                            if (primary_owner != clipboard_owner)
709                                    goto fail;
710    
711                            DEBUG_CLIPBOARD(("Got fellow rdesktop formats\n"));
712                            probing_selections = False;
713                            rdesktop_is_selection_owner = True;
714                            cliprdr_send_native_format_announce(data, nitems);
715                    }
716                    else if (!xclip_send_data_with_convert(data, nitems, event->target))
717                  {                  {
718                          goto fail;                          goto fail;
719                  }                  }
720          }          }
721    
722          XFree(data);        end:
723            if (data)
724                    XFree(data);
725    
726          return;          return;
727    
728        fail:        fail:
729          XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);          xclip_clear_target_props();
730          XFree(data);          if (probing_selections)
731          helper_cliprdr_send_empty_response();          {
732                    DEBUG_CLIPBOARD(("Unable to find suitable target. Using default text format.\n"));
733                    probing_selections = False;
734                    rdesktop_is_selection_owner = False;
735    
736                    /* FIXME:
737                       Without XFIXES, we cannot reliably know the formats offered by an
738                       upcoming selection owner, so we just lie about him offering
739                       RDP_CF_TEXT. */
740                    cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
741            }
742            else
743            {
744                    helper_cliprdr_send_empty_response();
745            }
746            goto end;
747  }  }
748    
749  /* This function is called for SelectionRequest events.  /* This function is called for SelectionRequest events.
# Line 516  xclip_handle_SelectionRequest(XSelection Line 755  xclip_handle_SelectionRequest(XSelection
755  {  {
756          unsigned long nitems, bytes_left;          unsigned long nitems, bytes_left;
757          unsigned char *prop_return;          unsigned char *prop_return;
         uint32 *wanted_format;  
758          int format, res;          int format, res;
759          Atom type;          Atom type;
760    
# Line 532  xclip_handle_SelectionRequest(XSelection Line 770  xclip_handle_SelectionRequest(XSelection
770          }          }
771          else if (event->target == timestamp_atom)          else if (event->target == timestamp_atom)
772          {          {
773                  xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);                  xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & acquire_time, 1);
774                  return;                  return;
775          }          }
776            else if (event->target == rdesktop_clipboard_formats_atom)
777            {
778                    xclip_provide_selection(event, XA_STRING, 8, formats_data, formats_data_length);
779            }
780          else          else
781          {          {
782                  /* 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 546  xclip_handle_SelectionRequest(XSelection Line 788  xclip_handle_SelectionRequest(XSelection
788                          xclip_refuse_selection(event);                          xclip_refuse_selection(event);
789                          return;                          return;
790                  }                  }
791                  if (event->target == rdesktop_clipboard_formats_atom)                  if (event->target == rdesktop_native_atom)
792                  {                  {
793                          /* Before the requestor makes a request for the _RDESKTOP_CLIPBOARD_FORMATS target,                          /* Before the requestor makes a request for the _RDESKTOP_NATIVE target,
794                             he should declare requestor[_RDESKTOP_CLIPBOARD_TARGET] = CF_SOMETHING.                             he should declare requestor[property] = CF_SOMETHING. */
                            Otherwise, we default to RDP_CF_TEXT.  
                          */  
795                          res = XGetWindowProperty(g_display, event->requestor,                          res = XGetWindowProperty(g_display, event->requestor,
796                                                   rdesktop_clipboard_target_atom, 0, 1, True,                                                   event->property, 0, 1, True,
797                                                   XA_INTEGER, &type, &format, &nitems, &bytes_left,                                                   XA_INTEGER, &type, &format, &nitems, &bytes_left,
798                                                   &prop_return);                                                   &prop_return);
799                          wanted_format = (uint32 *) prop_return;                          if (res != Success)
800                          format = (res == Success) ? *wanted_format : RDP_CF_TEXT;                          {
801                                    DEBUG_CLIPBOARD(("Requested native format but didn't specifiy which.\n"));
802                                    xclip_refuse_selection(event);
803                                    return;
804                            }
805    
806                            format = *(uint32 *) prop_return;
807                          XFree(prop_return);                          XFree(prop_return);
808                  }                  }
809                  else if (event->target == format_string_atom || event->target == XA_STRING)                  else if (event->target == format_string_atom || event->target == XA_STRING)
# Line 605  void Line 851  void
851  xclip_handle_SelectionClear(void)  xclip_handle_SelectionClear(void)
852  {  {
853          DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));          DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
854          have_primary = 0;          xclip_notify_change();
855          XDeleteProperty(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);  
856  }  }
857    
858  /* 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 621  xclip_handle_PropertyNotify(XPropertyEve Line 862  xclip_handle_PropertyNotify(XPropertyEve
862          unsigned long nitems;          unsigned long nitems;
863          unsigned long offset = 0;          unsigned long offset = 0;
864          unsigned long bytes_left = 1;          unsigned long bytes_left = 1;
865          int format, res;          int format;
866          XWindowAttributes wa;          XWindowAttributes wa;
867          uint8 *data;          uint8 *data;
868          Atom type;          Atom type;
# Line 679  xclip_handle_PropertyNotify(XPropertyEve Line 920  xclip_handle_PropertyNotify(XPropertyEve
920                  return;                  return;
921          }          }
922    
923          if ((event->atom == rdesktop_clipboard_formats_atom) &&          if ((event->atom == rdesktop_selection_notify_atom) &&
924              (event->window == DefaultRootWindow(g_display)) &&              (event->window == DefaultRootWindow(g_display)))
925              !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;  
         }  
926  }  }
927  #endif  #endif
928    
# Line 716  xclip_handle_PropertyNotify(XPropertyEve Line 935  xclip_handle_PropertyNotify(XPropertyEve
935  void  void
936  ui_clip_format_announce(uint8 * data, uint32 length)  ui_clip_format_announce(uint8 * data, uint32 length)
937  {  {
938          XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime);          acquire_time = g_last_gesturetime;
939    
940            XSetSelectionOwner(g_display, primary_atom, g_wnd, acquire_time);
941          if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)          if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
         {  
942                  warning("Failed to aquire ownership of PRIMARY clipboard\n");                  warning("Failed to aquire ownership of PRIMARY clipboard\n");
                 return;  
         }  
   
         have_primary = 1;  
         XChangeProperty(g_display, DefaultRootWindow(g_display),  
                         rdesktop_clipboard_formats_atom, XA_STRING, 8, PropModeReplace, data,  
                         length);  
943    
944          XSetSelectionOwner(g_display, clipboard_atom, g_wnd, g_last_gesturetime);          XSetSelectionOwner(g_display, clipboard_atom, g_wnd, acquire_time);
945          if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)          if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)
946                  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");                  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
947    
948            if (formats_data)
949                    xfree(formats_data);
950            formats_data = xmalloc(length);
951            memcpy(formats_data, data, length);
952            formats_data_length = length;
953    
954            xclip_notify_change();
955  }  }
956    
957  /* Called when the RDP server responds with clipboard data (after we've requested it). */  /* Called when the RDP server responds with clipboard data (after we've requested it). */
# Line 739  ui_clip_handle_data(uint8 * data, uint32 Line 960  ui_clip_handle_data(uint8 * data, uint32
960  {  {
961          BOOL free_data = False;          BOOL free_data = False;
962    
963            if (length == 0)
964            {
965                    xclip_refuse_selection(&selection_request);
966                    has_selection_request = False;
967                    return;
968            }
969    
970          if (selection_request.target == format_string_atom || selection_request.target == XA_STRING)          if (selection_request.target == format_string_atom || selection_request.target == XA_STRING)
971          {          {
972                  /* We're expecting a CF_TEXT response */                  /* We're expecting a CF_TEXT response */
# Line 772  ui_clip_handle_data(uint8 * data, uint32 Line 1000  ui_clip_handle_data(uint8 * data, uint32
1000                                  iconv_close(cd);                                  iconv_close(cd);
1001                                  return;                                  return;
1002                          }                          }
1003                          iconv(cd, &data_remaining, &length_remaining, &utf8_data_remaining,                          iconv(cd, (ICONV_CONST char **) &data_remaining, &length_remaining,
1004                                &utf8_length_remaining);                                &utf8_data_remaining, &utf8_length_remaining);
1005                          iconv_close(cd);                          iconv_close(cd);
1006                          free_data = True;                          free_data = True;
1007                          data = (uint8 *) utf8_data;                          data = (uint8 *) utf8_data;
# Line 787  ui_clip_handle_data(uint8 * data, uint32 Line 1015  ui_clip_handle_data(uint8 * data, uint32
1015                     for further conversions. */                     for further conversions. */
1016          }          }
1017  #endif  #endif
1018          else if (selection_request.target == rdesktop_clipboard_formats_atom)          else if (selection_request.target == rdesktop_native_atom)
1019          {          {
1020                  /* Pass as-is */                  /* Pass as-is */
1021          }          }
1022          else          else
1023          {          {
1024                    DEBUG_CLIPBOARD(("ui_clip_handle_data: BUG! I don't know how to convert selection target %s!\n", XGetAtomName(g_display, selection_request.target)));
1025                  xclip_refuse_selection(&selection_request);                  xclip_refuse_selection(&selection_request);
1026                  has_selection_request = False;                  has_selection_request = False;
1027                  return;                  return;
# Line 806  ui_clip_handle_data(uint8 * data, uint32 Line 1035  ui_clip_handle_data(uint8 * data, uint32
1035  }  }
1036    
1037  void  void
1038    ui_clip_request_failed()
1039    {
1040            xclip_refuse_selection(&selection_request);
1041            has_selection_request = False;
1042    }
1043    
1044    void
1045  ui_clip_request_data(uint32 format)  ui_clip_request_data(uint32 format)
1046  {  {
1047          Window selectionowner;          Window primary_owner, clipboard_owner;
1048    
1049          DEBUG_CLIPBOARD(("Request from server for format %d\n", format));          DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
1050          rdp_clipboard_request_format = format;          rdp_clipboard_request_format = format;
1051    
1052            if (probing_selections)
1053            {
1054                    DEBUG_CLIPBOARD(("ui_clip_request_data: Selection probe in progress. Cannot handle request.\n"));
1055                    helper_cliprdr_send_empty_response();
1056                    return;
1057            }
1058    
1059            xclip_clear_target_props();
1060    
1061          if (rdesktop_is_selection_owner)          if (rdesktop_is_selection_owner)
1062          {          {
1063                  XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,                  XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
1064                                  XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);                                  XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
1065    
1066                  XConvertSelection(g_display, primary_atom, rdesktop_clipboard_formats_atom,                  XConvertSelection(g_display, primary_atom, rdesktop_native_atom,
1067                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
1068                  return;                  return;
1069          }          }
1070    
1071          selectionowner = XGetSelectionOwner(g_display, primary_atom);          if (auto_mode)
1072          if (selectionowner != None)                  primary_owner = XGetSelectionOwner(g_display, primary_atom);
1073            else
1074                    primary_owner = None;
1075    
1076            clipboard_owner = XGetSelectionOwner(g_display, clipboard_atom);
1077    
1078            /* Both available */
1079            if ((primary_owner != None) && (clipboard_owner != None))
1080            {
1081                    primary_timestamp = 0;
1082                    clipboard_timestamp = 0;
1083                    XConvertSelection(g_display, primary_atom, timestamp_atom,
1084                                      rdesktop_primary_timestamp_target_atom, g_wnd, CurrentTime);
1085                    XConvertSelection(g_display, clipboard_atom, timestamp_atom,
1086                                      rdesktop_clipboard_timestamp_target_atom, g_wnd, CurrentTime);
1087                    return;
1088            }
1089    
1090            /* Just PRIMARY */
1091            if (primary_owner != None)
1092          {          {
1093                  XConvertSelection(g_display, primary_atom, targets_atom,                  XConvertSelection(g_display, primary_atom, targets_atom,
1094                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
1095                  return;                  return;
1096          }          }
1097    
1098          /* No PRIMARY, try CLIPBOARD */          /* Just CLIPBOARD */
1099          selectionowner = XGetSelectionOwner(g_display, clipboard_atom);          if (clipboard_owner != None)
         if (selectionowner != None)  
1100          {          {
1101                  XConvertSelection(g_display, clipboard_atom, targets_atom,                  XConvertSelection(g_display, clipboard_atom, targets_atom,
1102                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);                                    rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
# Line 841  ui_clip_request_data(uint32 format) Line 1104  ui_clip_request_data(uint32 format)
1104          }          }
1105    
1106          /* No data available */          /* No data available */
1107          cliprdr_send_data(NULL, 0);          helper_cliprdr_send_empty_response();
1108  }  }
1109    
1110  void  void
1111  ui_clip_sync(void)  ui_clip_sync(void)
1112  {  {
1113          cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);          xclip_probe_selections();
1114  }  }
1115    
1116    void
1117    ui_clip_set_mode(const char *optarg)
1118    {
1119            g_rdpclip = True;
1120    
1121            if (str_startswith(optarg, "PRIMARYCLIPBOARD"))
1122                    auto_mode = True;
1123            else if (str_startswith(optarg, "CLIPBOARD"))
1124                    auto_mode = False;
1125            else
1126            {
1127                    warning("Invalid clipboard mode '%s'.\n", optarg);
1128                    g_rdpclip = False;
1129            }
1130    }
1131    
1132  void  void
1133  xclip_init(void)  xclip_init(void)
1134  {  {
1135            if (!g_rdpclip)
1136                    return;
1137    
1138          if (!cliprdr_init())          if (!cliprdr_init())
1139                  return;                  return;
1140    
# Line 863  xclip_init(void) Line 1144  xclip_init(void)
1144          timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);          timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);
1145          rdesktop_clipboard_target_atom =          rdesktop_clipboard_target_atom =
1146                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
1147            rdesktop_primary_timestamp_target_atom =
1148                    XInternAtom(g_display, "_RDESKTOP_PRIMARY_TIMESTAMP_TARGET", False);
1149            rdesktop_clipboard_timestamp_target_atom =
1150                    XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TIMESTAMP_TARGET", False);
1151          incr_atom = XInternAtom(g_display, "INCR", False);          incr_atom = XInternAtom(g_display, "INCR", False);
1152          format_string_atom = XInternAtom(g_display, "STRING", False);          format_string_atom = XInternAtom(g_display, "STRING", False);
1153          format_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False);          format_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False);
1154          format_unicode_atom = XInternAtom(g_display, "text/unicode", False);          format_unicode_atom = XInternAtom(g_display, "text/unicode", False);
1155    
1156            /* rdesktop sets _RDESKTOP_SELECTION_NOTIFY on the root window when acquiring the clipboard.
1157               Other interested rdesktops can use this to notify their server of the available formats. */
1158            rdesktop_selection_notify_atom =
1159                    XInternAtom(g_display, "_RDESKTOP_SELECTION_NOTIFY", False);
1160            XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);
1161            probing_selections = False;
1162    
1163            rdesktop_native_atom = XInternAtom(g_display, "_RDESKTOP_NATIVE", False);
1164            rdesktop_clipboard_formats_atom =
1165                    XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
1166            rdesktop_primary_owner_atom = XInternAtom(g_display, "_RDESKTOP_PRIMARY_OWNER", False);
1167            rdesktop_clipboard_owner_atom = XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_OWNER", False);
1168    
1169          num_targets = 0;          num_targets = 0;
1170          targets[num_targets++] = targets_atom;          targets[num_targets++] = targets_atom;
1171          targets[num_targets++] = timestamp_atom;          targets[num_targets++] = timestamp_atom;
1172            targets[num_targets++] = rdesktop_native_atom;
1173          targets[num_targets++] = rdesktop_clipboard_formats_atom;          targets[num_targets++] = rdesktop_clipboard_formats_atom;
         targets[num_targets++] = format_string_atom;  
1174  #ifdef USE_UNICODE_CLIPBOARD  #ifdef USE_UNICODE_CLIPBOARD
1175          targets[num_targets++] = format_utf8_string_atom;          targets[num_targets++] = format_utf8_string_atom;
1176  #endif  #endif
1177          targets[num_targets++] = format_unicode_atom;          targets[num_targets++] = format_unicode_atom;
1178            targets[num_targets++] = format_string_atom;
1179          targets[num_targets++] = XA_STRING;          targets[num_targets++] = XA_STRING;
1180    }
1181    
1182          /* rdesktop sets _RDESKTOP_CLIPBOARD_FORMATS on the root window when acquiring the clipboard.  void
1183             Other interested rdesktops can use this to notify their server of the available formats. */  xclip_deinit(void)
1184          rdesktop_clipboard_formats_atom =  {
1185                  XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);          if (XGetSelectionOwner(g_display, primary_atom) == g_wnd)
1186          XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);                  XSetSelectionOwner(g_display, primary_atom, None, acquire_time);
1187            if (XGetSelectionOwner(g_display, clipboard_atom) == g_wnd)
1188                    XSetSelectionOwner(g_display, clipboard_atom, None, acquire_time);
1189            xclip_notify_change();
1190  }  }

Legend:
Removed from v.1038  
changed lines
  Added in v.1365

  ViewVC Help
Powered by ViewVC 1.1.26