--- sourceforge.net/trunk/rdesktop/xclip.c 2006/01/02 15:55:59 1037 +++ sourceforge.net/trunk/rdesktop/xclip.c 2006/03/27 09:20:24 1207 @@ -53,7 +53,12 @@ extern Display *g_display; extern Window g_wnd; extern Time g_last_gesturetime; +extern BOOL g_rdpclip; +/* Mode of operation. + - Auto: Look at both PRIMARY and CLIPBOARD and use the most recent. + - Non-auto: Look at just CLIPBOARD. */ +static BOOL auto_mode = True; /* 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 */ @@ -69,6 +74,12 @@ before requesting clipboard data from a fellow rdesktop using the _RDESKTOP_CLIPBOARD_FORMATS target. */ static Atom rdesktop_clipboard_target_atom; +/* Atoms _RDESKTOP_PRIMARY_TIMESTAMP_TARGET and _RDESKTOP_CLIPBOARD_TIMESTAMP_TARGET + are used to store the timestamps for when a window got ownership of the selections. + We use these to determine which is more recent and should be used. */ +static Atom rdesktop_primary_timestamp_target_atom, rdesktop_clipboard_timestamp_target_atom; +/* Storage for timestamps since we get them in two separate notifications. */ +static Time primary_timestamp, clipboard_timestamp; /* 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. @@ -146,13 +157,13 @@ if (result == NULL) return NULL; - inptr = (uint16*)data; - outptr = (uint16*)result; + inptr = (uint16 *) data; + outptr = (uint16 *) result; /* Check for a reversed BOM */ Bool swap_endianess = (*inptr == 0xfffe); - while ((uint8*)inptr < data + *size) + while ((uint8 *) inptr < data + *size) { uint16 uvalue = *inptr; if (swap_endianess) @@ -161,8 +172,8 @@ *outptr++ = swap_endianess ? 0x0d00 : 0x0d; *outptr++ = *inptr++; } - *outptr++ = 0; /* null termination */ - *size = (uint8*)outptr - result; + *outptr++ = 0; /* null termination */ + *size = (uint8 *) outptr - result; return result; } @@ -260,12 +271,11 @@ to the expected RDP format as necessary. Returns true if data was sent. */ static Bool -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) { - #ifdef USE_UNICODE_CLIPBOARD +#ifdef USE_UNICODE_CLIPBOARD if (target == format_string_atom || - target == format_unicode_atom || - target == format_utf8_string_atom) + target == format_unicode_atom || target == format_utf8_string_atom) { if (rdp_clipboard_request_format != RDP_CF_TEXT) return False; @@ -276,14 +286,14 @@ WinNT versions are Unicode-minded). */ size_t unicode_buffer_size; - char* unicode_buffer; + char *unicode_buffer; iconv_t cd; if (target == format_string_atom) { - char* locale_charset = nl_langinfo(CODESET); + char *locale_charset = nl_langinfo(CODESET); cd = iconv_open(WINDOWS_CODEPAGE, locale_charset); - if (cd == (iconv_t)-1) + if (cd == (iconv_t) - 1) { DEBUG_CLIPBOARD(("Locale charset %s not found in iconv. Unable to convert clipboard text.\n", locale_charset)); return False; @@ -293,7 +303,7 @@ else if (target == format_unicode_atom) { cd = iconv_open(WINDOWS_CODEPAGE, "UCS-2"); - if (cd == (iconv_t)-1) + if (cd == (iconv_t) - 1) { return False; } @@ -302,7 +312,7 @@ else if (target == format_utf8_string_atom) { cd = iconv_open(WINDOWS_CODEPAGE, "UTF-8"); - if (cd == (iconv_t)-1) + if (cd == (iconv_t) - 1) { return False; } @@ -318,19 +328,22 @@ unicode_buffer = xmalloc(unicode_buffer_size); size_t unicode_buffer_size_remaining = unicode_buffer_size; - char* unicode_buffer_remaining = unicode_buffer; - char* data_remaining = (char*)source; + char *unicode_buffer_remaining = unicode_buffer; + char *data_remaining = (char *) source; size_t data_size_remaining = source_size; - iconv(cd, &data_remaining, &data_size_remaining, &unicode_buffer_remaining, &unicode_buffer_size_remaining); + iconv(cd, (ICONV_CONST char **) &data_remaining, &data_size_remaining, + &unicode_buffer_remaining, &unicode_buffer_size_remaining); iconv_close(cd); /* translate linebreaks */ uint32 translated_data_size = unicode_buffer_size - unicode_buffer_size_remaining; - uint8* translated_data = utf16_lf2crlf((uint8*)unicode_buffer, &translated_data_size); + uint8 *translated_data = + utf16_lf2crlf((uint8 *) unicode_buffer, &translated_data_size); if (translated_data != NULL) { - DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n", translated_data_size)); - cliprdr_send_data(translated_data, translated_data_size); + DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n", + translated_data_size)); + helper_cliprdr_send_response(translated_data, translated_data_size); xfree(translated_data); /* Not the same thing as XFree! */ } @@ -338,7 +351,7 @@ return True; } - #else +#else if (target == format_string_atom) { uint8 *translated_data; @@ -351,13 +364,13 @@ translated_data = lf2crlf(source, &length); if (translated_data != NULL) { - cliprdr_send_data(translated_data, length); + helper_cliprdr_send_response(translated_data, length); xfree(translated_data); /* Not the same thing as XFree! */ } return True; } - #endif +#endif else if (target == rdesktop_clipboard_formats_atom) { helper_cliprdr_send_response(source, source_size + 1); @@ -370,6 +383,14 @@ } } +static void +xclip_clear_target_props() +{ + XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom); + XDeleteProperty(g_display, g_wnd, rdesktop_primary_timestamp_target_atom); + XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_timestamp_target_atom); +} + /* This function is called for SelectionNotify events. The SelectionNotify event is sent from the clipboard owner to the requestor after his request was satisfied. @@ -383,7 +404,7 @@ Atom type; Atom *supported_targets; int res, i, format; - uint8 *data; + uint8 *data = NULL; if (event->property == None) goto fail; @@ -393,13 +414,78 @@ XGetAtomName(g_display, event->target), XGetAtomName(g_display, event->property))); - if (event->property == None) - goto fail; + if (event->target == timestamp_atom) + { + if (event->selection == primary_atom) + { + res = XGetWindowProperty(g_display, g_wnd, + rdesktop_primary_timestamp_target_atom, 0, + XMaxRequestSize(g_display), False, XA_INTEGER, + &type, &format, &nitems, &bytes_left, &data); + } + else + { + res = XGetWindowProperty(g_display, g_wnd, + rdesktop_clipboard_timestamp_target_atom, 0, + XMaxRequestSize(g_display), False, XA_INTEGER, + &type, &format, &nitems, &bytes_left, &data); + } + + + if ((res != Success) || (nitems != 1)) + { + DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n")); + goto fail; + } + + if (event->selection == primary_atom) + { + primary_timestamp = *(Time *) data; + if (primary_timestamp == 0) + primary_timestamp++; + XDeleteProperty(g_display, g_wnd, rdesktop_primary_timestamp_target_atom); + DEBUG_CLIPBOARD(("Got PRIMARY timestamp: %u\n", + (unsigned) primary_timestamp)); + } + else + { + clipboard_timestamp = *(Time *) data; + if (clipboard_timestamp == 0) + clipboard_timestamp++; + XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_timestamp_target_atom); + DEBUG_CLIPBOARD(("Got CLIPBOARD timestamp: %u\n", + (unsigned) clipboard_timestamp)); + } + + XFree(data); + + if (primary_timestamp && clipboard_timestamp) + { + if (primary_timestamp > clipboard_timestamp) + { + DEBUG_CLIPBOARD(("PRIMARY is most recent selection.\n")); + XConvertSelection(g_display, primary_atom, targets_atom, + rdesktop_clipboard_target_atom, g_wnd, + event->time); + } + else + { + DEBUG_CLIPBOARD(("CLIPBOARD is most recent selection.\n")); + XConvertSelection(g_display, clipboard_atom, targets_atom, + rdesktop_clipboard_target_atom, g_wnd, + event->time); + } + } + + return; + } res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom, 0, XMaxRequestSize(g_display), False, AnyPropertyType, &type, &format, &nitems, &bytes_left, &data); + xclip_clear_target_props(); + if (res != Success) { DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n")); @@ -415,15 +501,12 @@ { XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask)); } - XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom); XFree(data); g_incr_target = event->target; g_waiting_for_INCR = 1; return; } - XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom); - /* Negotiate target format */ if (event->target == targets_atom) { @@ -432,7 +515,7 @@ (ignore TEXT and COMPOUND_TEXT because we don't have code to handle them) */ int text_target_satisfaction = 0; - Atom best_text_target = 0; /* measures how much we're satisfied with what we found */ + Atom best_text_target = 0; /* measures how much we're satisfied with what we found */ if (type != None) { supported_targets = (Atom *) data; @@ -449,7 +532,7 @@ text_target_satisfaction = 1; } } - #ifdef USE_UNICODE_CLIPBOARD +#ifdef USE_UNICODE_CLIPBOARD else if (supported_targets[i] == format_unicode_atom) { if (text_target_satisfaction < 2) @@ -468,7 +551,7 @@ text_target_satisfaction = 3; } } - #endif +#endif } } @@ -477,7 +560,8 @@ */ if (best_text_target != 0) { - XConvertSelection(g_display, clipboard_atom, best_text_target, rdesktop_clipboard_target_atom, g_wnd, event->time); + XConvertSelection(g_display, event->selection, best_text_target, + rdesktop_clipboard_target_atom, g_wnd, event->time); return; } else @@ -499,8 +583,9 @@ return; fail: - XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom); - XFree(data); + xclip_clear_target_props(); + if (data) + XFree(data); helper_cliprdr_send_empty_response(); } @@ -550,27 +635,27 @@ Otherwise, we default to RDP_CF_TEXT. */ res = XGetWindowProperty(g_display, event->requestor, - rdesktop_clipboard_target_atom, 0, 1, True, XA_INTEGER, - &type, &format, &nitems, &bytes_left, &prop_return); + rdesktop_clipboard_target_atom, 0, 1, True, + XA_INTEGER, &type, &format, &nitems, &bytes_left, + &prop_return); wanted_format = (uint32 *) prop_return; format = (res == Success) ? *wanted_format : RDP_CF_TEXT; XFree(prop_return); } - else if (event->target == format_string_atom || - event->target == XA_STRING) + else if (event->target == format_string_atom || event->target == XA_STRING) { /* STRING and XA_STRING are defined to be ISO8859-1 */ format = CF_TEXT; } else if (event->target == format_utf8_string_atom) { - #ifdef USE_UNICODE_CLIPBOARD +#ifdef USE_UNICODE_CLIPBOARD format = CF_UNICODETEXT; - #else +#else DEBUG_CLIPBOARD(("Requested target unavailable due to lack of Unicode support. (It was not in TARGETS, so why did you ask for it?!)\n")); xclip_refuse_selection(event); return; - #endif +#endif } else if (event->target == format_unicode_atom) { @@ -587,7 +672,7 @@ cliprdr_send_data_request(format); selection_request = *event; has_selection_request = True; - return; /* wait for data */ + return; /* wait for data */ } } @@ -651,7 +736,8 @@ if (g_clip_buflen > 0) { - if (!xclip_send_data_with_convert(g_clip_buffer, g_clip_buflen, g_incr_target)) + if (!xclip_send_data_with_convert + (g_clip_buffer, g_clip_buflen, g_incr_target)) { helper_cliprdr_send_empty_response(); } @@ -663,7 +749,7 @@ else { /* Another chunk in the INCR transfer */ - offset += (nitems / 4); /* offset at which to begin the next slurp */ + offset += (nitems / 4); /* offset at which to begin the next slurp */ g_clip_buffer = xrealloc(g_clip_buffer, g_clip_buflen + nitems); memcpy(g_clip_buffer + g_clip_buflen, data, nitems); g_clip_buflen += nitems; @@ -677,7 +763,7 @@ if ((event->atom == rdesktop_clipboard_formats_atom) && (event->window == DefaultRootWindow(g_display)) && - !have_primary /* not interested in our own events */) + !have_primary /* not interested in our own events */ ) { if (event->state == PropertyNewValue) { @@ -685,8 +771,8 @@ res = XGetWindowProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom, 0, - XMaxRequestSize(g_display), False, XA_STRING, &type, - &format, &nitems, &bytes_left, &data); + XMaxRequestSize(g_display), False, XA_STRING, + &type, &format, &nitems, &bytes_left, &data); if ((res == Success) && (nitems > 0)) { @@ -735,44 +821,44 @@ { BOOL free_data = False; - if (selection_request.target == format_string_atom || - selection_request.target == XA_STRING) - { + if (selection_request.target == format_string_atom || selection_request.target == XA_STRING) + { /* We're expecting a CF_TEXT response */ - uint8 *firstnull; + uint8 *firstnull; - /* translate linebreaks */ - crlf2lf(data, &length); + /* translate linebreaks */ + crlf2lf(data, &length); - /* Only send data up to null byte, if any */ - firstnull = (uint8 *) strchr((char *) data, '\0'); - if (firstnull) - { - length = firstnull - data + 1; - } - } + /* Only send data up to null byte, if any */ + firstnull = (uint8 *) strchr((char *) data, '\0'); + if (firstnull) + { + length = firstnull - data + 1; + } + } #ifdef USE_UNICODE_CLIPBOARD else if (selection_request.target == format_utf8_string_atom) { /* We're expecting a CF_UNICODETEXT response */ iconv_t cd = iconv_open("UTF-8", WINDOWS_CODEPAGE); - if (cd != (iconv_t)-1) + if (cd != (iconv_t) - 1) { size_t utf8_length = length * 2; - char* utf8_data = malloc(utf8_length); + char *utf8_data = malloc(utf8_length); size_t utf8_length_remaining = utf8_length; - char* utf8_data_remaining = utf8_data; - char* data_remaining = (char*)data; - size_t length_remaining = (size_t)length; + char *utf8_data_remaining = utf8_data; + char *data_remaining = (char *) data; + size_t length_remaining = (size_t) length; if (utf8_data == NULL) { iconv_close(cd); return; } - iconv(cd, &data_remaining, &length_remaining, &utf8_data_remaining, &utf8_length_remaining); + iconv(cd, (ICONV_CONST char **) &data_remaining, &length_remaining, + &utf8_data_remaining, &utf8_length_remaining); iconv_close(cd); free_data = True; - data = (uint8*)utf8_data; + data = (uint8 *) utf8_data; length = utf8_length - utf8_length_remaining; } } @@ -804,11 +890,13 @@ void ui_clip_request_data(uint32 format) { - Window selectionowner; + Window primary_owner, clipboard_owner; DEBUG_CLIPBOARD(("Request from server for format %d\n", format)); rdp_clipboard_request_format = format; + xclip_clear_target_props(); + if (rdesktop_is_selection_owner) { XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom, @@ -819,17 +907,35 @@ return; } - selectionowner = XGetSelectionOwner(g_display, primary_atom); - if (selectionowner != None) + if (auto_mode) + primary_owner = XGetSelectionOwner(g_display, primary_atom); + else + primary_owner = None; + + clipboard_owner = XGetSelectionOwner(g_display, clipboard_atom); + + /* Both available */ + if ((primary_owner != None) && (clipboard_owner != None)) + { + primary_timestamp = 0; + clipboard_timestamp = 0; + XConvertSelection(g_display, primary_atom, timestamp_atom, + rdesktop_primary_timestamp_target_atom, g_wnd, CurrentTime); + XConvertSelection(g_display, clipboard_atom, timestamp_atom, + rdesktop_clipboard_timestamp_target_atom, g_wnd, CurrentTime); + return; + } + + /* Just PRIMARY */ + if (primary_owner != None) { XConvertSelection(g_display, primary_atom, targets_atom, rdesktop_clipboard_target_atom, g_wnd, CurrentTime); return; } - /* No PRIMARY, try CLIPBOARD */ - selectionowner = XGetSelectionOwner(g_display, clipboard_atom); - if (selectionowner != None) + /* Just CLIPBOARD */ + if (clipboard_owner != None) { XConvertSelection(g_display, clipboard_atom, targets_atom, rdesktop_clipboard_target_atom, g_wnd, CurrentTime); @@ -837,7 +943,7 @@ } /* No data available */ - cliprdr_send_data(NULL, 0); + helper_cliprdr_send_empty_response(); } void @@ -846,10 +952,29 @@ cliprdr_send_simple_native_format_announce(RDP_CF_TEXT); } +void +ui_clip_set_mode(const char *optarg) +{ + g_rdpclip = True; + + if (str_startswith(optarg, "auto") || str_startswith(optarg, "on") + || str_startswith(optarg, "PRIMARYCLIPBOARD")) + auto_mode = True; + else if (str_startswith(optarg, "CLIPBOARD")) + auto_mode = False; + else + { + warning("Invalid clipboard mode '%s'.\n", optarg); + g_rdpclip = False; + } +} void xclip_init(void) { + if (!g_rdpclip) + return; + if (!cliprdr_init()) return; @@ -859,6 +984,10 @@ timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False); rdesktop_clipboard_target_atom = XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False); + rdesktop_primary_timestamp_target_atom = + XInternAtom(g_display, "_RDESKTOP_PRIMARY_TIMESTAMP_TARGET", False); + rdesktop_clipboard_timestamp_target_atom = + XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TIMESTAMP_TARGET", False); incr_atom = XInternAtom(g_display, "INCR", False); format_string_atom = XInternAtom(g_display, "STRING", False); format_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False); @@ -868,9 +997,9 @@ targets[num_targets++] = timestamp_atom; targets[num_targets++] = rdesktop_clipboard_formats_atom; targets[num_targets++] = format_string_atom; - #ifdef USE_UNICODE_CLIPBOARD +#ifdef USE_UNICODE_CLIPBOARD targets[num_targets++] = format_utf8_string_atom; - #endif +#endif targets[num_targets++] = format_unicode_atom; targets[num_targets++] = XA_STRING;