/[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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1214 - (hide annotations)
Tue Mar 28 13:56:08 2006 UTC (18 years, 2 months ago) by ossman_
File MIME type: text/plain
File size: 35971 byte(s)
Make sure we notify other rdesktops that we exited so that they can update
their clipboard status.

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

  ViewVC Help
Powered by ViewVC 1.1.26