xclip.c revision d65680efa46fa49e8bf14e67b29b782510ff934c
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* -*- c-basic-offset: 8 -*-
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync rdesktop: A Remote Desktop Protocol client.
1ce069685b24d243eb0464f46d4c56b250c64445vboxsync Protocol services - Clipboard functions
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync Copyright (C) Erik Forsberg <forsberg@cendio.se> 2003-2007
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync Copyright (C) Matthew Chapman 2003-2007
3a8aa22ef125135ef67bfc396771bcee15ef02dfvboxsync This program is free software; you can redistribute it and/or modify
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync it under the terms of the GNU General Public License as published by
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync the Free Software Foundation; either version 2 of the License, or
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync (at your option) any later version.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync This program is distributed in the hope that it will be useful,
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync but WITHOUT ANY WARRANTY; without even the implied warranty of
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync GNU General Public License for more details.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync You should have received a copy of the GNU General Public License
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync along with this program; if not, write to the Free Software
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync * Sun GPL Disclaimer: For the avoidance of doubt, except that if any license choice
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync * other than GPL or LGPL is available it will apply instead, Sun elects to use only
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync * the General Public License version 2 (GPLv2) at this time for any software where
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync * a choice of GPL license versions is made available with the language indicating
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync * that GPLv2 or any later version may be used, or where a choice of which version
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync * of the GPL is applied is otherwise unspecified.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync To gain better understanding of this code, one could be assisted by the following documents:
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync - Inter-Client Communication Conventions Manual (ICCCM)
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync PDF: http://ftp.xfree86.org/pub/XFree86/4.5.0/doc/PDF/icccm.pdf
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync - MSDN: Clipboard Formats
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/dataexchange/clipboard/clipboardformats.asp
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Mode of operation.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync - Auto: Look at both PRIMARY and CLIPBOARD and use the most recent.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync - Non-auto: Look at just CLIPBOARD. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Atoms of the two X selections we're dealing with: CLIPBOARD (explicit-copy) and PRIMARY (selection-copy) */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Atom of the TARGETS clipboard target */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Atom of the TIMESTAMP clipboard target */
508452243fd3328f7b9e0405d39fb9dc004e31b8vboxsync/* Atom _RDESKTOP_CLIPBOARD_TARGET which is used as the 'property' argument in
508452243fd3328f7b9e0405d39fb9dc004e31b8vboxsync XConvertSelection calls: This is the property of our window into which
508452243fd3328f7b9e0405d39fb9dc004e31b8vboxsync XConvertSelection will store the received clipboard data. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Atoms _RDESKTOP_PRIMARY_TIMESTAMP_TARGET and _RDESKTOP_CLIPBOARD_TIMESTAMP_TARGET
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync are used to store the timestamps for when a window got ownership of the selections.
f409459bdd4c15cdb8d7fb6c6d54338cce9ac814vboxsync We use these to determine which is more recent and should be used. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsyncstatic Atom rdesktop_primary_timestamp_target_atom, rdesktop_clipboard_timestamp_target_atom;
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Storage for timestamps since we get them in two separate notifications. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Clipboard target for getting a list of native Windows clipboard formats. The
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync presence of this target indicates that the selection owner is another rdesktop. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* The clipboard target (X jargon for "clipboard format") for rdesktop-to-rdesktop
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync interchange of Windows native clipboard data. The requestor must supply the
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync desired native Windows clipboard format in the associated property. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Local copy of the list of native Windows clipboard formats. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* We need to know when another rdesktop process gets or loses ownership of a
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync selection. Without XFixes we do this by touching a property on the root window
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync which will generate PropertyNotify notifications. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* State variables that indicate if we're currently probing the targets of the
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync selection owner. reprobe_selections indicate that the ownership changed in
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync the middle of the current probe so it should be restarted. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsyncstatic RD_BOOL probing_selections, reprobe_selections;
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Atoms _RDESKTOP_PRIMARY_OWNER and _RDESKTOP_CLIPBOARD_OWNER. Used as properties
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync on the root window to indicate which selections that are owned by rdesktop. */
6e12ccc60ac657fb87e27b7a2b26e0a63bebe024vboxsyncstatic Atom rdesktop_primary_owner_atom, rdesktop_clipboard_owner_atom;
6e12ccc60ac657fb87e27b7a2b26e0a63bebe024vboxsyncstatic Atom format_string_atom, format_utf8_string_atom, format_unicode_atom;
6e12ccc60ac657fb87e27b7a2b26e0a63bebe024vboxsync/* Atom of the INCR clipboard type (see ICCCM on "INCR Properties") */
6e12ccc60ac657fb87e27b7a2b26e0a63bebe024vboxsync/* Stores the last "selection request" (= another X client requesting clipboard data from us).
6e12ccc60ac657fb87e27b7a2b26e0a63bebe024vboxsync To satisfy such a request, we request the clipboard data from the RDP server.
6e12ccc60ac657fb87e27b7a2b26e0a63bebe024vboxsync When we receive the response from the RDP server (asynchronously), this variable gives us
6e12ccc60ac657fb87e27b7a2b26e0a63bebe024vboxsync the context to proceed. */
6e12ccc60ac657fb87e27b7a2b26e0a63bebe024vboxsync/* Denotes we have a pending selection request. */
6e12ccc60ac657fb87e27b7a2b26e0a63bebe024vboxsync/* Stores the clipboard format (CF_TEXT, CF_UNICODETEXT etc.) requested in the last
6e12ccc60ac657fb87e27b7a2b26e0a63bebe024vboxsync CLIPDR_DATA_REQUEST (= the RDP server requesting clipboard data from us).
6e12ccc60ac657fb87e27b7a2b26e0a63bebe024vboxsync When we receive this data from whatever X client offering it, this variable gives us
6e12ccc60ac657fb87e27b7a2b26e0a63bebe024vboxsync the context to proceed.
6e12ccc60ac657fb87e27b7a2b26e0a63bebe024vboxsync/* Array of offered clipboard targets that will be sent to fellow X clients upon a TARGETS request. */
6e12ccc60ac657fb87e27b7a2b26e0a63bebe024vboxsync/* Denotes that an rdesktop (not this rdesktop) is owning the selection,
6e12ccc60ac657fb87e27b7a2b26e0a63bebe024vboxsync allowing us to interchange Windows native clipboard data directly. */
6e12ccc60ac657fb87e27b7a2b26e0a63bebe024vboxsync/* Time when we acquired the selection. */
657b2c9f6d33f08001e5fa6f6e0572dcf0391013vboxsync/* Denotes that an INCR ("chunked") transfer is in progress. */
657b2c9f6d33f08001e5fa6f6e0572dcf0391013vboxsync/* Denotes the target format of the ongoing INCR ("chunked") transfer. */
657b2c9f6d33f08001e5fa6f6e0572dcf0391013vboxsync/* Buffers an INCR transfer. */
657b2c9f6d33f08001e5fa6f6e0572dcf0391013vboxsync/* Denotes the size of g_clip_buffer. */
657b2c9f6d33f08001e5fa6f6e0572dcf0391013vboxsync/* Translates CR-LF to LF.
657b2c9f6d33f08001e5fa6f6e0572dcf0391013vboxsync Changes the string in-place.
9cb702c3a5fd2287c57c7c1e98a61ba9e357b4devboxsync Does not stop on embedded nulls.
657b2c9f6d33f08001e5fa6f6e0572dcf0391013vboxsync The length is updated. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Translate LF to CR-LF. To do this, we must allocate more memory.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync The returned string is null-terminated, as required by CF_UNICODETEXT.
ae017640afff8b6cc50453182a4edf2eb0903a12vboxsync The size is updated. */
b986941f0aa5155c7fd37da0aa5876675a7680e4vboxsync /* Worst case: Every char is LF */
3a8aa22ef125135ef67bfc396771bcee15ef02dfvboxsync /* Check for a reversed BOM */
b986941f0aa5155c7fd37da0aa5876675a7680e4vboxsync uint16 uvalue_previous = 0; /* Kept so we'll avoid translating CR-LF to CR-CR-LF */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Translate LF to CR-LF. To do this, we must allocate more memory.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync The length is updated. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync /* Worst case: Every char is LF */
3a8aa22ef125135ef67bfc396771bcee15ef02dfvboxsync uint8 previous = '\0'; /* Kept to avoid translating CR-LF to CR-CR-LF */
3a8aa22ef125135ef67bfc396771bcee15ef02dfvboxsync *o++ = '\x0d';
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync *o++ = *p++;
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync /* Convenience */
42aef05f4b27fb393967e581be04be455064c80avboxsync *o++ = '\0';
4328e87247f4a96449677e199c7e99ef516fc1cevboxsyncxclip_provide_selection(XSelectionRequestEvent * req, Atom type, unsigned int format, uint8 * data,
3a8aa22ef125135ef67bfc396771bcee15ef02dfvboxsync 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));
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync XChangeProperty(g_display, req->requestor, req->property,
3a8aa22ef125135ef67bfc396771bcee15ef02dfvboxsync XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);
3a8aa22ef125135ef67bfc396771bcee15ef02dfvboxsync/* Replies a clipboard requestor, telling that we're unable to satisfy his request for whatever reason.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync This has the benefit of finalizing the clipboard negotiation and thus not leaving our requestor
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync lingering (and, potentially, stuck). */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsyncxclip_refuse_selection(XSelectionRequestEvent * req)
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync DEBUG_CLIPBOARD(("xclip_refuse_selection: requestor=0x%08x, target=%s, property=%s\n",
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync (unsigned) req->requestor, XGetAtomName(g_display, req->target),
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Wrapper for cliprdr_send_data which also cleans the request state. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsynchelper_cliprdr_send_response(uint8 * data, uint32 length)
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Last resort, when we have to provide clipboard data but for whatever
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync reason couldn't get any.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Replies with clipboard data to RDP, converting it from the target format
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync to the expected RDP format as necessary. Returns true if data was sent.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsyncxclip_send_data_with_convert(uint8 * source, size_t source_size, Atom target)
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync DEBUG_CLIPBOARD(("xclip_send_data_with_convert: target=%s, size=%u\n",
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync XGetAtomName(g_display, target), (unsigned) source_size));
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync target == format_unicode_atom || target == format_utf8_string_atom)
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync /* Make an attempt to convert any string we send to Unicode.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync We don't know what the RDP server's ANSI Codepage is, or how to convert
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync to it, so using CF_TEXT is not safe (and is unnecessary, since all
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync WinNT versions are Unicode-minded).
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync DEBUG_CLIPBOARD(("Locale charset %s not found in iconv. Unable to convert clipboard text.\n", locale_charset));
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync /* UTF-8 is guaranteed to be less or equally compact
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync as UTF-16 for all Unicode chars >=2 bytes.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync unicode_buffer_size_remaining = unicode_buffer_size;
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync iconv(cd, (ICONV_CONST char **) &data_remaining, &data_size_remaining,
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync &unicode_buffer_remaining, &unicode_buffer_size_remaining);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync /* translate linebreaks */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync translated_data_size = unicode_buffer_size - unicode_buffer_size_remaining;
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync translated_data = utf16_lf2crlf((uint8 *) unicode_buffer, &translated_data_size);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n",
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync helper_cliprdr_send_response(translated_data, translated_data_size);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync xfree(translated_data); /* Not the same thing as XFree! */
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync helper_cliprdr_send_response(translated_data, length);
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync xfree(translated_data); /* Not the same thing as XFree! */
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync helper_cliprdr_send_response(source, source_size + 1);
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync XDeleteProperty(g_display, g_wnd, rdesktop_primary_timestamp_target_atom);
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_timestamp_target_atom);
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync XChangeProperty(g_display, DefaultRootWindow(g_display),
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync rdesktop_selection_notify_atom, XA_INTEGER, 32, PropModeReplace, NULL, 0);
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync DEBUG_CLIPBOARD(("Already probing selections. Scheduling reprobe.\n"));
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync primary_owner = XGetSelectionOwner(g_display, primary_atom);
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync clipboard_owner = XGetSelectionOwner(g_display, clipboard_atom);
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync /* If we own all relevant selections then don't do anything. */
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync if (((primary_owner == g_wnd) || !auto_mode) && (clipboard_owner == g_wnd))
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync /* Both available */
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync if ((primary_owner != None) && (clipboard_owner != None))
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync XConvertSelection(g_display, primary_atom, timestamp_atom,
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync rdesktop_primary_timestamp_target_atom, g_wnd, CurrentTime);
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync XConvertSelection(g_display, clipboard_atom, timestamp_atom,
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync rdesktop_clipboard_timestamp_target_atom, g_wnd, CurrentTime);
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync /* Just PRIMARY */
8ccde4f32d77b1ad3f02111f28a48ee85abf6779vboxsync XConvertSelection(g_display, primary_atom, targets_atom,
8ccde4f32d77b1ad3f02111f28a48ee85abf6779vboxsync rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
8ccde4f32d77b1ad3f02111f28a48ee85abf6779vboxsync /* Just CLIPBOARD */
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync XConvertSelection(g_display, clipboard_atom, targets_atom,
b4bcdbd7ac35c938e6f71a6403fe9f3ebf106a07vboxsync rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync Without XFIXES, we cannot reliably know the formats offered by an
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync upcoming selection owner, so we just lie about him offering
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync RDP_CF_TEXT. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* This function is called for SelectionNotify events.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync The SelectionNotify event is sent from the clipboard owner to the requestor
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync after his request was satisfied.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync If this function is called, we're the requestor side. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsyncxclip_handle_SelectionNotify(XSelectionEvent * event)
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync DEBUG_CLIPBOARD(("xclip_handle_SelectionNotify: selection=%s, target=%s, property=%s\n",
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync XMaxRequestSize(g_display), False, AnyPropertyType,
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync XMaxRequestSize(g_display), False, AnyPropertyType,
1207f59aa62006952dbb0bf7700decf34d8caeb2vboxsync if ((res != Success) || (nitems != 1) || (format != 32))
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync XDeleteProperty(g_display, g_wnd, rdesktop_primary_timestamp_target_atom);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_timestamp_target_atom);
e48239695d41f806ff02d8a60b97dc20d4822d7avboxsync DEBUG_CLIPBOARD(("PRIMARY is most recent selection.\n"));
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync XConvertSelection(g_display, primary_atom, targets_atom,
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync DEBUG_CLIPBOARD(("CLIPBOARD is most recent selection.\n"));
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync XConvertSelection(g_display, clipboard_atom, targets_atom,
3933885bc0c2c93436d858a14564c6179ec72872vboxsync res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync 0, XMaxRequestSize(g_display), False, AnyPropertyType,
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync if ((wa.your_event_mask | PropertyChangeMask) != wa.your_event_mask)
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync /* Negotiate target format */
3933885bc0c2c93436d858a14564c6179ec72872vboxsync /* Determine the best of text targets that we have available:
3933885bc0c2c93436d858a14564c6179ec72872vboxsync Prefer UTF8_STRING > text/unicode (unspecified encoding) > STRING
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync (ignore TEXT and COMPOUND_TEXT because we don't have code to handle them)
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync Atom best_text_target = 0; /* measures how much we're satisfied with what we found */
3933885bc0c2c93436d858a14564c6179ec72872vboxsync for (i = 0; i < nitems; i++)
3933885bc0c2c93436d858a14564c6179ec72872vboxsync DEBUG_CLIPBOARD(("Other party supports STRING, choosing that as best_target\n"));
3933885bc0c2c93436d858a14564c6179ec72872vboxsync else if (supported_targets[i] == format_unicode_atom)
3933885bc0c2c93436d858a14564c6179ec72872vboxsync DEBUG_CLIPBOARD(("Other party supports text/unicode, choosing that as best_target\n"));
3933885bc0c2c93436d858a14564c6179ec72872vboxsync else if (supported_targets[i] == format_utf8_string_atom)
70ca8d009d026a301bf7fa08cd18c6494c45fdeevboxsync DEBUG_CLIPBOARD(("Other party supports UTF8_STRING, choosing that as best_target\n"));
70ca8d009d026a301bf7fa08cd18c6494c45fdeevboxsync else if (supported_targets[i] == rdesktop_clipboard_formats_atom)
3933885bc0c2c93436d858a14564c6179ec72872vboxsync if (probing_selections && (text_target_satisfaction < 4))
3933885bc0c2c93436d858a14564c6179ec72872vboxsync DEBUG_CLIPBOARD(("Other party supports native formats, choosing that as best_target\n"));
3933885bc0c2c93436d858a14564c6179ec72872vboxsync /* Kickstarting the next step in the process of satisfying RDP's
3933885bc0c2c93436d858a14564c6179ec72872vboxsync clipboard request -- specifically, requesting the actual clipboard data.
3933885bc0c2c93436d858a14564c6179ec72872vboxsync || (best_text_target == rdesktop_clipboard_formats_atom)))
3933885bc0c2c93436d858a14564c6179ec72872vboxsync XConvertSelection(g_display, event->selection, best_text_target,
3933885bc0c2c93436d858a14564c6179ec72872vboxsync rdesktop_clipboard_target_atom, g_wnd, event->time);
3933885bc0c2c93436d858a14564c6179ec72872vboxsync DEBUG_CLIPBOARD(("Unable to find a textual target to satisfy RDP clipboard text request\n"));
70ca8d009d026a301bf7fa08cd18c6494c45fdeevboxsync Without XFIXES, we must make sure that the other
70ca8d009d026a301bf7fa08cd18c6494c45fdeevboxsync rdesktop owns all relevant selections or we might try
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync to get a native format from non-rdesktop window later
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync clipboard_owner = XGetSelectionOwner(g_display, clipboard_atom);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync primary_owner = XGetSelectionOwner(g_display, primary_atom);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync DEBUG_CLIPBOARD(("Got fellow rdesktop formats\n"));
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync else if ((!nitems) || (!xclip_send_data_with_convert(data, nitems, event->target)))
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync DEBUG_CLIPBOARD(("Unable to find suitable target. Using default text format.\n"));
1207f59aa62006952dbb0bf7700decf34d8caeb2vboxsync Without XFIXES, we cannot reliably know the formats offered by an
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync upcoming selection owner, so we just lie about him offering
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync RDP_CF_TEXT. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync cliprdr_send_simple_native_format_announce(RDP_CF_TEXT);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* This function is called for SelectionRequest events.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync The SelectionRequest event is sent from the requestor to the clipboard owner
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync to request clipboard data.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsyncxclip_handle_SelectionRequest(XSelectionRequestEvent * event)
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync DEBUG_CLIPBOARD(("xclip_handle_SelectionRequest: selection=%s, target=%s, property=%s\n",
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, num_targets);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & acquire_time, 1);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync else if (event->target == rdesktop_clipboard_formats_atom)
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync xclip_provide_selection(event, XA_STRING, 8, formats_data, formats_data_length);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync /* All the following targets require an async operation with the RDP server
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync and currently we don't do X clipboard request queueing so we can only
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync handle one such request at a time. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync DEBUG_CLIPBOARD(("Error: Another clipboard request was already sent to the RDP server and not yet responded. Refusing this request.\n"));
c9e3f6ad81ea9a279ffb537720699e552882c40avboxsync /* Before the requestor makes a request for the _RDESKTOP_NATIVE target,
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync he should declare requestor[property] = CF_SOMETHING. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync res = XGetWindowProperty(g_display, event->requestor,
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync DEBUG_CLIPBOARD(("Requested native format but didn't specifiy which.\n"));
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync else if (event->target == format_string_atom || event->target == XA_STRING)
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync /* STRING and XA_STRING are defined to be ISO8859-1 */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync DEBUG_CLIPBOARD(("Requested target unavailable due to lack of Unicode support. (It was not in TARGETS, so why did you ask for it?!)\n"));
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync /* Assuming text/unicode to be UTF-16 */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync DEBUG_CLIPBOARD(("Requested target unavailable. (It was not in TARGETS, so why did you ask for it?!)\n"));
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync return; /* wait for data */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* While this rdesktop holds ownership over the clipboard, it means the clipboard data
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync is offered by the RDP server (and when it is pasted inside RDP, there's no network
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync roundtrip).
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync This event (SelectionClear) symbolizes this rdesktop lost onwership of the clipboard
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync to some other X client. We should find out what clipboard formats this other
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync client offers and announce that to RDP. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Called when any property changes in our window or the root window. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync unsigned long nitems;
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync unsigned long offset = 0;
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync if (event->state == PropertyNewValue && g_waiting_for_INCR)
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: g_waiting_for_INCR != 0\n"));
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync /* Unlike the specification, we don't set the 'delete' arugment to True
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync since we slurp the INCR's chunks in even-smaller chunks of 4096 bytes. */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync (g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync False, AnyPropertyType, &type, &format, &nitems, &bytes_left,
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync /* INCR transfer finished */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync /* Another chunk in the INCR transfer */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync offset += (nitems / 4); /* offset at which to begin the next slurp */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync g_clip_buffer = xrealloc(g_clip_buffer, g_clip_buflen + nitems);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync memcpy(g_clip_buffer + g_clip_buflen, data, nitems);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync if ((event->atom == rdesktop_selection_notify_atom) &&
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Called when the RDP server announces new clipboard data formats.
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync In response, we:
629169500a4e1696f37dd3118a791d68278f71davboxsync - take ownership over the clipboard
629169500a4e1696f37dd3118a791d68278f71davboxsync - declare those formats in their Windows native form
629169500a4e1696f37dd3118a791d68278f71davboxsync to other rdesktop instances on this X server */
629169500a4e1696f37dd3118a791d68278f71davboxsyncui_clip_format_announce(uint8 * data, uint32 length)
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync XSetSelectionOwner(g_display, primary_atom, g_wnd, acquire_time);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync warning("Failed to aquire ownership of PRIMARY clipboard\n");
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync XSetSelectionOwner(g_display, clipboard_atom, g_wnd, acquire_time);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync/* Called when the RDP server responds with clipboard data (after we've requested it). */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync if (selection_request.target == format_string_atom || selection_request.target == XA_STRING)
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync /* We're expecting a CF_TEXT response */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync /* translate linebreaks */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync /* Only send data up to null byte, if any */
b2c92fb03e119c7de54f86a32fae9c1d59bc479evboxsync else if (selection_request.target == format_utf8_string_atom)
b2c92fb03e119c7de54f86a32fae9c1d59bc479evboxsync /* We're expecting a CF_UNICODETEXT response */
b2c92fb03e119c7de54f86a32fae9c1d59bc479evboxsync iconv_t cd = iconv_open("UTF-8", WINDOWS_CODEPAGE);
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync iconv(cd, (ICONV_CONST char **) &data_remaining, &length_remaining,
825c2485cf84eec495985ffd605a1c9cddee8c32vboxsync /* translate linebreaks (works just as well on UTF-8) */
4328e87247f4a96449677e199c7e99ef516fc1cevboxsync else if (selection_request.target == format_unicode_atom)
DEBUG_CLIPBOARD(("ui_clip_handle_data: BUG! I don't know how to convert selection target %s!\n", XGetAtomName(g_display, selection_request.target)));
if (free_data)
if (probing_selections)
if (auto_mode)
primary_timestamp = 0;
clipboard_timestamp = 0;
ui_clip_sync(void)
xclip_init(void)
if (!g_rdpclip)
if (!cliprdr_init())
num_targets = 0;
#ifdef USE_UNICODE_CLIPBOARD
xclip_deinit(void)