--- sourceforge.net/trunk/rdesktop/xclip.c 2006/01/05 11:56:57 1038 +++ sourceforge.net/trunk/rdesktop/xclip.c 2006/03/27 11:10:09 1209 @@ -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. @@ -320,8 +331,8 @@ 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 */ @@ -332,7 +343,7 @@ { DEBUG_CLIPBOARD(("Sending Unicode string of %d bytes\n", translated_data_size)); - cliprdr_send_data(translated_data, translated_data_size); + helper_cliprdr_send_response(translated_data, translated_data_size); xfree(translated_data); /* Not the same thing as XFree! */ } @@ -353,7 +364,7 @@ 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! */ } @@ -372,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. @@ -385,7 +404,7 @@ Atom type; Atom *supported_targets; int res, i, format; - uint8 *data; + uint8 *data = NULL; if (event->property == None) goto fail; @@ -395,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")); @@ -417,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) { @@ -479,7 +560,7 @@ */ if (best_text_target != 0) { - XConvertSelection(g_display, clipboard_atom, best_text_target, + XConvertSelection(g_display, event->selection, best_text_target, rdesktop_clipboard_target_atom, g_wnd, event->time); return; } @@ -502,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(); } @@ -739,6 +821,13 @@ { BOOL free_data = False; + if (length == 0) + { + xclip_refuse_selection(&selection_request); + has_selection_request = False; + return; + } + if (selection_request.target == format_string_atom || selection_request.target == XA_STRING) { /* We're expecting a CF_TEXT response */ @@ -772,8 +861,8 @@ 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; @@ -808,11 +897,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, @@ -823,17 +914,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); @@ -841,7 +950,7 @@ } /* No data available */ - cliprdr_send_data(NULL, 0); + helper_cliprdr_send_empty_response(); } void @@ -850,10 +959,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; @@ -863,24 +991,29 @@ 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); format_unicode_atom = XInternAtom(g_display, "text/unicode", False); + + /* 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. */ + rdesktop_clipboard_formats_atom = + XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False); + XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask); + num_targets = 0; targets[num_targets++] = targets_atom; targets[num_targets++] = timestamp_atom; targets[num_targets++] = rdesktop_clipboard_formats_atom; - targets[num_targets++] = format_string_atom; #ifdef USE_UNICODE_CLIPBOARD targets[num_targets++] = format_utf8_string_atom; #endif targets[num_targets++] = format_unicode_atom; + targets[num_targets++] = format_string_atom; targets[num_targets++] = 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. */ - rdesktop_clipboard_formats_atom = - XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False); - XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask); }