2003-07-01 11:31:25 +02:00
|
|
|
/* -*- c-basic-offset: 8 -*-
|
|
|
|
rdesktop: A Remote Desktop Protocol client.
|
|
|
|
Protocol services - Clipboard functions
|
|
|
|
Copyright (C) Erik Forsberg <forsberg@cendio.se> 2003
|
|
|
|
Copyright (C) Matthew Chapman 2003
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
#include <X11/Xatom.h>
|
|
|
|
#include "rdesktop.h"
|
|
|
|
|
2005-11-14 15:11:42 +01:00
|
|
|
/*
|
|
|
|
To gain better understanding of this code, one could be assisted by the following documents:
|
|
|
|
- Inter-Client Communication Conventions Manual (ICCCM)
|
|
|
|
HTML: http://tronche.com/gui/x/icccm/
|
|
|
|
PDF: http://ftp.xfree86.org/pub/XFree86/4.5.0/doc/PDF/icccm.pdf
|
|
|
|
- MSDN: Clipboard Formats
|
|
|
|
http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/dataexchange/clipboard/clipboardformats.asp
|
|
|
|
*/
|
|
|
|
|
2003-07-01 11:31:25 +02:00
|
|
|
#define NUM_TARGETS 6
|
|
|
|
|
2003-08-28 00:51:33 +02:00
|
|
|
extern Display *g_display;
|
|
|
|
extern Window g_wnd;
|
|
|
|
extern Time g_last_gesturetime;
|
2003-07-01 11:31:25 +02:00
|
|
|
|
2005-11-14 15:11:42 +01:00
|
|
|
/* Atoms of the two X selections we're dealing with: CLIPBOARD (explicit-copy) and PRIMARY (selection-copy) */
|
|
|
|
static Atom clipboard_atom, primary_atom;
|
|
|
|
/* Atom of the TARGETS clipboard target */
|
|
|
|
static Atom targets_atom;
|
|
|
|
/* Atom of the TIMESTAMP clipboard target */
|
|
|
|
static Atom timestamp_atom;
|
|
|
|
/* Atom _RDESKTOP_CLIPBOARD_TARGET which has multiple uses:
|
|
|
|
- The 'property' argument in XConvertSelection calls: This is the property of our
|
|
|
|
window into which XConvertSelection will store the received clipboard data.
|
|
|
|
- In a clipboard request of target _RDESKTOP_CLIPBOARD_FORMATS, an XA_INTEGER-typed
|
|
|
|
property carrying the Windows native (CF_...) format desired by the requestor.
|
|
|
|
Requestor set this property (e.g. requestor_wnd[_RDESKTOP_CLIPBOARD_TARGET] = CF_TEXT)
|
|
|
|
before requesting clipboard data from a fellow rdesktop using
|
|
|
|
the _RDESKTOP_CLIPBOARD_FORMATS target. */
|
|
|
|
static Atom rdesktop_clipboard_target_atom;
|
|
|
|
/* Atom _RDESKTOP_CLIPBOARD_FORMATS which has multiple uses:
|
|
|
|
- The clipboard target (X jargon for "clipboard format") for rdesktop-to-rdesktop interchange
|
|
|
|
of Windows native clipboard data.
|
|
|
|
This target cannot be used standalone; the requestor must keep the
|
|
|
|
_RDESKTOP_CLIPBOARD_TARGET property on his window denoting
|
|
|
|
the Windows native clipboard format being requested.
|
|
|
|
- The root window property set by rdesktop when it owns the clipboard,
|
|
|
|
denoting all Windows native clipboard formats it offers via
|
|
|
|
requests of the _RDESKTOP_CLIPBOARD_FORMATS target. */
|
|
|
|
static Atom rdesktop_clipboard_formats_atom;
|
|
|
|
/* Atom of the INCR clipboard type (see ICCCM on "INCR Properties") */
|
|
|
|
static Atom incr_atom;
|
|
|
|
/* Stores the last "selection request" (= another X client requesting clipboard data from us).
|
|
|
|
To satisfy such a request, we request the clipboard data from the RDP server.
|
|
|
|
When we receive the response from the RDP server (asynchronously), this variable gives us
|
|
|
|
the context to proceed. */
|
2003-07-01 11:31:25 +02:00
|
|
|
static XSelectionRequestEvent selection_request;
|
2005-11-14 15:11:42 +01:00
|
|
|
/* Array of offered clipboard targets that will be sent to fellow X clients upon a TARGETS request. */
|
2003-07-01 11:31:25 +02:00
|
|
|
static Atom targets[NUM_TARGETS];
|
2005-11-14 15:11:42 +01:00
|
|
|
/* Denotes that this client currently holds the PRIMARY selection. */
|
2003-07-01 11:31:25 +02:00
|
|
|
static int have_primary = 0;
|
2005-11-14 15:11:42 +01:00
|
|
|
/* Denotes that an rdesktop (not this rdesktop) is owning the selection,
|
|
|
|
allowing us to interchange Windows native clipboard data directly. */
|
2003-07-01 11:31:25 +02:00
|
|
|
static int rdesktop_is_selection_owner = 0;
|
|
|
|
|
2005-11-14 15:11:42 +01:00
|
|
|
/* Denotes that an INCR ("chunked") transfer is in progress. */
|
2005-06-27 14:18:48 +02:00
|
|
|
static int g_waiting_for_INCR = 0;
|
2005-11-14 15:11:42 +01:00
|
|
|
/* Buffers an INCR transfer. */
|
2005-06-27 14:18:48 +02:00
|
|
|
static uint8 *g_clip_buffer = 0;
|
2005-11-14 15:11:42 +01:00
|
|
|
/* Denotes the size of g_clip_buffer. */
|
2005-06-27 14:18:48 +02:00
|
|
|
static uint32 g_clip_buflen = 0;
|
2003-12-05 11:36:04 +01:00
|
|
|
|
2005-11-14 15:11:42 +01:00
|
|
|
/* Translate LF to CR-LF. To do this, we must allocate more memory.
|
|
|
|
The returned string is null-terminated, as required by CF_TEXT.
|
|
|
|
Does not stop on embedded nulls.
|
|
|
|
The length is updated. */
|
2003-12-05 11:36:04 +01:00
|
|
|
static void
|
|
|
|
crlf2lf(uint8 * data, uint32 * length)
|
|
|
|
{
|
|
|
|
uint8 *dst, *src;
|
|
|
|
src = dst = data;
|
|
|
|
while (src < data + *length)
|
|
|
|
{
|
|
|
|
if (*src != '\x0d')
|
|
|
|
*dst++ = *src;
|
|
|
|
src++;
|
|
|
|
}
|
|
|
|
*length = dst - data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Translate LF to CR-LF. To do this, we must allocate more memory.
|
|
|
|
The length is updated. */
|
|
|
|
static uint8 *
|
|
|
|
lf2crlf(uint8 * data, uint32 * length)
|
|
|
|
{
|
|
|
|
uint8 *result, *p, *o;
|
|
|
|
|
|
|
|
/* Worst case: Every char is LF */
|
|
|
|
result = xmalloc(*length * 2);
|
|
|
|
|
|
|
|
p = data;
|
|
|
|
o = result;
|
|
|
|
|
|
|
|
while (p < data + *length)
|
|
|
|
{
|
|
|
|
if (*p == '\x0a')
|
|
|
|
*o++ = '\x0d';
|
|
|
|
*o++ = *p++;
|
|
|
|
}
|
|
|
|
*length = o - result;
|
|
|
|
|
|
|
|
/* Convenience */
|
|
|
|
*o++ = '\0';
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-07-01 11:31:25 +02:00
|
|
|
static void
|
2003-07-09 11:18:20 +02:00
|
|
|
xclip_provide_selection(XSelectionRequestEvent * req, Atom type, unsigned int format, uint8 * data,
|
|
|
|
uint32 length)
|
2003-07-01 11:31:25 +02:00
|
|
|
{
|
|
|
|
XEvent xev;
|
|
|
|
|
2003-08-28 00:51:33 +02:00
|
|
|
XChangeProperty(g_display, req->requestor, req->property,
|
2003-07-01 11:31:25 +02:00
|
|
|
type, format, PropModeReplace, data, length);
|
|
|
|
|
|
|
|
xev.xselection.type = SelectionNotify;
|
|
|
|
xev.xselection.serial = 0;
|
|
|
|
xev.xselection.send_event = True;
|
|
|
|
xev.xselection.requestor = req->requestor;
|
|
|
|
xev.xselection.selection = req->selection;
|
|
|
|
xev.xselection.target = req->target;
|
|
|
|
xev.xselection.property = req->property;
|
|
|
|
xev.xselection.time = req->time;
|
2003-08-28 00:51:33 +02:00
|
|
|
XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);
|
2003-07-01 11:31:25 +02:00
|
|
|
}
|
|
|
|
|
2005-11-14 15:11:42 +01:00
|
|
|
/* This function is called for SelectionNotify events.
|
|
|
|
The SelectionNotify message is sent from the clipboard owner to the requestor
|
|
|
|
after his request was satisfied.
|
|
|
|
If this function is called, we're the requestor side. */
|
2005-08-02 11:27:46 +02:00
|
|
|
#ifndef MAKE_PROTO
|
2003-09-02 12:42:16 +02:00
|
|
|
void
|
2003-07-01 11:31:25 +02:00
|
|
|
xclip_handle_SelectionNotify(XSelectionEvent * event)
|
|
|
|
{
|
|
|
|
unsigned long nitems, bytes_left;
|
2005-06-27 14:18:48 +02:00
|
|
|
XWindowAttributes wa;
|
2003-07-01 11:31:25 +02:00
|
|
|
Atom type, best_target, text_target;
|
|
|
|
Atom *supported_targets;
|
|
|
|
int res, i, format;
|
|
|
|
uint8 *data;
|
|
|
|
|
|
|
|
if (event->property == None)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
DEBUG_CLIPBOARD(("xclip_handle_SelectionNotify: selection=%s, target=%s, property=%s\n",
|
2003-08-28 00:51:33 +02:00
|
|
|
XGetAtomName(g_display, event->selection),
|
|
|
|
XGetAtomName(g_display, event->target),
|
|
|
|
XGetAtomName(g_display, event->property)));
|
2003-07-01 11:31:25 +02:00
|
|
|
|
|
|
|
if (event->property == None)
|
|
|
|
goto fail;
|
|
|
|
|
2003-08-28 00:51:33 +02:00
|
|
|
res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
|
2005-11-09 16:15:28 +01:00
|
|
|
0, XMaxRequestSize(g_display), False, AnyPropertyType,
|
2003-07-01 11:31:25 +02:00
|
|
|
&type, &format, &nitems, &bytes_left, &data);
|
|
|
|
|
|
|
|
if (res != Success)
|
|
|
|
{
|
|
|
|
DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2005-11-09 16:15:28 +01:00
|
|
|
|
|
|
|
if (type == incr_atom)
|
|
|
|
{
|
|
|
|
DEBUG_CLIPBOARD(("Received INCR.\n"));
|
|
|
|
|
|
|
|
XGetWindowAttributes(g_display, g_wnd, &wa);
|
|
|
|
if ((wa.your_event_mask | PropertyChangeMask) != wa.your_event_mask)
|
|
|
|
{
|
|
|
|
XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));
|
|
|
|
}
|
|
|
|
XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
|
|
|
|
XFree(data);
|
|
|
|
g_waiting_for_INCR = 1;
|
|
|
|
return;
|
2005-11-14 15:46:16 +01:00
|
|
|
}
|
|
|
|
|
2005-11-09 16:15:28 +01:00
|
|
|
XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
|
2005-11-14 15:46:16 +01:00
|
|
|
|
2003-12-05 11:36:04 +01:00
|
|
|
/* Negotiate target format */
|
2003-07-01 11:31:25 +02:00
|
|
|
if (event->target == targets_atom)
|
|
|
|
{
|
|
|
|
/* FIXME: We should choose format here based on what the server wanted */
|
|
|
|
best_target = XA_STRING;
|
|
|
|
if (type != None)
|
|
|
|
{
|
|
|
|
supported_targets = (Atom *) data;
|
2003-08-28 00:51:33 +02:00
|
|
|
text_target = XInternAtom(g_display, "TEXT", False);
|
2003-07-01 11:31:25 +02:00
|
|
|
for (i = 0; i < nitems; i++)
|
|
|
|
{
|
2003-07-09 11:18:20 +02:00
|
|
|
DEBUG_CLIPBOARD(("Target %d: %s\n", i,
|
2003-08-28 00:51:33 +02:00
|
|
|
XGetAtomName(g_display, supported_targets[i])));
|
2003-07-01 11:31:25 +02:00
|
|
|
if (supported_targets[i] == text_target)
|
|
|
|
{
|
|
|
|
DEBUG_CLIPBOARD(("Other party supports TEXT, choosing that as best_target\n"));
|
|
|
|
best_target = text_target;
|
2003-12-05 11:36:04 +01:00
|
|
|
break;
|
2003-07-01 11:31:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
XFree(data);
|
|
|
|
}
|
|
|
|
|
2003-08-28 00:51:33 +02:00
|
|
|
XConvertSelection(g_display, primary_atom, best_target,
|
|
|
|
rdesktop_clipboard_target_atom, g_wnd, event->time);
|
2003-07-01 11:31:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2003-12-05 11:36:04 +01:00
|
|
|
/* Translate linebreaks, but only if not getting data from
|
|
|
|
other rdesktop instance */
|
|
|
|
if (event->target != rdesktop_clipboard_formats_atom)
|
|
|
|
{
|
|
|
|
uint8 *translated_data;
|
|
|
|
uint32 length = nitems;
|
|
|
|
|
|
|
|
DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
|
|
|
|
translated_data = lf2crlf(data, &length);
|
|
|
|
cliprdr_send_data(translated_data, length + 1);
|
|
|
|
xfree(translated_data); /* Not the same thing as XFree! */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cliprdr_send_data(data, nitems + 1);
|
|
|
|
}
|
2003-07-01 11:31:25 +02:00
|
|
|
XFree(data);
|
|
|
|
|
|
|
|
if (!rdesktop_is_selection_owner)
|
2005-11-07 14:15:19 +01:00
|
|
|
cliprdr_send_simple_native_format_announce(CF_TEXT);
|
2003-07-01 11:31:25 +02:00
|
|
|
return;
|
|
|
|
|
2005-11-14 15:46:16 +01:00
|
|
|
fail:
|
2005-11-09 16:15:28 +01:00
|
|
|
XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
|
|
|
|
XFree(data);
|
2003-07-01 11:31:25 +02:00
|
|
|
cliprdr_send_data(NULL, 0);
|
|
|
|
}
|
|
|
|
|
2005-11-14 15:11:42 +01:00
|
|
|
/* This function is called for SelectionRequest events.
|
|
|
|
The SelectionRequest message is sent from the requestor to the clipboard owner
|
|
|
|
to request clipboard data.
|
|
|
|
*/
|
2003-09-02 12:42:16 +02:00
|
|
|
void
|
2003-07-09 11:18:20 +02:00
|
|
|
xclip_handle_SelectionRequest(XSelectionRequestEvent * event)
|
2003-07-01 11:31:25 +02:00
|
|
|
{
|
|
|
|
unsigned long nitems, bytes_left;
|
2003-09-05 10:41:21 +02:00
|
|
|
unsigned char *prop_return;
|
2003-07-01 11:31:25 +02:00
|
|
|
uint32 *wanted_format;
|
|
|
|
int format, res;
|
|
|
|
Atom type;
|
|
|
|
|
|
|
|
DEBUG_CLIPBOARD(("xclip_handle_SelectionRequest: selection=%s, target=%s, property=%s\n",
|
2003-08-28 00:51:33 +02:00
|
|
|
XGetAtomName(g_display, event->selection),
|
|
|
|
XGetAtomName(g_display, event->target),
|
|
|
|
XGetAtomName(g_display, event->property)));
|
2003-07-01 11:31:25 +02:00
|
|
|
|
|
|
|
if (event->target == targets_atom)
|
|
|
|
{
|
2003-07-09 11:18:20 +02:00
|
|
|
xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, NUM_TARGETS);
|
2003-07-01 11:31:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (event->target == timestamp_atom)
|
|
|
|
{
|
2003-08-28 00:51:33 +02:00
|
|
|
xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);
|
2003-07-01 11:31:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (event->target == rdesktop_clipboard_formats_atom)
|
|
|
|
{
|
2003-08-28 00:51:33 +02:00
|
|
|
res = XGetWindowProperty(g_display, event->requestor,
|
2003-07-09 11:18:20 +02:00
|
|
|
rdesktop_clipboard_target_atom, 0, 1, True, XA_INTEGER,
|
2003-09-15 09:59:36 +02:00
|
|
|
&type, &format, &nitems, &bytes_left, &prop_return);
|
2003-09-05 10:41:21 +02:00
|
|
|
wanted_format = (uint32 *) prop_return;
|
2003-07-01 11:31:25 +02:00
|
|
|
format = (res == Success) ? *wanted_format : CF_TEXT;
|
2003-09-05 10:41:21 +02:00
|
|
|
/* FIXME: Need to free returned data? */
|
2003-07-01 11:31:25 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
format = CF_TEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
cliprdr_send_data_request(format);
|
|
|
|
selection_request = *event;
|
|
|
|
/* wait for data */
|
|
|
|
}
|
|
|
|
|
2005-11-14 15:11:42 +01:00
|
|
|
/* When this rdesktop holds ownership over the clipboard, it means the clipboard data
|
|
|
|
is offered by the RDP server (and when its pasted inside RDP, there's no network
|
|
|
|
roundtrip). This event symbolizes this rdesktop lost onwership of the clipboard
|
|
|
|
to some other X client. We should find out what clipboard formats this other
|
|
|
|
client offers and announce that to RDP. */
|
2003-09-02 12:42:16 +02:00
|
|
|
void
|
2003-07-01 11:31:25 +02:00
|
|
|
xclip_handle_SelectionClear(void)
|
|
|
|
{
|
|
|
|
DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
|
|
|
|
have_primary = 0;
|
2003-08-28 00:51:33 +02:00
|
|
|
XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);
|
2005-11-07 14:15:19 +01:00
|
|
|
cliprdr_send_simple_native_format_announce(CF_TEXT);
|
2003-07-01 11:31:25 +02:00
|
|
|
}
|
|
|
|
|
2005-11-14 15:11:42 +01:00
|
|
|
/* Called when any property changes in our window or the root window. */
|
2003-09-02 12:42:16 +02:00
|
|
|
void
|
2003-07-09 11:18:20 +02:00
|
|
|
xclip_handle_PropertyNotify(XPropertyEvent * event)
|
2003-07-01 11:31:25 +02:00
|
|
|
{
|
2005-11-14 15:46:16 +01:00
|
|
|
unsigned long nitems;
|
2005-11-09 16:15:28 +01:00
|
|
|
unsigned long offset = 0;
|
|
|
|
unsigned long bytes_left = 1;
|
2003-07-01 11:31:25 +02:00
|
|
|
int format, res;
|
2005-06-27 14:18:48 +02:00
|
|
|
XWindowAttributes wa;
|
2003-07-01 11:31:25 +02:00
|
|
|
uint8 *data;
|
|
|
|
Atom type;
|
|
|
|
|
2005-06-27 14:18:48 +02:00
|
|
|
if (event->state == PropertyNewValue && g_waiting_for_INCR)
|
|
|
|
{
|
|
|
|
DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: g_waiting_for_INCR != 0\n"));
|
|
|
|
|
2005-11-14 15:46:16 +01:00
|
|
|
while (bytes_left > 0)
|
|
|
|
{
|
|
|
|
if ((XGetWindowProperty
|
|
|
|
(g_display, g_wnd, rdesktop_clipboard_target_atom, offset, 4096L,
|
|
|
|
False, AnyPropertyType, &type, &format, &nitems, &bytes_left,
|
|
|
|
&data) != Success))
|
2005-11-09 16:15:28 +01:00
|
|
|
{
|
|
|
|
XFree(data);
|
|
|
|
return;
|
|
|
|
}
|
2005-06-27 14:18:48 +02:00
|
|
|
|
2005-11-09 16:15:28 +01:00
|
|
|
if (nitems == 0)
|
2005-06-27 14:18:48 +02:00
|
|
|
{
|
2005-11-09 16:15:28 +01:00
|
|
|
XGetWindowAttributes(g_display, g_wnd, &wa);
|
2005-11-14 15:46:16 +01:00
|
|
|
XSelectInput(g_display, g_wnd,
|
|
|
|
(wa.your_event_mask ^ PropertyChangeMask));
|
2005-11-09 16:15:28 +01:00
|
|
|
XFree(data);
|
|
|
|
g_waiting_for_INCR = 0;
|
2005-06-27 14:18:48 +02:00
|
|
|
|
2005-11-09 16:15:28 +01:00
|
|
|
if (g_clip_buflen > 0)
|
|
|
|
{
|
|
|
|
cliprdr_send_data(g_clip_buffer, g_clip_buflen + 1);
|
2005-06-27 14:18:48 +02:00
|
|
|
|
2005-11-09 16:15:28 +01:00
|
|
|
if (!rdesktop_is_selection_owner)
|
|
|
|
cliprdr_send_simple_native_format_announce(CF_TEXT);
|
|
|
|
|
|
|
|
xfree(g_clip_buffer);
|
|
|
|
g_clip_buffer = 0;
|
|
|
|
g_clip_buflen = 0;
|
|
|
|
}
|
2005-06-27 14:18:48 +02:00
|
|
|
}
|
2005-11-09 16:15:28 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
uint8 *translated_data;
|
|
|
|
uint32 length = nitems;
|
|
|
|
uint8 *tmp;
|
2005-06-27 14:18:48 +02:00
|
|
|
|
2005-11-14 15:46:16 +01:00
|
|
|
offset += (length / 4);
|
2005-11-09 16:15:28 +01:00
|
|
|
DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
|
|
|
|
translated_data = lf2crlf(data, &length);
|
2005-06-27 14:18:48 +02:00
|
|
|
|
2005-11-09 16:15:28 +01:00
|
|
|
tmp = xmalloc(length + g_clip_buflen);
|
|
|
|
strncpy((char *) tmp, (char *) g_clip_buffer, g_clip_buflen);
|
|
|
|
xfree(g_clip_buffer);
|
2005-06-27 14:18:48 +02:00
|
|
|
|
2005-11-14 15:46:16 +01:00
|
|
|
strncpy((char *) (tmp + g_clip_buflen), (char *) translated_data,
|
|
|
|
length);
|
2005-11-09 16:15:28 +01:00
|
|
|
xfree(translated_data);
|
2005-06-27 14:18:48 +02:00
|
|
|
|
2005-11-09 16:15:28 +01:00
|
|
|
g_clip_buffer = tmp;
|
|
|
|
g_clip_buflen += length;
|
2005-06-27 14:18:48 +02:00
|
|
|
|
2005-11-09 16:15:28 +01:00
|
|
|
XFree(data);
|
|
|
|
}
|
2005-06-27 14:18:48 +02:00
|
|
|
}
|
2005-11-09 16:15:28 +01:00
|
|
|
XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
|
2005-06-27 14:18:48 +02:00
|
|
|
}
|
|
|
|
|
2003-07-01 11:31:25 +02:00
|
|
|
if (event->atom != rdesktop_clipboard_formats_atom)
|
|
|
|
return;
|
|
|
|
|
2003-07-09 11:18:20 +02:00
|
|
|
if (have_primary) /* from us */
|
2003-07-01 11:31:25 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (event->state == PropertyNewValue)
|
|
|
|
{
|
2003-08-28 00:51:33 +02:00
|
|
|
res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),
|
2003-07-09 11:18:20 +02:00
|
|
|
rdesktop_clipboard_formats_atom, 0,
|
2003-08-31 22:01:12 +02:00
|
|
|
XMaxRequestSize(g_display), False, XA_STRING, &type,
|
|
|
|
&format, &nitems, &bytes_left, &data);
|
2003-07-01 11:31:25 +02:00
|
|
|
|
|
|
|
if ((res == Success) && (nitems > 0))
|
|
|
|
{
|
|
|
|
cliprdr_send_native_format_announce(data, nitems);
|
|
|
|
rdesktop_is_selection_owner = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* PropertyDelete, or XGetWindowProperty failed */
|
2005-11-07 14:15:19 +01:00
|
|
|
cliprdr_send_simple_native_format_announce(CF_TEXT);
|
2003-07-01 11:31:25 +02:00
|
|
|
rdesktop_is_selection_owner = 0;
|
|
|
|
}
|
2005-08-02 11:27:46 +02:00
|
|
|
#endif
|
2003-07-01 11:31:25 +02:00
|
|
|
|
|
|
|
|
2005-11-14 15:11:42 +01:00
|
|
|
/* Called when the RDP server announces new clipboard data formats.
|
|
|
|
In response, we:
|
|
|
|
- take ownership over the clipboard
|
|
|
|
- declare those formats in their Windows native form
|
|
|
|
to other rdesktop instances on this X server */
|
2003-07-01 11:31:25 +02:00
|
|
|
void
|
2003-11-03 14:22:09 +01:00
|
|
|
ui_clip_format_announce(uint8 * data, uint32 length)
|
2003-07-01 11:31:25 +02:00
|
|
|
{
|
2003-08-28 00:51:33 +02:00
|
|
|
XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime);
|
|
|
|
if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
|
2003-07-01 11:31:25 +02:00
|
|
|
{
|
|
|
|
warning("Failed to aquire ownership of PRIMARY clipboard\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
have_primary = 1;
|
2003-08-28 00:51:33 +02:00
|
|
|
XChangeProperty(g_display, DefaultRootWindow(g_display),
|
2003-07-09 11:18:20 +02:00
|
|
|
rdesktop_clipboard_formats_atom, XA_STRING, 8, PropModeReplace, data,
|
|
|
|
length);
|
2003-07-01 11:31:25 +02:00
|
|
|
|
2003-08-28 00:51:33 +02:00
|
|
|
XSetSelectionOwner(g_display, clipboard_atom, g_wnd, g_last_gesturetime);
|
|
|
|
if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)
|
2003-07-01 11:31:25 +02:00
|
|
|
warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2003-11-03 14:22:09 +01:00
|
|
|
ui_clip_handle_data(uint8 * data, uint32 length)
|
2003-07-01 11:31:25 +02:00
|
|
|
{
|
2003-12-05 11:36:04 +01:00
|
|
|
if (selection_request.target != rdesktop_clipboard_formats_atom)
|
|
|
|
{
|
|
|
|
uint8 *firstnull;
|
|
|
|
|
|
|
|
/* translate linebreaks */
|
|
|
|
crlf2lf(data, &length);
|
|
|
|
|
|
|
|
/* Only send data up to null byte, if any */
|
2004-02-15 22:19:28 +01:00
|
|
|
firstnull = (uint8 *) strchr((char *) data, '\0');
|
2003-12-05 11:36:04 +01:00
|
|
|
if (firstnull)
|
|
|
|
{
|
|
|
|
length = firstnull - data + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-07-09 11:18:20 +02:00
|
|
|
xclip_provide_selection(&selection_request, XA_STRING, 8, data, length - 1);
|
2003-07-01 11:31:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ui_clip_request_data(uint32 format)
|
|
|
|
{
|
|
|
|
Window selectionowner;
|
|
|
|
|
|
|
|
DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
|
|
|
|
|
|
|
|
if (rdesktop_is_selection_owner)
|
|
|
|
{
|
2003-08-28 00:51:33 +02:00
|
|
|
XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
|
2003-07-01 11:31:25 +02:00
|
|
|
XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
|
|
|
|
|
2003-08-28 00:51:33 +02:00
|
|
|
XConvertSelection(g_display, primary_atom, rdesktop_clipboard_formats_atom,
|
|
|
|
rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
|
2003-07-01 11:31:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2003-08-28 00:51:33 +02:00
|
|
|
selectionowner = XGetSelectionOwner(g_display, primary_atom);
|
2003-07-01 11:31:25 +02:00
|
|
|
if (selectionowner != None)
|
|
|
|
{
|
2003-08-28 00:51:33 +02:00
|
|
|
XConvertSelection(g_display, primary_atom, targets_atom,
|
|
|
|
rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
|
2003-07-01 11:31:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No PRIMARY, try CLIPBOARD */
|
2003-08-28 00:51:33 +02:00
|
|
|
selectionowner = XGetSelectionOwner(g_display, clipboard_atom);
|
2003-07-01 11:31:25 +02:00
|
|
|
if (selectionowner != None)
|
|
|
|
{
|
2003-08-28 00:51:33 +02:00
|
|
|
XConvertSelection(g_display, clipboard_atom, targets_atom,
|
|
|
|
rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
|
2003-07-01 11:31:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No data available */
|
|
|
|
cliprdr_send_data(NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ui_clip_sync(void)
|
|
|
|
{
|
2005-11-07 14:15:19 +01:00
|
|
|
cliprdr_send_simple_native_format_announce(CF_TEXT);
|
2003-07-01 11:31:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
xclip_init(void)
|
|
|
|
{
|
|
|
|
if (!cliprdr_init())
|
|
|
|
return;
|
|
|
|
|
2003-08-28 00:51:33 +02:00
|
|
|
primary_atom = XInternAtom(g_display, "PRIMARY", False);
|
|
|
|
clipboard_atom = XInternAtom(g_display, "CLIPBOARD", False);
|
|
|
|
targets_atom = XInternAtom(g_display, "TARGETS", False);
|
|
|
|
timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);
|
2003-08-31 22:01:12 +02:00
|
|
|
rdesktop_clipboard_target_atom =
|
|
|
|
XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
|
2003-08-28 00:51:33 +02:00
|
|
|
incr_atom = XInternAtom(g_display, "INCR", False);
|
2003-07-01 11:31:25 +02:00
|
|
|
targets[0] = targets_atom;
|
2003-08-28 00:51:33 +02:00
|
|
|
targets[1] = XInternAtom(g_display, "TEXT", False);
|
2004-06-04 10:42:11 +02:00
|
|
|
targets[2] = XInternAtom(g_display, "STRING", False);
|
2003-08-28 00:51:33 +02:00
|
|
|
targets[3] = XInternAtom(g_display, "text/unicode", False);
|
|
|
|
targets[4] = XInternAtom(g_display, "TIMESTAMP", False);
|
2003-07-01 11:31:25 +02:00
|
|
|
targets[5] = XA_STRING;
|
|
|
|
|
|
|
|
/* rdesktop sets _RDESKTOP_CLIPBOARD_FORMATS on the root window when acquiring the clipboard.
|
|
|
|
Other interested rdesktops can use this to notify their server of the available formats. */
|
2003-07-09 11:18:20 +02:00
|
|
|
rdesktop_clipboard_formats_atom =
|
2003-08-28 00:51:33 +02:00
|
|
|
XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
|
|
|
|
XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);
|
2003-07-01 11:31:25 +02:00
|
|
|
}
|