1 |
/* -*- c-basic-offset: 8 -*- |
/* -*- c-basic-offset: 8 -*- |
2 |
rdesktop: A Remote Desktop Protocol client. |
rdesktop: A Remote Desktop Protocol client. |
3 |
Protocol services - Clipboard functions |
Protocol services - Clipboard functions |
4 |
Copyright (C) Erik Forsberg <forsberg@cendio.se> 2003 |
Copyright (C) Erik Forsberg <forsberg@cendio.se> 2003-2007 |
5 |
Copyright (C) Matthew Chapman 2003 |
Copyright (C) Matthew Chapman 2003-2007 |
6 |
|
|
7 |
This program is free software; you can redistribute it and/or modify |
This program is free software; you can redistribute it and/or modify |
8 |
it under the terms of the GNU General Public License as published by |
it under the terms of the GNU General Public License as published by |
53 |
extern Display *g_display; |
extern Display *g_display; |
54 |
extern Window g_wnd; |
extern Window g_wnd; |
55 |
extern Time g_last_gesturetime; |
extern Time g_last_gesturetime; |
56 |
extern BOOL g_rdpclip; |
extern RD_BOOL g_rdpclip; |
57 |
|
|
58 |
/* Mode of operation. |
/* Mode of operation. |
59 |
- Auto: Look at both PRIMARY and CLIPBOARD and use the most recent. |
- Auto: Look at both PRIMARY and CLIPBOARD and use the most recent. |
60 |
- Non-auto: Look at just CLIPBOARD. */ |
- Non-auto: Look at just CLIPBOARD. */ |
61 |
static BOOL auto_mode = True; |
static RD_BOOL auto_mode = True; |
62 |
/* Atoms of the two X selections we're dealing with: CLIPBOARD (explicit-copy) and PRIMARY (selection-copy) */ |
/* Atoms of the two X selections we're dealing with: CLIPBOARD (explicit-copy) and PRIMARY (selection-copy) */ |
63 |
static Atom clipboard_atom, primary_atom; |
static Atom clipboard_atom, primary_atom; |
64 |
/* Atom of the TARGETS clipboard target */ |
/* Atom of the TARGETS clipboard target */ |
92 |
/* State variables that indicate if we're currently probing the targets of the |
/* State variables that indicate if we're currently probing the targets of the |
93 |
selection owner. reprobe_selections indicate that the ownership changed in |
selection owner. reprobe_selections indicate that the ownership changed in |
94 |
the middle of the current probe so it should be restarted. */ |
the middle of the current probe so it should be restarted. */ |
95 |
static BOOL probing_selections, reprobe_selections; |
static RD_BOOL probing_selections, reprobe_selections; |
96 |
/* Atoms _RDESKTOP_PRIMARY_OWNER and _RDESKTOP_CLIPBOARD_OWNER. Used as properties |
/* Atoms _RDESKTOP_PRIMARY_OWNER and _RDESKTOP_CLIPBOARD_OWNER. Used as properties |
97 |
on the root window to indicate which selections that are owned by rdesktop. */ |
on the root window to indicate which selections that are owned by rdesktop. */ |
98 |
static Atom rdesktop_primary_owner_atom, rdesktop_clipboard_owner_atom; |
static Atom rdesktop_primary_owner_atom, rdesktop_clipboard_owner_atom; |
105 |
the context to proceed. */ |
the context to proceed. */ |
106 |
static XSelectionRequestEvent selection_request; |
static XSelectionRequestEvent selection_request; |
107 |
/* Denotes we have a pending selection request. */ |
/* Denotes we have a pending selection request. */ |
108 |
static Bool has_selection_request; |
static RD_BOOL has_selection_request; |
109 |
/* Stores the clipboard format (CF_TEXT, CF_UNICODETEXT etc.) requested in the last |
/* Stores the clipboard format (CF_TEXT, CF_UNICODETEXT etc.) requested in the last |
110 |
CLIPDR_DATA_REQUEST (= the RDP server requesting clipboard data from us). |
CLIPDR_DATA_REQUEST (= the RDP server requesting clipboard data from us). |
111 |
When we receive this data from whatever X client offering it, this variable gives us |
When we receive this data from whatever X client offering it, this variable gives us |
117 |
static int num_targets; |
static int num_targets; |
118 |
/* Denotes that an rdesktop (not this rdesktop) is owning the selection, |
/* Denotes that an rdesktop (not this rdesktop) is owning the selection, |
119 |
allowing us to interchange Windows native clipboard data directly. */ |
allowing us to interchange Windows native clipboard data directly. */ |
120 |
static BOOL rdesktop_is_selection_owner = False; |
static RD_BOOL rdesktop_is_selection_owner = False; |
121 |
/* Time when we acquired the selection. */ |
/* Time when we acquired the selection. */ |
122 |
static Time acquire_time = 0; |
static Time acquire_time = 0; |
123 |
|
|
130 |
/* Denotes the size of g_clip_buffer. */ |
/* Denotes the size of g_clip_buffer. */ |
131 |
static uint32 g_clip_buflen = 0; |
static uint32 g_clip_buflen = 0; |
132 |
|
|
133 |
/* Translate LF to CR-LF. To do this, we must allocate more memory. |
/* Translates CR-LF to LF. |
134 |
The returned string is null-terminated, as required by CF_TEXT. |
Changes the string in-place. |
135 |
Does not stop on embedded nulls. |
Does not stop on embedded nulls. |
136 |
The length is updated. */ |
The length is updated. */ |
137 |
static void |
static void |
157 |
{ |
{ |
158 |
uint8 *result; |
uint8 *result; |
159 |
uint16 *inptr, *outptr; |
uint16 *inptr, *outptr; |
160 |
|
RD_BOOL swap_endianess; |
161 |
|
|
162 |
/* Worst case: Every char is LF */ |
/* Worst case: Every char is LF */ |
163 |
result = xmalloc((*size * 2) + 2); |
result = xmalloc((*size * 2) + 2); |
168 |
outptr = (uint16 *) result; |
outptr = (uint16 *) result; |
169 |
|
|
170 |
/* Check for a reversed BOM */ |
/* Check for a reversed BOM */ |
171 |
Bool swap_endianess = (*inptr == 0xfffe); |
swap_endianess = (*inptr == 0xfffe); |
172 |
|
|
173 |
|
uint16 uvalue_previous = 0; /* Kept so we'll avoid translating CR-LF to CR-CR-LF */ |
174 |
while ((uint8 *) inptr < data + *size) |
while ((uint8 *) inptr < data + *size) |
175 |
{ |
{ |
176 |
uint16 uvalue = *inptr; |
uint16 uvalue = *inptr; |
177 |
if (swap_endianess) |
if (swap_endianess) |
178 |
uvalue = ((uvalue << 8) & 0xff00) + (uvalue >> 8); |
uvalue = ((uvalue << 8) & 0xff00) + (uvalue >> 8); |
179 |
if (uvalue == 0x0a) |
if ((uvalue == 0x0a) && (uvalue_previous != 0x0d)) |
180 |
*outptr++ = swap_endianess ? 0x0d00 : 0x0d; |
*outptr++ = swap_endianess ? 0x0d00 : 0x0d; |
181 |
|
uvalue_previous = uvalue; |
182 |
*outptr++ = *inptr++; |
*outptr++ = *inptr++; |
183 |
} |
} |
184 |
*outptr++ = 0; /* null termination */ |
*outptr++ = 0; /* null termination */ |
200 |
p = data; |
p = data; |
201 |
o = result; |
o = result; |
202 |
|
|
203 |
|
uint8 previous = '\0'; /* Kept to avoid translating CR-LF to CR-CR-LF */ |
204 |
while (p < data + *length) |
while (p < data + *length) |
205 |
{ |
{ |
206 |
if (*p == '\x0a') |
if ((*p == '\x0a') && (previous != '\x0d')) |
207 |
*o++ = '\x0d'; |
*o++ = '\x0d'; |
208 |
|
previous = *p; |
209 |
*o++ = *p++; |
*o++ = *p++; |
210 |
} |
} |
211 |
*length = o - result; |
*length = o - result; |
287 |
/* Replies with clipboard data to RDP, converting it from the target format |
/* Replies with clipboard data to RDP, converting it from the target format |
288 |
to the expected RDP format as necessary. Returns true if data was sent. |
to the expected RDP format as necessary. Returns true if data was sent. |
289 |
*/ |
*/ |
290 |
static Bool |
static RD_BOOL |
291 |
xclip_send_data_with_convert(uint8 * source, size_t source_size, Atom target) |
xclip_send_data_with_convert(uint8 * source, size_t source_size, Atom target) |
292 |
{ |
{ |
293 |
DEBUG_CLIPBOARD(("xclip_send_data_with_convert: target=%s, size=%u\n", |
DEBUG_CLIPBOARD(("xclip_send_data_with_convert: target=%s, size=%u\n", |
297 |
if (target == format_string_atom || |
if (target == format_string_atom || |
298 |
target == format_unicode_atom || target == format_utf8_string_atom) |
target == format_unicode_atom || target == format_utf8_string_atom) |
299 |
{ |
{ |
300 |
|
size_t unicode_buffer_size; |
301 |
|
char *unicode_buffer; |
302 |
|
iconv_t cd; |
303 |
|
size_t unicode_buffer_size_remaining; |
304 |
|
char *unicode_buffer_remaining; |
305 |
|
char *data_remaining; |
306 |
|
size_t data_size_remaining; |
307 |
|
uint32 translated_data_size; |
308 |
|
uint8 *translated_data; |
309 |
|
|
310 |
if (rdp_clipboard_request_format != RDP_CF_TEXT) |
if (rdp_clipboard_request_format != RDP_CF_TEXT) |
311 |
return False; |
return False; |
312 |
|
|
315 |
to it, so using CF_TEXT is not safe (and is unnecessary, since all |
to it, so using CF_TEXT is not safe (and is unnecessary, since all |
316 |
WinNT versions are Unicode-minded). |
WinNT versions are Unicode-minded). |
317 |
*/ |
*/ |
|
size_t unicode_buffer_size; |
|
|
char *unicode_buffer; |
|
|
iconv_t cd; |
|
|
|
|
318 |
if (target == format_string_atom) |
if (target == format_string_atom) |
319 |
{ |
{ |
320 |
char *locale_charset = nl_langinfo(CODESET); |
char *locale_charset = nl_langinfo(CODESET); |
353 |
} |
} |
354 |
|
|
355 |
unicode_buffer = xmalloc(unicode_buffer_size); |
unicode_buffer = xmalloc(unicode_buffer_size); |
356 |
size_t unicode_buffer_size_remaining = unicode_buffer_size; |
unicode_buffer_size_remaining = unicode_buffer_size; |
357 |
char *unicode_buffer_remaining = unicode_buffer; |
unicode_buffer_remaining = unicode_buffer; |
358 |
char *data_remaining = (char *) source; |
data_remaining = (char *) source; |
359 |
size_t data_size_remaining = source_size; |
data_size_remaining = source_size; |
360 |
iconv(cd, (ICONV_CONST char **) &data_remaining, &data_size_remaining, |
iconv(cd, (ICONV_CONST char **) &data_remaining, &data_size_remaining, |
361 |
&unicode_buffer_remaining, &unicode_buffer_size_remaining); |
&unicode_buffer_remaining, &unicode_buffer_size_remaining); |
362 |
iconv_close(cd); |
iconv_close(cd); |
363 |
|
|
364 |
/* translate linebreaks */ |
/* translate linebreaks */ |
365 |
uint32 translated_data_size = unicode_buffer_size - unicode_buffer_size_remaining; |
translated_data_size = unicode_buffer_size - unicode_buffer_size_remaining; |
366 |
uint8 *translated_data = |
translated_data = utf16_lf2crlf((uint8 *) unicode_buffer, &translated_data_size); |
|
utf16_lf2crlf((uint8 *) unicode_buffer, &translated_data_size); |
|
367 |
if (translated_data != NULL) |
if (translated_data != NULL) |
368 |
{ |
{ |
369 |
DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n", |
DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n", |
522 |
{ |
{ |
523 |
res = XGetWindowProperty(g_display, g_wnd, |
res = XGetWindowProperty(g_display, g_wnd, |
524 |
rdesktop_primary_timestamp_target_atom, 0, |
rdesktop_primary_timestamp_target_atom, 0, |
525 |
XMaxRequestSize(g_display), False, XA_INTEGER, |
XMaxRequestSize(g_display), False, AnyPropertyType, |
526 |
&type, &format, &nitems, &bytes_left, &data); |
&type, &format, &nitems, &bytes_left, &data); |
527 |
} |
} |
528 |
else |
else |
529 |
{ |
{ |
530 |
res = XGetWindowProperty(g_display, g_wnd, |
res = XGetWindowProperty(g_display, g_wnd, |
531 |
rdesktop_clipboard_timestamp_target_atom, 0, |
rdesktop_clipboard_timestamp_target_atom, 0, |
532 |
XMaxRequestSize(g_display), False, XA_INTEGER, |
XMaxRequestSize(g_display), False, AnyPropertyType, |
533 |
&type, &format, &nitems, &bytes_left, &data); |
&type, &format, &nitems, &bytes_left, &data); |
534 |
} |
} |
535 |
|
|
536 |
|
|
537 |
if ((res != Success) || (nitems != 1)) |
if ((res != Success) || (nitems != 1) || (format != 32)) |
538 |
{ |
{ |
539 |
DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n")); |
DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n")); |
540 |
goto fail; |
goto fail; |
717 |
rdesktop_is_selection_owner = True; |
rdesktop_is_selection_owner = True; |
718 |
cliprdr_send_native_format_announce(data, nitems); |
cliprdr_send_native_format_announce(data, nitems); |
719 |
} |
} |
720 |
else if (!xclip_send_data_with_convert(data, nitems, event->target)) |
else if ((!nitems) || (!xclip_send_data_with_convert(data, nitems, event->target))) |
721 |
{ |
{ |
722 |
goto fail; |
goto fail; |
723 |
} |
} |
758 |
xclip_handle_SelectionRequest(XSelectionRequestEvent * event) |
xclip_handle_SelectionRequest(XSelectionRequestEvent * event) |
759 |
{ |
{ |
760 |
unsigned long nitems, bytes_left; |
unsigned long nitems, bytes_left; |
761 |
unsigned char *prop_return; |
unsigned char *prop_return = NULL; |
762 |
int format, res; |
int format, res; |
763 |
Atom type; |
Atom type; |
764 |
|
|
800 |
event->property, 0, 1, True, |
event->property, 0, 1, True, |
801 |
XA_INTEGER, &type, &format, &nitems, &bytes_left, |
XA_INTEGER, &type, &format, &nitems, &bytes_left, |
802 |
&prop_return); |
&prop_return); |
803 |
if (res != Success) |
if (res != Success || (!prop_return)) |
804 |
{ |
{ |
805 |
DEBUG_CLIPBOARD(("Requested native format but didn't specifiy which.\n")); |
DEBUG_CLIPBOARD(("Requested native format but didn't specifiy which.\n")); |
806 |
xclip_refuse_selection(event); |
xclip_refuse_selection(event); |
962 |
void |
void |
963 |
ui_clip_handle_data(uint8 * data, uint32 length) |
ui_clip_handle_data(uint8 * data, uint32 length) |
964 |
{ |
{ |
965 |
BOOL free_data = False; |
RD_BOOL free_data = False; |
966 |
|
|
967 |
if (length == 0) |
if (length == 0) |
968 |
{ |
{ |
1010 |
free_data = True; |
free_data = True; |
1011 |
data = (uint8 *) utf8_data; |
data = (uint8 *) utf8_data; |
1012 |
length = utf8_length - utf8_length_remaining; |
length = utf8_length - utf8_length_remaining; |
1013 |
|
/* translate linebreaks (works just as well on UTF-8) */ |
1014 |
|
crlf2lf(data, &length); |
1015 |
} |
} |
1016 |
} |
} |
1017 |
else if (selection_request.target == format_unicode_atom) |
else if (selection_request.target == format_unicode_atom) |
1124 |
{ |
{ |
1125 |
g_rdpclip = True; |
g_rdpclip = True; |
1126 |
|
|
1127 |
if (str_startswith(optarg, "auto") || str_startswith(optarg, "on") |
if (str_startswith(optarg, "PRIMARYCLIPBOARD")) |
|
|| str_startswith(optarg, "PRIMARYCLIPBOARD")) |
|
1128 |
auto_mode = True; |
auto_mode = True; |
1129 |
else if (str_startswith(optarg, "CLIPBOARD")) |
else if (str_startswith(optarg, "CLIPBOARD")) |
1130 |
auto_mode = False; |
auto_mode = False; |