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