/[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 1404 - (hide annotations)
Wed Apr 25 12:44:26 2007 UTC (17 years, 2 months ago) by astrand
File MIME type: text/plain
File size: 36433 byte(s)
Applied patch #1700909 from Ilya Konstantinov: Linebreak (CR-LF) fixes for clipboard code

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

  ViewVC Help
Powered by ViewVC 1.1.26