--- rxvt-unicode/src/rxvttoolkit.C 2010/12/18 18:17:39 1.111 +++ rxvt-unicode/src/rxvttoolkit.C 2011/03/24 23:55:31 1.123 @@ -3,7 +3,8 @@ *----------------------------------------------------------------------* * * All portions of code are copyright by their respective author/s. - * Copyright (c) 2003-2007 Marc Lehmann + * Copyright (c) 2003-2011 Marc Lehmann + * Copyright (c) 2011 Emanuele Giaquinta * * 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 @@ -229,10 +230,7 @@ rxvt_drawable &rxvt_screen::scratch_drawable (int w, int h) { - // it's actually faster to re-allocate every time. don't ask me - // why, but its likely no big deal there are no roundtrips - // (I think/hope). - if (!scratch_area || w > scratch_w || h > scratch_h || 1/*D*/) + if (!scratch_area || w > scratch_w || h > scratch_h) { if (scratch_area) { @@ -317,6 +315,11 @@ char *displayResource, *xe; XrmDatabase rdb1, database = 0; +#if !XLIB_ILLEGAL_ACCESS + /* work around a bug in XrmSetDatabase where it frees the db, see ref_next */ + database = XrmGetStringDatabase (""); +#endif + // for ordering, see for example http://www.faqs.org/faqs/Xt-FAQ/ Subject: 20 // as opposed to "standard practise", we always read in ~/.Xdefaults @@ -493,6 +496,10 @@ // TODO: somehow check whether the database files/resources changed // before affording re-loading/parsing XrmDestroyDatabase (XrmGetDatabase (dpy)); +#if XLIB_ILLEGAL_ACCESS + /* work around a bug in XrmSetDatabase where it frees the db */ + dpy->db = 0; +#endif XrmSetDatabase (dpy, get_resources (true)); } @@ -509,6 +516,7 @@ #ifdef USE_XIM xims.clear (); #endif + XrmDestroyDatabase (XrmGetDatabase (dpy)); XCloseDisplay (dpy); } @@ -886,3 +894,291 @@ ); } +rxvt_selection::rxvt_selection (rxvt_display *disp, int selnum, Time tm, Window win, Atom prop, rxvt_term *term) +: display (disp), request_time (tm), request_win (win), request_prop (prop), term (term) +{ + assert (selnum >= Sel_Primary && selnum <= Sel_Clipboard); + + timer_ev.set (this); + timer_ev.repeat = 10.; + x_ev.set (this); + + incr_buf = 0; + incr_buf_size = incr_buf_fill = 0; + selection_wait = Sel_normal; + selection_type = selnum; +} + +void +rxvt_selection::stop () +{ + free (incr_buf); + incr_buf = 0; + timer_ev.stop (); + x_ev.stop (display); +} + +rxvt_selection::~rxvt_selection () +{ + stop (); +} + +void +rxvt_selection::run () +{ + int selnum = selection_type; + +#if ENABLE_FRILLS + if (selnum == Sel_Primary && display->selection_owner) + { + /* internal selection */ + char *str = rxvt_wcstombs (display->selection_owner->selection.text, display->selection_owner->selection.len); + finish (str, strlen (str)); + free (str); + return; + } +#endif + +#if X_HAVE_UTF8_STRING + selection_type = Sel_UTF8String; + if (request (display->xa[XA_UTF8_STRING], selnum)) + return; +#else + selection_type = Sel_CompoundText; + if (request (display->xa[XA_COMPOUND_TEXT], selnum)) + return; +#endif + + // fallback to CUT_BUFFER0 if the requested property has no owner + handle_selection (display->root, XA_CUT_BUFFER0, false); +} + +void +rxvt_selection::finish (char *data, unsigned int len) +{ + if (term) + { + if (data) + term->paste (data, len); + + term->selection_req = 0; + delete this; + } +#if ENABLE_PERL + else + { + stop (); // we do not really trust perl callbacks + rxvt_perl.selection_finish (this, data, len); + } +#endif +} + +bool +rxvt_selection::request (Atom target, int selnum) +{ + Atom sel; + + selection_type |= selnum; + + if (selnum == Sel_Primary) + sel = XA_PRIMARY; + else if (selnum == Sel_Secondary) + sel = XA_SECONDARY; + else + sel = display->xa[XA_CLIPBOARD]; + + if (XGetSelectionOwner (display->dpy, sel) != None) + { + XConvertSelection (display->dpy, sel, target, request_prop, + request_win, request_time); + x_ev.start (display, request_win); + timer_ev.again (); + return true; + } + + return false; +} + +void +rxvt_selection::handle_selection (Window win, Atom prop, bool delete_prop) +{ + Display *dpy = display->dpy; + char *data = 0; + unsigned int data_len = 0; + unsigned long bytes_after; + XTextProperty ct; + + // check for failed XConvertSelection + if (prop == None) + { + bool error = true; + int selnum = selection_type & Sel_whereMask; + + if (selection_type & Sel_CompoundText) + { + selection_type = 0; + error = !request (XA_STRING, selnum); + } + + if (selection_type & Sel_UTF8String) + { + selection_type = Sel_CompoundText; + error = !request (display->xa[XA_COMPOUND_TEXT], selnum); + } + + if (error) + { + ct.value = 0; + goto bailout; + } + + return; + } + + // length == (2^31 - 1) / 4, as gdk + if (XGetWindowProperty (dpy, win, prop, + 0, 0x1fffffff, + delete_prop, AnyPropertyType, + &ct.encoding, &ct.format, + &ct.nitems, &bytes_after, + &ct.value) != Success) + { + ct.value = 0; + goto bailout; + } + + if (ct.encoding == None) + goto bailout; + + if (ct.value == 0) + goto bailout; + + if (ct.encoding == display->xa[XA_INCR]) + { + // INCR selection, start handshake + if (!delete_prop) + XDeleteProperty (dpy, win, prop); + + selection_wait = Sel_incr; + timer_ev.again (); + + goto bailout; + } + + if (ct.nitems == 0) + { + if (selection_wait == Sel_incr) + { + XFree (ct.value); + + // finally complete, now paste the whole thing + selection_wait = Sel_normal; + ct.value = (unsigned char *)incr_buf; + ct.nitems = incr_buf_fill; + incr_buf = 0; + timer_ev.stop (); + } + else + { + // avoid recursion + if (win != display->root || prop != XA_CUT_BUFFER0) + { + XFree (ct.value); + + // fallback to CUT_BUFFER0 if the requested property + // has an owner but is empty + handle_selection (display->root, XA_CUT_BUFFER0, False); + return; + } + + goto bailout; + } + } + else if (selection_wait == Sel_incr) + { + timer_ev.again (); + + while (incr_buf_fill + ct.nitems > incr_buf_size) + { + incr_buf_size = incr_buf_size ? incr_buf_size * 2 : 128*1024; + incr_buf = (char *)rxvt_realloc (incr_buf, incr_buf_size); + } + + memcpy (incr_buf + incr_buf_fill, ct.value, ct.nitems); + incr_buf_fill += ct.nitems; + + goto bailout; + } + + char **cl; + int cr; + + // we honour the first item only + +#if !ENABLE_MINIMAL + // xlib is horribly broken with respect to UTF8_STRING, and nobody cares to fix it + // so recode it manually + if (ct.encoding == display->xa[XA_UTF8_STRING]) + { + wchar_t *w = rxvt_utf8towcs ((const char *)ct.value, ct.nitems); + data = rxvt_wcstombs (w); + free (w); + } + else +#endif + if (XmbTextPropertyToTextList (dpy, &ct, &cl, &cr) >= 0 + && cl) + { + data = strdup (cl[0]); + XFreeStringList (cl); + } + else + { + // paste raw + data = strdup ((const char *)ct.value); + } + + data_len = strlen (data); + +bailout: + XFree (ct.value); + + if (selection_wait == Sel_normal) + { + finish (data, data_len); + free (data); + } +} + +void +rxvt_selection::timer_cb (ev::timer &w, int revents) +{ + if (selection_wait == Sel_incr) + rxvt_warn ("data loss: timeout on INCR selection paste, ignoring.\n"); + + finish (); +} + +void +rxvt_selection::x_cb (XEvent &xev) +{ + switch (xev.type) + { + case PropertyNotify: + if (selection_wait == Sel_incr + && xev.xproperty.atom == request_prop + && xev.xproperty.state == PropertyNewValue) + handle_selection (xev.xproperty.window, xev.xproperty.atom, true); + break; + + case SelectionNotify: + if (selection_wait == Sel_normal + && xev.xselection.time == request_time + && xev.xselection.property == request_prop) + { + timer_ev.stop (); + handle_selection (xev.xselection.requestor, xev.xselection.property, true); + } + break; + } +}