--- sourceforge.net/trunk/rdesktop/xclip.c 2003/08/31 20:01:12 456 +++ sourceforge.net/trunk/rdesktop/xclip.c 2005/11/09 15:15:28 1026 @@ -36,6 +36,54 @@ static int have_primary = 0; static int rdesktop_is_selection_owner = 0; +static int g_waiting_for_INCR = 0; +static uint8 *g_clip_buffer = 0; +static uint32 g_clip_buflen = 0; + +/* Replace CR-LF to LF (well, rather removing all CR:s) This is done + in-place. The length is updated. Handles embedded nulls */ +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; +} + + static void xclip_provide_selection(XSelectionRequestEvent * req, Atom type, unsigned int format, uint8 * data, uint32 length) @@ -56,10 +104,12 @@ XSendEvent(g_display, req->requestor, False, NoEventMask, &xev); } +#ifndef MAKE_PROTO void xclip_handle_SelectionNotify(XSelectionEvent * event) { unsigned long nitems, bytes_left; + XWindowAttributes wa; Atom type, best_target, text_target; Atom *supported_targets; int res, i, format; @@ -77,7 +127,7 @@ goto fail; res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom, - 0, XMaxRequestSize(g_display), True, AnyPropertyType, + 0, XMaxRequestSize(g_display), False, AnyPropertyType, &type, &format, &nitems, &bytes_left, &data); if (res != Success) @@ -86,6 +136,25 @@ goto fail; } + + 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; + } + + XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom); + + /* Negotiate target format */ if (event->target == targets_atom) { /* FIXME: We should choose format here based on what the server wanted */ @@ -102,6 +171,7 @@ { DEBUG_CLIPBOARD(("Other party supports TEXT, choosing that as best_target\n")); best_target = text_target; + break; } } XFree(data); @@ -112,20 +182,31 @@ return; } - if (type == incr_atom) + /* 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 { - warning("We don't support INCR transfers at this time. Try cutting less data.\n"); - goto fail; + cliprdr_send_data(data, nitems + 1); } - - cliprdr_send_data(data, nitems + 1); XFree(data); if (!rdesktop_is_selection_owner) - cliprdr_send_text_format_announce(); + cliprdr_send_simple_native_format_announce(CF_TEXT); return; - fail: + fail: + XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom); + XFree(data); cliprdr_send_data(NULL, 0); } @@ -133,6 +214,7 @@ xclip_handle_SelectionRequest(XSelectionRequestEvent * event) { unsigned long nitems, bytes_left; + unsigned char *prop_return; uint32 *wanted_format; int format, res; Atom type; @@ -156,9 +238,10 @@ { res = XGetWindowProperty(g_display, event->requestor, rdesktop_clipboard_target_atom, 0, 1, True, XA_INTEGER, - &type, &format, &nitems, &bytes_left, - (unsigned char **) &wanted_format); + &type, &format, &nitems, &bytes_left, &prop_return); + wanted_format = (uint32 *) prop_return; format = (res == Success) ? *wanted_format : CF_TEXT; + /* FIXME: Need to free returned data? */ } else { @@ -176,17 +259,78 @@ DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n")); have_primary = 0; XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom); - cliprdr_send_text_format_announce(); + cliprdr_send_simple_native_format_announce(CF_TEXT); } void xclip_handle_PropertyNotify(XPropertyEvent * event) { - unsigned long nitems, bytes_left; + unsigned long nitems; + unsigned long offset = 0; + unsigned long bytes_left = 1; int format, res; + XWindowAttributes wa; uint8 *data; Atom type; + if (event->state == PropertyNewValue && g_waiting_for_INCR) + { + DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: g_waiting_for_INCR != 0\n")); + + 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)) + { + XFree(data); + return; + } + + if (nitems == 0) + { + XGetWindowAttributes(g_display, g_wnd, &wa); + XSelectInput(g_display, g_wnd, (wa.your_event_mask ^ PropertyChangeMask)); + XFree(data); + g_waiting_for_INCR = 0; + + if (g_clip_buflen > 0) + { + cliprdr_send_data(g_clip_buffer, g_clip_buflen + 1); + + 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; + } + } + else + { + uint8 *translated_data; + uint32 length = nitems; + uint8 *tmp; + + offset += (length/4); + DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n")); + translated_data = lf2crlf(data, &length); + + tmp = xmalloc(length + g_clip_buflen); + strncpy((char *) tmp, (char *) g_clip_buffer, g_clip_buflen); + xfree(g_clip_buffer); + + strncpy((char *) (tmp + g_clip_buflen), (char *) translated_data, length); + xfree(translated_data); + + g_clip_buffer = tmp; + g_clip_buflen += length; + + XFree(data); + } + } + XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom); + } + if (event->atom != rdesktop_clipboard_formats_atom) return; @@ -209,13 +353,14 @@ } /* PropertyDelete, or XGetWindowProperty failed */ - cliprdr_send_text_format_announce(); + cliprdr_send_simple_native_format_announce(CF_TEXT); rdesktop_is_selection_owner = 0; } +#endif void -ui_clip_format_announce(char *data, uint32 length) +ui_clip_format_announce(uint8 * data, uint32 length) { XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime); if (XGetSelectionOwner(g_display, primary_atom) != g_wnd) @@ -236,8 +381,23 @@ void -ui_clip_handle_data(char *data, uint32 length) +ui_clip_handle_data(uint8 * data, uint32 length) { + if (selection_request.target != rdesktop_clipboard_formats_atom) + { + uint8 *firstnull; + + /* 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; + } + } + xclip_provide_selection(&selection_request, XA_STRING, 8, data, length - 1); } @@ -282,7 +442,7 @@ void ui_clip_sync(void) { - cliprdr_send_text_format_announce(); + cliprdr_send_simple_native_format_announce(CF_TEXT); } @@ -301,7 +461,7 @@ incr_atom = XInternAtom(g_display, "INCR", False); targets[0] = targets_atom; targets[1] = XInternAtom(g_display, "TEXT", False); - targets[2] = XInternAtom(g_display, "UTF8_STRING", False); + targets[2] = XInternAtom(g_display, "STRING", False); targets[3] = XInternAtom(g_display, "text/unicode", False); targets[4] = XInternAtom(g_display, "TIMESTAMP", False); targets[5] = XA_STRING;