/[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 1365 - (hide annotations)
Thu Jan 4 05:39:39 2007 UTC (17 years, 5 months ago) by jsorg71
File MIME type: text/plain
File size: 36097 byte(s)
copyright year update

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

  ViewVC Help
Powered by ViewVC 1.1.26