ReactOS  0.4.14-dev-317-g96040ec
xclip.c
Go to the documentation of this file.
1 /* -*- c-basic-offset: 8 -*-
2  rdesktop: A Remote Desktop Protocol client.
3  Protocol services - Clipboard functions
4  Copyright (C) Erik Forsberg <forsberg@cendio.se> 2003
5  Copyright (C) Matthew Chapman 2003
6 
7  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
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License along
18  with this program; if not, write to the Free Software Foundation, Inc.,
19  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21 
22 #include <X11/Xlib.h>
23 #include <X11/Xatom.h>
24 #include "rdesktop.h"
25 
26 /*
27  To gain better understanding of this code, one could be assisted by the following documents:
28  - Inter-Client Communication Conventions Manual (ICCCM)
29  HTML: http://tronche.com/gui/x/icccm/
30  PDF: http://ftp.xfree86.org/pub/XFree86/4.5.0/doc/PDF/icccm.pdf
31  - MSDN: Clipboard Formats
32  http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/dataexchange/clipboard/clipboardformats.asp
33 */
34 
35 #ifdef HAVE_ICONV
36 #ifdef HAVE_LANGINFO_H
37 #ifdef HAVE_ICONV_H
38 #include <langinfo.h>
39 #include <iconv.h>
40 #define USE_UNICODE_CLIPBOARD
41 #endif
42 #endif
43 #endif
44 
45 #ifdef USE_UNICODE_CLIPBOARD
46 #define RDP_CF_TEXT CF_UNICODETEXT
47 #else
48 #define RDP_CF_TEXT CF_TEXT
49 #endif
50 
51 
52 /* Translate LF to CR-LF. To do this, we must allocate more memory.
53  The returned string is null-terminated, as required by CF_TEXT.
54  Does not stop on embedded nulls.
55  The length is updated. */
56 static void
58 {
59  uint8 *dst, *src;
60  src = dst = data;
61  while (src < data + *length)
62  {
63  if (*src != '\x0d')
64  *dst++ = *src;
65  src++;
66  }
67  *length = dst - data;
68 }
69 
70 #ifdef USE_UNICODE_CLIPBOARD
71 /* Translate LF to CR-LF. To do this, we must allocate more memory.
72  The returned string is null-terminated, as required by CF_UNICODETEXT.
73  The size is updated. */
74 static uint8 *
75 utf16_lf2crlf(uint8 * data, uint32 * size)
76 {
77  uint8 *result;
78  uint16 *inptr, *outptr;
79  Bool swap_endianess;
80 
81  /* Worst case: Every char is LF */
82  result = xmalloc((*size * 2) + 2);
83  if (result == NULL)
84  return NULL;
85 
86  inptr = (uint16 *) data;
87  outptr = (uint16 *) result;
88 
89  /* Check for a reversed BOM */
90  swap_endianess = (*inptr == 0xfffe);
91 
92  while ((uint8 *) inptr < data + *size)
93  {
94  uint16 uvalue = *inptr;
95  if (swap_endianess)
96  uvalue = ((uvalue << 8) & 0xff00) + (uvalue >> 8);
97  if (uvalue == 0x0a)
98  *outptr++ = swap_endianess ? 0x0d00 : 0x0d;
99  *outptr++ = *inptr++;
100  }
101  *outptr++ = 0; /* null termination */
102  *size = (uint8 *) outptr - result;
103 
104  return result;
105 }
106 #else
107 /* Translate LF to CR-LF. To do this, we must allocate more memory.
108  The length is updated. */
109 static uint8 *
111 {
112  uint8 *result, *p, *o;
113 
114  /* Worst case: Every char is LF */
115  result = xmalloc(*length * 2);
116 
117  p = data;
118  o = result;
119 
120  while (p < data + *length)
121  {
122  if (*p == '\x0a')
123  *o++ = '\x0d';
124  *o++ = *p++;
125  }
126  *length = o - result;
127 
128  /* Convenience */
129  *o++ = '\0';
130 
131  return result;
132 }
133 #endif
134 
135 static void
136 xclip_provide_selection(RDPCLIENT * This, XSelectionRequestEvent * req, Atom type, unsigned int format, uint8 * data,
137  uint32 length)
138 {
139  XEvent xev;
140 
141  DEBUG_CLIPBOARD(("xclip_provide_selection: requestor=0x%08x, target=%s, property=%s, length=%u\n", (unsigned) req->requestor, XGetAtomName(This->display, req->target), XGetAtomName(This->display, req->property), (unsigned) length));
142 
143  XChangeProperty(This->display, req->requestor, req->property,
144  type, format, PropModeReplace, data, length);
145 
146  xev.xselection.type = SelectionNotify;
147  xev.xselection.serial = 0;
148  xev.xselection.send_event = True;
149  xev.xselection.requestor = req->requestor;
150  xev.xselection.selection = req->selection;
151  xev.xselection.target = req->target;
152  xev.xselection.property = req->property;
153  xev.xselection.time = req->time;
154  XSendEvent(This->display, req->requestor, False, NoEventMask, &xev);
155 }
156 
157 /* Replies a clipboard requestor, telling that we're unable to satisfy his request for whatever reason.
158  This has the benefit of finalizing the clipboard negotiation and thus not leaving our requestor
159  lingering (and, potentially, stuck). */
160 static void
161 xclip_refuse_selection(RDPCLIENT * This, XSelectionRequestEvent * req)
162 {
163  XEvent xev;
164 
165  DEBUG_CLIPBOARD(("xclip_refuse_selection: requestor=0x%08x, target=%s, property=%s\n",
166  (unsigned) req->requestor, XGetAtomName(This->display, req->target),
167  XGetAtomName(This->display, req->property)));
168 
169  xev.xselection.type = SelectionNotify;
170  xev.xselection.serial = 0;
171  xev.xselection.send_event = True;
172  xev.xselection.requestor = req->requestor;
173  xev.xselection.selection = req->selection;
174  xev.xselection.target = req->target;
175  xev.xselection.property = None;
176  xev.xselection.time = req->time;
177  XSendEvent(This->display, req->requestor, False, NoEventMask, &xev);
178 }
179 
180 /* Wrapper for cliprdr_send_data which also cleans the request state. */
181 static void
183 {
184  if (This->xclip.rdp_clipboard_request_format != 0)
185  {
187  This->xclip.rdp_clipboard_request_format = 0;
188  if (!This->xclip.rdesktop_is_selection_owner)
190  }
191 }
192 
193 /* Last resort, when we have to provide clipboard data but for whatever
194  reason couldn't get any.
195  */
196 static void
198 {
200 }
201 
202 /* Replies with clipboard data to RDP, converting it from the target format
203  to the expected RDP format as necessary. Returns true if data was sent.
204  */
205 static Bool
207 {
208  DEBUG_CLIPBOARD(("xclip_send_data_with_convert: target=%s, size=%u\n",
209  XGetAtomName(This->display, target), (unsigned) source_size));
210 
211 #ifdef USE_UNICODE_CLIPBOARD
212  if (target == This->xclip.format_string_atom ||
213  target == This->xclip.format_unicode_atom || target == This->xclip.format_utf8_string_atom)
214  {
215  size_t unicode_buffer_size;
216  char *unicode_buffer;
217  iconv_t cd;
218  size_t unicode_buffer_size_remaining;
219  char *unicode_buffer_remaining;
220  char *data_remaining;
221  size_t data_size_remaining;
222  uint32 translated_data_size;
223  uint8 *translated_data;
224 
225  if (This->xclip.rdp_clipboard_request_format != RDP_CF_TEXT)
226  return False;
227 
228  /* Make an attempt to convert any string we send to Unicode.
229  We don't know what the RDP server's ANSI Codepage is, or how to convert
230  to it, so using CF_TEXT is not safe (and is unnecessary, since all
231  WinNT versions are Unicode-minded).
232  */
233  if (target == This->xclip.format_string_atom)
234  {
235  char *locale_charset = nl_langinfo(CODESET);
236  cd = iconv_open(WINDOWS_CODEPAGE, locale_charset);
237  if (cd == (iconv_t) - 1)
238  {
239  DEBUG_CLIPBOARD(("Locale charset %s not found in iconv. Unable to convert clipboard text.\n", locale_charset));
240  return False;
241  }
242  unicode_buffer_size = source_size * 4;
243  }
244  else if (target == This->xclip.format_unicode_atom)
245  {
246  cd = iconv_open(WINDOWS_CODEPAGE, "UCS-2");
247  if (cd == (iconv_t) - 1)
248  {
249  return False;
250  }
251  unicode_buffer_size = source_size;
252  }
253  else if (target == This->xclip.format_utf8_string_atom)
254  {
255  cd = iconv_open(WINDOWS_CODEPAGE, "UTF-8");
256  if (cd == (iconv_t) - 1)
257  {
258  return False;
259  }
260  /* UTF-8 is guaranteed to be less or equally compact
261  as UTF-16 for all Unicode chars >=2 bytes.
262  */
263  unicode_buffer_size = source_size * 2;
264  }
265  else
266  {
267  return False;
268  }
269 
270  unicode_buffer = xmalloc(unicode_buffer_size);
271  unicode_buffer_size_remaining = unicode_buffer_size;
272  unicode_buffer_remaining = unicode_buffer;
273  data_remaining = (char *) source;
274  data_size_remaining = source_size;
275  iconv(cd, (ICONV_CONST char **) &data_remaining, &data_size_remaining,
276  &unicode_buffer_remaining, &unicode_buffer_size_remaining);
277  iconv_close(cd);
278 
279  /* translate linebreaks */
280  translated_data_size = unicode_buffer_size - unicode_buffer_size_remaining;
281  translated_data = utf16_lf2crlf((uint8 *) unicode_buffer, &translated_data_size);
282  if (translated_data != NULL)
283  {
284  DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n",
285  translated_data_size));
286  helper_cliprdr_send_response(This, translated_data, translated_data_size);
287  xfree(translated_data); /* Not the same thing as XFree! */
288  }
289 
290  xfree(unicode_buffer);
291 
292  return True;
293  }
294 #else
295  if (target == This->xclip.format_string_atom)
296  {
297  uint8 *translated_data;
298  uint32 length = source_size;
299 
300  if (This->xclip.rdp_clipboard_request_format != RDP_CF_TEXT)
301  return False;
302 
303  DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
304  translated_data = lf2crlf(source, &length);
305  if (translated_data != NULL)
306  {
307  helper_cliprdr_send_response(This, translated_data, length);
308  xfree(translated_data); /* Not the same thing as XFree! */
309  }
310 
311  return True;
312  }
313 #endif
314  else if (target == This->xclip.rdesktop_native_atom)
315  {
316  helper_cliprdr_send_response(This, source, source_size + 1);
317 
318  return True;
319  }
320  else
321  {
322  return False;
323  }
324 }
325 
326 static void
328 {
329  XDeleteProperty(This->display, This->wnd, This->xclip.rdesktop_clipboard_target_atom);
330  XDeleteProperty(This->display, This->wnd, This->xclip.rdesktop_primary_timestamp_target_atom);
331  XDeleteProperty(This->display, This->wnd, This->xclip.rdesktop_clipboard_timestamp_target_atom);
332 }
333 
334 static void
336 {
337  XChangeProperty(This->display, DefaultRootWindow(This->display),
338  This->xclip.rdesktop_selection_notify_atom, XA_INTEGER, 32, PropModeReplace, NULL, 0);
339 }
340 
341 static void
343 {
344  Window primary_owner, clipboard_owner;
345 
346  if (This->xclip.probing_selections)
347  {
348  DEBUG_CLIPBOARD(("Already probing selections. Scheduling reprobe.\n"));
349  This->xclip.reprobe_selections = True;
350  return;
351  }
352 
353  DEBUG_CLIPBOARD(("Probing selections.\n"));
354 
355  This->xclip.probing_selections = True;
356  This->xclip.reprobe_selections = False;
357 
359 
360  if (This->xclip.auto_mode)
361  primary_owner = XGetSelectionOwner(This->display, This->xclip.primary_atom);
362  else
363  primary_owner = None;
364 
365  clipboard_owner = XGetSelectionOwner(This->display, This->xclip.clipboard_atom);
366 
367  /* If we own all relevant selections then don't do anything. */
368  if (((primary_owner == This->wnd) || !This->xclip.auto_mode) && (clipboard_owner == This->wnd))
369  goto end;
370 
371  /* Both available */
372  if ((primary_owner != None) && (clipboard_owner != None))
373  {
374  This->xclip.primary_timestamp = 0;
375  This->xclip.clipboard_timestamp = 0;
376  XConvertSelection(This->display, This->xclip.primary_atom, This->xclip.timestamp_atom,
377  This->xclip.rdesktop_primary_timestamp_target_atom, This->wnd, CurrentTime);
378  XConvertSelection(This->display, This->xclip.clipboard_atom, This->xclip.timestamp_atom,
379  This->xclip.rdesktop_clipboard_timestamp_target_atom, This->wnd, CurrentTime);
380  return;
381  }
382 
383  /* Just PRIMARY */
384  if (primary_owner != None)
385  {
386  XConvertSelection(This->display, This->xclip.primary_atom, This->xclip.targets_atom,
387  This->xclip.rdesktop_clipboard_target_atom, This->wnd, CurrentTime);
388  return;
389  }
390 
391  /* Just CLIPBOARD */
392  if (clipboard_owner != None)
393  {
394  XConvertSelection(This->display, This->xclip.clipboard_atom, This->xclip.targets_atom,
395  This->xclip.rdesktop_clipboard_target_atom, This->wnd, CurrentTime);
396  return;
397  }
398 
399  DEBUG_CLIPBOARD(("No owner of any selection.\n"));
400 
401  /* FIXME:
402  Without XFIXES, we cannot reliably know the formats offered by an
403  upcoming selection owner, so we just lie about him offering
404  RDP_CF_TEXT. */
406 
407  end:
408  This->xclip.probing_selections = False;
409 }
410 
411 /* This function is called for SelectionNotify events.
412  The SelectionNotify event is sent from the clipboard owner to the requestor
413  after his request was satisfied.
414  If this function is called, we're the requestor side. */
415 #ifndef MAKE_PROTO
416 void
418 {
419  unsigned long nitems, bytes_left;
420  XWindowAttributes wa;
421  Atom type;
422  Atom *supported_targets;
423  int res, i, format;
424  uint8 *data = NULL;
425 
426  if (event->property == None)
427  goto fail;
428 
429  DEBUG_CLIPBOARD(("xclip_handle_SelectionNotify: selection=%s, target=%s, property=%s\n",
430  XGetAtomName(This->display, event->selection),
431  XGetAtomName(This->display, event->target),
432  XGetAtomName(This->display, event->property)));
433 
434  if (event->target == This->xclip.timestamp_atom)
435  {
436  if (event->selection == This->xclip.primary_atom)
437  {
438  res = XGetWindowProperty(This->display, This->wnd,
439  This->xclip.rdesktop_primary_timestamp_target_atom, 0,
440  XMaxRequestSize(This->display), False, AnyPropertyType,
441  &type, &format, &nitems, &bytes_left, &data);
442  }
443  else
444  {
445  res = XGetWindowProperty(This->display, This->wnd,
446  This->xclip.rdesktop_clipboard_timestamp_target_atom, 0,
447  XMaxRequestSize(This->display), False, AnyPropertyType,
448  &type, &format, &nitems, &bytes_left, &data);
449  }
450 
451 
452  if ((res != Success) || (nitems != 1) || (format != 32))
453  {
454  DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
455  goto fail;
456  }
457 
458  if (event->selection == This->xclip.primary_atom)
459  {
460  This->xclip.primary_timestamp = *(Time *) data;
461  if (This->xclip.primary_timestamp == 0)
462  This->xclip.primary_timestamp++;
463  XDeleteProperty(This->display, This->wnd, This->xclip.rdesktop_primary_timestamp_target_atom);
464  DEBUG_CLIPBOARD(("Got PRIMARY timestamp: %u\n",
465  (unsigned) This->xclip.primary_timestamp));
466  }
467  else
468  {
469  This->xclip.clipboard_timestamp = *(Time *) data;
470  if (This->xclip.clipboard_timestamp == 0)
471  This->xclip.clipboard_timestamp++;
472  XDeleteProperty(This->display, This->wnd, This->xclip.rdesktop_clipboard_timestamp_target_atom);
473  DEBUG_CLIPBOARD(("Got CLIPBOARD timestamp: %u\n",
474  (unsigned) This->xclip.clipboard_timestamp));
475  }
476 
477  XFree(data);
478 
479  if (This->xclip.primary_timestamp && This->xclip.clipboard_timestamp)
480  {
481  if (This->xclip.primary_timestamp > This->xclip.clipboard_timestamp)
482  {
483  DEBUG_CLIPBOARD(("PRIMARY is most recent selection.\n"));
484  XConvertSelection(This->display, This->xclip.primary_atom, This->xclip.targets_atom,
485  This->xclip.rdesktop_clipboard_target_atom, This->wnd,
486  event->time);
487  }
488  else
489  {
490  DEBUG_CLIPBOARD(("CLIPBOARD is most recent selection.\n"));
491  XConvertSelection(This->display, This->xclip.clipboard_atom, This->xclip.targets_atom,
492  This->xclip.rdesktop_clipboard_target_atom, This->wnd,
493  event->time);
494  }
495  }
496 
497  return;
498  }
499 
500  if (This->xclip.probing_selections && This->xclip.reprobe_selections)
501  {
502  This->xclip.probing_selections = False;
504  return;
505  }
506 
507  res = XGetWindowProperty(This->display, This->wnd, This->xclip.rdesktop_clipboard_target_atom,
508  0, XMaxRequestSize(This->display), False, AnyPropertyType,
509  &type, &format, &nitems, &bytes_left, &data);
510 
512 
513  if (res != Success)
514  {
515  DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
516  goto fail;
517  }
518 
519  if (type == This->xclip.incr_atom)
520  {
521  DEBUG_CLIPBOARD(("Received INCR.\n"));
522 
523  XGetWindowAttributes(This->display, This->wnd, &wa);
524  if ((wa.your_event_mask | PropertyChangeMask) != wa.your_event_mask)
525  {
526  XSelectInput(This->display, This->wnd, (wa.your_event_mask | PropertyChangeMask));
527  }
528  XFree(data);
529  This->xclip.incr_target = event->target;
530  This->xclip.waiting_for_INCR = 1;
531  goto end;
532  }
533 
534  /* Negotiate target format */
535  if (event->target == This->xclip.targets_atom)
536  {
537  /* Determine the best of text This->xclip.targets that we have available:
538  Prefer UTF8_STRING > text/unicode (unspecified encoding) > STRING
539  (ignore TEXT and COMPOUND_TEXT because we don't have code to handle them)
540  */
541  int text_target_satisfaction = 0;
542  Atom best_text_target = 0; /* measures how much we're satisfied with what we found */
543  if (type != None)
544  {
545  supported_targets = (Atom *) data;
546  for (i = 0; i < nitems; i++)
547  {
548  DEBUG_CLIPBOARD(("Target %d: %s\n", i,
549  XGetAtomName(This->display, supported_targets[i])));
550  if (supported_targets[i] == This->xclip.format_string_atom)
551  {
552  if (text_target_satisfaction < 1)
553  {
554  DEBUG_CLIPBOARD(("Other party supports STRING, choosing that as best_target\n"));
555  best_text_target = supported_targets[i];
556  text_target_satisfaction = 1;
557  }
558  }
559 #ifdef USE_UNICODE_CLIPBOARD
560  else if (supported_targets[i] == This->xclip.format_unicode_atom)
561  {
562  if (text_target_satisfaction < 2)
563  {
564  DEBUG_CLIPBOARD(("Other party supports text/unicode, choosing that as best_target\n"));
565  best_text_target = supported_targets[i];
566  text_target_satisfaction = 2;
567  }
568  }
569  else if (supported_targets[i] == This->xclip.format_utf8_string_atom)
570  {
571  if (text_target_satisfaction < 3)
572  {
573  DEBUG_CLIPBOARD(("Other party supports UTF8_STRING, choosing that as best_target\n"));
574  best_text_target = supported_targets[i];
575  text_target_satisfaction = 3;
576  }
577  }
578 #endif
579  else if (supported_targets[i] == This->xclip.rdesktop_clipboard_formats_atom)
580  {
581  if (This->xclip.probing_selections && (text_target_satisfaction < 4))
582  {
583  DEBUG_CLIPBOARD(("Other party supports native formats, choosing that as best_target\n"));
584  best_text_target = supported_targets[i];
585  text_target_satisfaction = 4;
586  }
587  }
588  }
589  }
590 
591  /* Kickstarting the next step in the process of satisfying RDP's
592  clipboard request -- specifically, requesting the actual clipboard data.
593  */
594  if ((best_text_target != 0)
595  && (!This->xclip.probing_selections
596  || (best_text_target == This->xclip.rdesktop_clipboard_formats_atom)))
597  {
598  XConvertSelection(This->display, event->selection, best_text_target,
599  This->xclip.rdesktop_clipboard_target_atom, This->wnd, event->time);
600  goto end;
601  }
602  else
603  {
604  DEBUG_CLIPBOARD(("Unable to find a textual target to satisfy RDP clipboard text request\n"));
605  goto fail;
606  }
607  }
608  else
609  {
610  if (This->xclip.probing_selections)
611  {
612  Window primary_owner, clipboard_owner;
613 
614  /* FIXME:
615  Without XFIXES, we must make sure that the other
616  rdesktop owns all relevant selections or we might try
617  to get a native format from non-rdesktop window later
618  on. */
619 
620  clipboard_owner = XGetSelectionOwner(This->display, This->xclip.clipboard_atom);
621 
622  if (This->xclip.auto_mode)
623  primary_owner = XGetSelectionOwner(This->display, This->xclip.primary_atom);
624  else
625  primary_owner = clipboard_owner;
626 
627  if (primary_owner != clipboard_owner)
628  goto fail;
629 
630  DEBUG_CLIPBOARD(("Got fellow rdesktop formats\n"));
631  This->xclip.probing_selections = False;
632  This->xclip.rdesktop_is_selection_owner = True;
634  }
635  else if (!xclip_send_data_with_convert(This, data, nitems, event->target))
636  {
637  goto fail;
638  }
639  }
640 
641  end:
642  if (data)
643  XFree(data);
644 
645  return;
646 
647  fail:
649  if (This->xclip.probing_selections)
650  {
651  DEBUG_CLIPBOARD(("Unable to find suitable target. Using default text format.\n"));
652  This->xclip.probing_selections = False;
653  This->xclip.rdesktop_is_selection_owner = False;
654 
655  /* FIXME:
656  Without XFIXES, we cannot reliably know the formats offered by an
657  upcoming selection owner, so we just lie about him offering
658  RDP_CF_TEXT. */
660  }
661  else
662  {
664  }
665  goto end;
666 }
667 
668 /* This function is called for SelectionRequest events.
669  The SelectionRequest event is sent from the requestor to the clipboard owner
670  to request clipboard data.
671  */
672 void
673 xclip_handle_SelectionRequest(RDPCLIENT * This, XSelectionRequestEvent * event)
674 {
675  unsigned long nitems, bytes_left;
676  unsigned char *prop_return;
677  int format, res;
678  Atom type;
679 
680  DEBUG_CLIPBOARD(("xclip_handle_SelectionRequest: selection=%s, target=%s, property=%s\n",
681  XGetAtomName(This->display, event->selection),
682  XGetAtomName(This->display, event->target),
683  XGetAtomName(This->display, event->property)));
684 
685  if (event->target == This->xclip.targets_atom)
686  {
687  xclip_provide_selection(This, event, XA_ATOM, 32, (uint8 *) & This->xclip.targets, This->xclip.num_targets);
688  return;
689  }
690  else if (event->target == This->xclip.timestamp_atom)
691  {
692  xclip_provide_selection(This, event, XA_INTEGER, 32, (uint8 *) & This->xclip.acquire_time, 1);
693  return;
694  }
695  else if (event->target == This->xclip.rdesktop_clipboard_formats_atom)
696  {
697  xclip_provide_selection(This, event, XA_STRING, 8, This->xclip.formats_data, This->xclip.formats_data_length);
698  }
699  else
700  {
701  /* All the following This->xclip.targets require an async operation with the RDP server
702  and currently we don't do X clipboard request queueing so we can only
703  handle one such request at a time. */
704  if (This->xclip.has_selection_request)
705  {
706  DEBUG_CLIPBOARD(("Error: Another clipboard request was already sent to the RDP server and not yet responded. Refusing this request.\n"));
708  return;
709  }
710  if (event->target == This->xclip.rdesktop_native_atom)
711  {
712  /* Before the requestor makes a request for the _RDESKTOP_NATIVE target,
713  he should declare requestor[property] = CF_SOMETHING. */
714  res = XGetWindowProperty(This->display, event->requestor,
715  event->property, 0, 1, True,
716  XA_INTEGER, &type, &format, &nitems, &bytes_left,
717  &prop_return);
718  if (res != Success)
719  {
720  DEBUG_CLIPBOARD(("Requested native format but didn't specifiy which.\n"));
722  return;
723  }
724 
725  format = *(uint32 *) prop_return;
726  XFree(prop_return);
727  }
728  else if (event->target == This->xclip.format_string_atom || event->target == XA_STRING)
729  {
730  /* STRING and XA_STRING are defined to be ISO8859-1 */
731  format = CF_TEXT;
732  }
733  else if (event->target == This->xclip.format_utf8_string_atom)
734  {
735 #ifdef USE_UNICODE_CLIPBOARD
737 #else
738  DEBUG_CLIPBOARD(("Requested target unavailable due to lack of Unicode support. (It was not in TARGETS, so why did you ask for it?!)\n"));
740  return;
741 #endif
742  }
743  else if (event->target == This->xclip.format_unicode_atom)
744  {
745  /* Assuming text/unicode to be UTF-16 */
747  }
748  else
749  {
750  DEBUG_CLIPBOARD(("Requested target unavailable. (It was not in TARGETS, so why did you ask for it?!)\n"));
752  return;
753  }
754 
756  This->xclip.selection_request = *event;
757  This->xclip.has_selection_request = True;
758  return; /* wait for data */
759  }
760 }
761 
762 /* While this rdesktop holds ownership over the clipboard, it means the clipboard data
763  is offered by the RDP server (and when it is pasted inside RDP, there's no network
764  roundtrip).
765 
766  This event (SelectionClear) symbolizes this rdesktop lost onwership of the clipboard
767  to some other X client. We should find out what clipboard formats this other
768  client offers and announce that to RDP. */
769 void
771 {
772  DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
775 }
776 
777 /* Called when any property changes in our window or the root window. */
778 void
780 {
781  unsigned long nitems;
782  unsigned long offset = 0;
783  unsigned long bytes_left = 1;
784  int format;
785  XWindowAttributes wa;
786  uint8 *data;
787  Atom type;
788 
789  if (event->state == PropertyNewValue && This->xclip.waiting_for_INCR)
790  {
791  DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: This->xclip.waiting_for_INCR != 0\n"));
792 
793  while (bytes_left > 0)
794  {
795  /* Unlike the specification, we don't set the 'delete' arugment to True
796  since we slurp the INCR's chunks in even-smaller chunks of 4096 bytes. */
797  if ((XGetWindowProperty
798  (This->display, This->wnd, This->xclip.rdesktop_clipboard_target_atom, offset, 4096L,
799  False, AnyPropertyType, &type, &format, &nitems, &bytes_left,
800  &data) != Success))
801  {
802  XFree(data);
803  return;
804  }
805 
806  if (nitems == 0)
807  {
808  /* INCR transfer finished */
809  XGetWindowAttributes(This->display, This->wnd, &wa);
810  XSelectInput(This->display, This->wnd,
811  (wa.your_event_mask ^ PropertyChangeMask));
812  XFree(data);
813  This->xclip.waiting_for_INCR = 0;
814 
815  if (This->xclip.clip_buflen > 0)
816  {
818  (This, This->xclip.clip_buffer, This->xclip.clip_buflen, This->xclip.incr_target))
819  {
821  }
822  xfree(This->xclip.clip_buffer);
823  This->xclip.clip_buffer = NULL;
824  This->xclip.clip_buflen = 0;
825  }
826  }
827  else
828  {
829  /* Another chunk in the INCR transfer */
830  offset += (nitems / 4); /* offset at which to begin the next slurp */
831  This->xclip.clip_buffer = xrealloc(This->xclip.clip_buffer, This->xclip.clip_buflen + nitems);
832  memcpy(This->xclip.clip_buffer + This->xclip.clip_buflen, data, nitems);
833  This->xclip.clip_buflen += nitems;
834 
835  XFree(data);
836  }
837  }
838  XDeleteProperty(This->display, This->wnd, This->xclip.rdesktop_clipboard_target_atom);
839  return;
840  }
841 
842  if ((event->atom == This->xclip.rdesktop_selection_notify_atom) &&
843  (event->window == DefaultRootWindow(This->display)))
845 }
846 #endif
847 
848 
849 /* Called when the RDP server announces new clipboard data formats.
850  In response, we:
851  - take ownership over the clipboard
852  - declare those formats in their Windows native form
853  to other rdesktop instances on this X server */
854 void
856 {
857  This->xclip.acquire_time = This->last_gesturetime;
858 
859  XSetSelectionOwner(This->display, This->xclip.primary_atom, This->wnd, This->xclip.acquire_time);
860  if (XGetSelectionOwner(This->display, This->xclip.primary_atom) != This->wnd)
861  warning("Failed to aquire ownership of PRIMARY clipboard\n");
862 
863  XSetSelectionOwner(This->display, This->xclip.clipboard_atom, This->wnd, This->xclip.acquire_time);
864  if (XGetSelectionOwner(This->display, This->xclip.clipboard_atom) != This->wnd)
865  warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
866 
867  if (This->xclip.formats_data)
868  xfree(This->xclip.formats_data);
869  This->xclip.formats_data = xmalloc(length);
870  memcpy(This->xclip.formats_data, data, length);
871  This->xclip.formats_data_length = length;
872 
874 }
875 
876 /* Called when the RDP server responds with clipboard data (after we've requested it). */
877 void
879 {
880  BOOL free_data = False;
881 
882  if (length == 0)
883  {
884  xclip_refuse_selection(This, &This->xclip.selection_request);
885  This->xclip.has_selection_request = False;
886  return;
887  }
888 
889  if (This->xclip.selection_request.target == This->xclip.format_string_atom || This->xclip.selection_request.target == XA_STRING)
890  {
891  /* We're expecting a CF_TEXT response */
892  uint8 *firstnull;
893 
894  /* translate linebreaks */
895  crlf2lf(data, &length);
896 
897  /* Only send data up to null byte, if any */
898  firstnull = (uint8 *) strchr((char *) data, '\0');
899  if (firstnull)
900  {
901  length = firstnull - data + 1;
902  }
903  }
904 #ifdef USE_UNICODE_CLIPBOARD
905  else if (This->xclip.selection_request.target == This->xclip.format_utf8_string_atom)
906  {
907  /* We're expecting a CF_UNICODETEXT response */
909  if (cd != (iconv_t) - 1)
910  {
911  size_t utf8_length = length * 2;
912  char *utf8_data = malloc(utf8_length);
913  size_t utf8_length_remaining = utf8_length;
914  char *utf8_data_remaining = utf8_data;
915  char *data_remaining = (char *) data;
916  size_t length_remaining = (size_t) length;
917  if (utf8_data == NULL)
918  {
919  iconv_close(cd);
920  return;
921  }
922  iconv(cd, (ICONV_CONST char **) &data_remaining, &length_remaining,
923  &utf8_data_remaining, &utf8_length_remaining);
924  iconv_close(cd);
925  free_data = True;
926  data = (uint8 *) utf8_data;
927  length = utf8_length - utf8_length_remaining;
928  }
929  }
930  else if (This->xclip.selection_request.target == This->xclip.format_unicode_atom)
931  {
932  /* We're expecting a CF_UNICODETEXT response, so what we're
933  receiving matches our requirements and there's no need
934  for further conversions. */
935  }
936 #endif
937  else if (This->xclip.selection_request.target == This->xclip.rdesktop_native_atom)
938  {
939  /* Pass as-is */
940  }
941  else
942  {
943  DEBUG_CLIPBOARD(("ui_clip_handle_data: BUG! I don't know how to convert selection target %s!\n", XGetAtomName(This->display, This->xclip.selection_request.target)));
944  xclip_refuse_selection(This, &This->xclip.selection_request);
945  This->xclip.has_selection_request = False;
946  return;
947  }
948 
949  xclip_provide_selection(This, &This->xclip.selection_request, This->xclip.selection_request.target, 8, data, length - 1);
950  This->xclip.has_selection_request = False;
951 
952  if (free_data)
953  free(data);
954 }
955 
956 void
958 {
959  xclip_refuse_selection(This, &This->xclip.selection_request);
960  This->xclip.has_selection_request = False;
961 }
962 
963 void
965 {
966  Window primary_owner, clipboard_owner;
967 
968  DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
969  This->xclip.rdp_clipboard_request_format = format;
970 
971  if (This->xclip.probing_selections)
972  {
973  DEBUG_CLIPBOARD(("ui_clip_request_data: Selection probe in progress. Cannot handle request.\n"));
975  return;
976  }
977 
979 
980  if (This->xclip.rdesktop_is_selection_owner)
981  {
982  XChangeProperty(This->display, This->wnd, This->xclip.rdesktop_clipboard_target_atom,
983  XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
984 
985  XConvertSelection(This->display, This->xclip.primary_atom, This->xclip.rdesktop_native_atom,
986  This->xclip.rdesktop_clipboard_target_atom, This->wnd, CurrentTime);
987  return;
988  }
989 
990  if (This->xclip.auto_mode)
991  primary_owner = XGetSelectionOwner(This->display, This->xclip.primary_atom);
992  else
993  primary_owner = None;
994 
995  clipboard_owner = XGetSelectionOwner(This->display, This->xclip.clipboard_atom);
996 
997  /* Both available */
998  if ((primary_owner != None) && (clipboard_owner != None))
999  {
1000  This->xclip.primary_timestamp = 0;
1001  This->xclip.clipboard_timestamp = 0;
1002  XConvertSelection(This->display, This->xclip.primary_atom, This->xclip.timestamp_atom,
1003  This->xclip.rdesktop_primary_timestamp_target_atom, This->wnd, CurrentTime);
1004  XConvertSelection(This->display, This->xclip.clipboard_atom, This->xclip.timestamp_atom,
1005  This->xclip.rdesktop_clipboard_timestamp_target_atom, This->wnd, CurrentTime);
1006  return;
1007  }
1008 
1009  /* Just PRIMARY */
1010  if (primary_owner != None)
1011  {
1012  XConvertSelection(This->display, This->xclip.primary_atom, This->xclip.targets_atom,
1013  This->xclip.rdesktop_clipboard_target_atom, This->wnd, CurrentTime);
1014  return;
1015  }
1016 
1017  /* Just CLIPBOARD */
1018  if (clipboard_owner != None)
1019  {
1020  XConvertSelection(This->display, This->xclip.clipboard_atom, This->xclip.targets_atom,
1021  This->xclip.rdesktop_clipboard_target_atom, This->wnd, CurrentTime);
1022  return;
1023  }
1024 
1025  /* No data available */
1027 }
1028 
1029 void
1031 {
1033 }
1034 
1035 void
1037 {
1038  This->rdpclip = True;
1039 
1040  if (str_startswith(optarg, "PRIMARYCLIPBOARD"))
1041  This->xclip.auto_mode = True;
1042  else if (str_startswith(optarg, "CLIPBOARD"))
1043  This->xclip.auto_mode = False;
1044  else
1045  {
1046  warning("Invalid clipboard mode '%s'.\n", optarg);
1047  This->rdpclip = False;
1048  }
1049 }
1050 
1051 void
1053 {
1054  if (!This->rdpclip)
1055  return;
1056 
1057  if (!cliprdr_init(This))
1058  return;
1059 
1060  This->xclip.primary_atom = XInternAtom(This->display, "PRIMARY", False);
1061  This->xclip.clipboard_atom = XInternAtom(This->display, "CLIPBOARD", False);
1062  This->xclip.targets_atom = XInternAtom(This->display, "TARGETS", False);
1063  This->xclip.timestamp_atom = XInternAtom(This->display, "TIMESTAMP", False);
1064  This->xclip.rdesktop_clipboard_target_atom =
1065  XInternAtom(This->display, "_RDESKTOP_CLIPBOARD_TARGET", False);
1066  This->xclip.rdesktop_primary_timestamp_target_atom =
1067  XInternAtom(This->display, "_RDESKTOP_PRIMARY_TIMESTAMP_TARGET", False);
1068  This->xclip.rdesktop_clipboard_timestamp_target_atom =
1069  XInternAtom(This->display, "_RDESKTOP_CLIPBOARD_TIMESTAMP_TARGET", False);
1070  This->xclip.incr_atom = XInternAtom(This->display, "INCR", False);
1071  This->xclip.format_string_atom = XInternAtom(This->display, "STRING", False);
1072  This->xclip.format_utf8_string_atom = XInternAtom(This->display, "UTF8_STRING", False);
1073  This->xclip.format_unicode_atom = XInternAtom(This->display, "text/unicode", False);
1074 
1075  /* rdesktop sets _RDESKTOP_SELECTION_NOTIFY on the root window when acquiring the clipboard.
1076  Other interested rdesktops can use this to notify their server of the available formats. */
1077  This->xclip.rdesktop_selection_notify_atom =
1078  XInternAtom(This->display, "_RDESKTOP_SELECTION_NOTIFY", False);
1079  XSelectInput(This->display, DefaultRootWindow(This->display), PropertyChangeMask);
1080  This->xclip.probing_selections = False;
1081 
1082  This->xclip.rdesktop_native_atom = XInternAtom(This->display, "_RDESKTOP_NATIVE", False);
1083  This->xclip.rdesktop_clipboard_formats_atom =
1084  XInternAtom(This->display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
1085  This->xclip.rdesktop_primary_owner_atom = XInternAtom(This->display, "_RDESKTOP_PRIMARY_OWNER", False);
1086  This->xclip.rdesktop_clipboard_owner_atom = XInternAtom(This->display, "_RDESKTOP_CLIPBOARD_OWNER", False);
1087 
1088  This->xclip.num_targets = 0;
1089  This->xclip.targets[This->xclip.num_targets++] = This->xclip.targets_atom;
1090  This->xclip.targets[This->xclip.num_targets++] = This->xclip.timestamp_atom;
1091  This->xclip.targets[This->xclip.num_targets++] = This->xclip.rdesktop_native_atom;
1092  This->xclip.targets[This->xclip.num_targets++] = This->xclip.rdesktop_clipboard_formats_atom;
1093 #ifdef USE_UNICODE_CLIPBOARD
1094  This->xclip.targets[This->xclip.num_targets++] = This->xclip.format_utf8_string_atom;
1095 #endif
1096  This->xclip.targets[This->xclip.num_targets++] = This->xclip.format_unicode_atom;
1097  This->xclip.targets[This->xclip.num_targets++] = This->xclip.format_string_atom;
1098  This->xclip.targets[This->xclip.num_targets++] = XA_STRING;
1099 }
1100 
1101 void
1103 {
1104  if (XGetSelectionOwner(This->display, This->xclip.primary_atom) == This->wnd)
1105  XSetSelectionOwner(This->display, This->xclip.primary_atom, None, This->xclip.acquire_time);
1106  if (XGetSelectionOwner(This->display, This->xclip.clipboard_atom) == This->wnd)
1107  XSetSelectionOwner(This->display, This->xclip.clipboard_atom, None, This->xclip.acquire_time);
1109 }
static uint8 * lf2crlf(uint8 *data, uint32 *length)
Definition: xclip.c:110
RD_BOOL str_startswith(const char *s, const char *prefix)
Definition: rdesktop.c:1235
static void xclip_probe_selections(RDPCLIENT *This)
Definition: xclip.c:342
void ui_clip_request_failed(RDPCLIENT *This)
Definition: xclip.c:957
static struct cd_image cd
Definition: cdmake.c:182
unsigned int uint32
Definition: types.h:32
#define free
Definition: debug_ros.c:5
GLintptr offset
Definition: glext.h:5920
GLuint GLuint GLsizei GLenum type
Definition: gl.h:1545
void xclip_handle_SelectionRequest(RDPCLIENT *This, XSelectionRequestEvent *event)
Definition: xclip.c:673
GLuint GLuint end
Definition: gl.h:1545
_Out_ RTL_ATOM * Atom
Definition: class.h:54
static void xclip_clear_target_props(RDPCLIENT *This)
Definition: xclip.c:327
void xclip_deinit(RDPCLIENT *This)
Definition: xclip.c:1102
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: gl.h:1546
static const char utf8_length[128]
Definition: utf8.c:28
RD_BOOL cliprdr_init(void)
static void helper_cliprdr_send_empty_response(RDPCLIENT *This)
Definition: xclip.c:197
static void xclip_provide_selection(RDPCLIENT *This, XSelectionRequestEvent *req, Atom type, unsigned int format, uint8 *data, uint32 length)
Definition: xclip.c:136
void ui_clip_set_mode(RDPCLIENT *This, const char *optarg)
Definition: xclip.c:1036
void cliprdr_send_data_request(uint32 format)
void cliprdr_send_native_format_announce(uint8 *formats_data, uint32 formats_data_length)
size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
Definition: win_iconv.c:771
Definition: window.c:29
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
void ui_clip_sync(RDPCLIENT *This)
Definition: xclip.c:1030
void * xrealloc(void *oldmem, size_t size)
Definition: uimain.c:736
#define None
Definition: i386-dis.c:444
void * xmalloc(int size)
Definition: uimain.c:747
unsigned int BOOL
Definition: ntddk_ex.h:94
#define DEBUG_CLIPBOARD(args)
Definition: rdesktop.h:147
#define CF_UNICODETEXT
Definition: constants.h:408
smooth NULL
Definition: ftsmooth.c:416
#define CF_TEXT
Definition: constants.h:396
__kernel_size_t size_t
Definition: linux.h:237
#define True
Definition: types.h:24
#define False
Definition: types.h:25
GLsizeiptr size
Definition: glext.h:5919
void ui_clip_request_data(RDPCLIENT *This, uint32 format)
Definition: xclip.c:964
void xfree(void *mem)
Definition: uimain.c:758
#define WINDOWS_CODEPAGE
Definition: constants.h:24
#define ICONV_CONST
Definition: win32config.h:27
GLenum GLuint GLenum GLsizei length
Definition: glext.h:5579
void xclip_handle_SelectionNotify(RDPCLIENT *This, XSelectionEvent *event)
Definition: xclip.c:417
static Bool xclip_send_data_with_convert(RDPCLIENT *This, uint8 *source, size_t source_size, Atom target)
Definition: xclip.c:206
unsigned char uint8
Definition: types.h:28
void ui_clip_format_announce(RDPCLIENT *This, uint8 *data, uint32 length)
Definition: xclip.c:855
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: gl.h:1950
void cliprdr_send_data(uint8 *data, uint32 length)
static const WCHAR L[]
Definition: oid.c:1250
#define memcpy(s1, s2, n)
Definition: mkisofs.h:878
iconv_t iconv_open(const char *tocode, const char *fromcode)
Definition: win_iconv.c:730
GLenum src
Definition: glext.h:6340
struct _cl_event * event
Definition: glext.h:7739
#define RDP_CF_TEXT
Definition: xclip.c:48
static void xclip_refuse_selection(RDPCLIENT *This, XSelectionRequestEvent *req)
Definition: xclip.c:161
void xclip_handle_SelectionClear(RDPCLIENT *This)
Definition: xclip.c:770
void xclip_init(RDPCLIENT *This)
Definition: xclip.c:1052
static void crlf2lf(uint8 *data, uint32 *length)
Definition: xclip.c:57
unsigned short uint16
Definition: types.h:30
const char * optarg
Definition: getopt.c:49
void cliprdr_send_simple_native_format_announce(uint32 format)
GLenum GLenum dst
Definition: glext.h:6340
char * strchr(const char *String, int ch)
Definition: utclib.c:501
GLuint res
Definition: glext.h:9613
GLenum target
Definition: glext.h:7315
#define malloc
Definition: debug_ros.c:4
static void xclip_notify_change(RDPCLIENT *This)
Definition: xclip.c:335
int iconv_close(iconv_t cd)
Definition: win_iconv.c:756
GLfloat GLfloat p
Definition: glext.h:8902
void xclip_handle_PropertyNotify(RDPCLIENT *This, XPropertyEvent *event)
Definition: xclip.c:779
GLuint64EXT * result
Definition: glext.h:11304
static void helper_cliprdr_send_response(RDPCLIENT *This, uint8 *data, uint32 length)
Definition: xclip.c:182
void ui_clip_handle_data(RDPCLIENT *This, uint8 *data, uint32 length)
Definition: xclip.c:878
static PLARGE_INTEGER Time
Definition: time.c:105
char Bool
Definition: ftraster.c:317
#define warning(s)
Definition: debug.h:71