From 899d118f5b7f8f23c4f7eb76d9c6a84fe7d66172 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 25 Jan 2006 00:42:20 +0000 Subject: [PATCH] *** empty log message *** --- Changes | 3 +- MANIFEST | 1 + src/hookinc.h | 5 ++ src/main.C | 109 ++++++++++++++++++++++++++++++++++++++--- src/perl/xim-onthespot | 79 +++++++++++++++++++++++++++++ src/rxvt.h | 6 +++ src/rxvtfont.C | 28 +++-------- src/rxvtperl.h | 3 +- src/rxvtperl.xs | 95 ++++++++++++++++++++++++----------- src/rxvttoolkit.C | 2 + src/rxvtutil.C | 15 ++++++ src/rxvtutil.h | 10 ++++ src/screen.C | 8 +-- src/urxvt.pm | 5 +- 14 files changed, 304 insertions(+), 65 deletions(-) create mode 100644 src/perl/xim-onthespot diff --git a/Changes b/Changes index bfd5322e..90279c6d 100644 --- a/Changes +++ b/Changes @@ -15,12 +15,11 @@ WISH: OnTheSpot editing, or maybe switch to miiiiiiif. or maybe use perl and an WISH: just for fun, do shade and tint with XRender. DUMB: support tex fonts -TODO: ctrl - mouse button2 in vim makes X freeze (unreproducable with cvs) - don't let iso14755 or mouse reporting get into the way of perl (could lead to global grabs never being cleared). + - experimental OnTheSpot editing support (-pe xim-onthespot). - moved Shift-Button2 paste combination to Meta-Button2. - removed (unused) arabic presentation form composing sequences. - - correctly ask XIM for events it needs to have enabled on the window. - changed version sos (ESC [ > c) response to be more compatible with xterm. diff --git a/MANIFEST b/MANIFEST index eb7c7518..9b17e3e7 100644 --- a/MANIFEST +++ b/MANIFEST @@ -168,6 +168,7 @@ src/perl/searchable-scrollback src/perl/automove-background src/perl/mark-urls src/perl/tabbed +src/perl/xim-onthespot src/perl/readline src/perl/example-refresh-hooks src/perl/block-graphics-to-ascii diff --git a/src/hookinc.h b/src/hookinc.h index 203632f9..e716ee7b 100644 --- a/src/hookinc.h +++ b/src/hookinc.h @@ -42,5 +42,10 @@ def (WM_PROTOCOLS) def (PROPERTY_NOTIFY) + def (XIM_PREEDIT_START) + def (XIM_PREEDIT_DONE) + def (XIM_PREEDIT_DRAW) + def (XIM_PREEDIT_CARET) + def (CUSTOM_REND) // hovering over custom rendition, generate enter/leave maybe? diff --git a/src/main.C b/src/main.C index 94571ef0..d87b108d 100644 --- a/src/main.C +++ b/src/main.C @@ -307,8 +307,7 @@ rxvt_term::destroy () if (destroy_ev.active) return; - if (HOOK_INVOKE ((this, HOOK_DESTROY, DT_END))) - return; + HOOK_INVOKE ((this, HOOK_DESTROY, DT_END)); #if ENABLE_OVERLAY scr_overlay_off (); @@ -1244,6 +1243,78 @@ rxvt_term::im_destroy () Input_Context = 0; } +#ifdef ENABLE_XIM_ONTHESPOT + +static void +xim_preedit_start (XIC ic, XPointer client_data, XPointer call_data) +{ + ((rxvt_term *)client_data)->make_current (); + HOOK_INVOKE (((rxvt_term *)client_data, HOOK_XIM_PREEDIT_START, DT_END)); +} + +static void +xim_preedit_done (XIC ic, XPointer client_data, XPointer call_data) +{ + ((rxvt_term *)client_data)->make_current (); + HOOK_INVOKE (((rxvt_term *)client_data, HOOK_XIM_PREEDIT_DONE, DT_END)); +} + +static void +xim_preedit_draw (XIC ic, XPointer client_data, XIMPreeditDrawCallbackStruct *call_data) +{ + rxvt_term *term = (rxvt_term *)client_data; + XIMText *text = call_data->text; + + term->make_current (); + + if (text) + { + void *str; + + if (!text->encoding_is_wchar && text->string.multi_byte) + { + // of course, X makes it ugly again + if (term->rs[Rs_imLocale]) + SET_LOCALE (term->rs[Rs_imLocale]); + + str = rxvt_temp_buf ((text->length + 1) * sizeof (wchar_t)); + mbstowcs ((wchar_t *)str, text->string.multi_byte, text->length + 1); + + if (term->rs[Rs_imLocale]) + SET_LOCALE (term->locale); + } + else + str = (void *)text->string.wide_char; + + HOOK_INVOKE ((term, HOOK_XIM_PREEDIT_DRAW, + DT_INT, call_data->caret, + DT_INT, call_data->chg_first, + DT_INT, call_data->chg_length, + DT_LCS_LEN, (void *)text->feedback, text->feedback ? (int)text->length : 0, + DT_WCS_LEN, str, str ? (int)text->length : 0, + DT_END)); + } + else + HOOK_INVOKE ((term, HOOK_XIM_PREEDIT_DRAW, + DT_INT, call_data->caret, + DT_INT, call_data->chg_first, + DT_INT, call_data->chg_length, + DT_END)); +} + +static void +xim_preedit_caret (XIC ic, XPointer client_data, XIMPreeditCaretCallbackStruct *call_data) +{ + ((rxvt_term *)client_data)->make_current (); + HOOK_INVOKE (((rxvt_term *)client_data, HOOK_XIM_PREEDIT_CARET, + DT_INT, call_data->position, + DT_INT, call_data->direction, + DT_INT, call_data->style, + DT_END)); +} + +#endif + /* * Try to open a XIM with the current modifiers, then see if we can * open a suitable preedit type @@ -1259,6 +1330,9 @@ rxvt_term::IM_get_IC (const char *modifiers) const char *p; char **s; XIMStyles *xim_styles; +#ifdef ENABLE_XIM_ONTHESPOT + XIMCallback xcb[4]; +#endif set_environ (envv); @@ -1294,13 +1368,19 @@ rxvt_term::IM_get_IC (const char *modifiers) for (i = found = 0; !found && s[i]; i++) { if (!strcmp (s[i], "OverTheSpot")) - input_style = (XIMPreeditPosition | XIMStatusNothing); + input_style = XIMPreeditPosition | XIMStatusNothing; else if (!strcmp (s[i], "OffTheSpot")) - input_style = (XIMPreeditArea | XIMStatusArea); + input_style = XIMPreeditArea | XIMStatusArea; else if (!strcmp (s[i], "Root")) - input_style = (XIMPreeditNothing | XIMStatusNothing); + input_style = XIMPreeditNothing | XIMStatusNothing; else if (!strcmp (s[i], "None")) - input_style = (XIMPreeditNone | XIMStatusNone); + input_style = XIMPreeditNone | XIMStatusNone; +#ifdef ENABLE_XIM_ONTHESPOT + else if (SHOULD_INVOKE (HOOK_XIM_PREEDIT_START) && !strcmp (s[i], "OnTheSpot")) + input_style = XIMPreeditCallbacks | XIMStatusNothing; +#endif + else + input_style = XIMPreeditNothing | XIMStatusNothing; for (j = 0; j < xim_styles->count_styles; j++) if (input_style == xim_styles->supported_styles[j]) @@ -1399,6 +1479,20 @@ foundpet: XNFontSet, fs, NULL); } + else if (input_style & XIMPreeditCallbacks) + { + xcb[0].client_data = (XPointer)this; xcb[0].callback = (XIMProc)xim_preedit_start; + xcb[1].client_data = (XPointer)this; xcb[1].callback = (XIMProc)xim_preedit_done; + xcb[2].client_data = (XPointer)this; xcb[2].callback = (XIMProc)xim_preedit_draw; + xcb[3].client_data = (XPointer)this; xcb[3].callback = (XIMProc)xim_preedit_caret; + + preedit_attr = XVaCreateNestedList (0, + XNPreeditStartCallback, &xcb[0], + XNPreeditDoneCallback , &xcb[1], + XNPreeditDrawCallback , &xcb[2], + XNPreeditCaretCallback, &xcb[3], + NULL); + } Input_Context = XCreateIC (xim, XNInputStyle, input_style, @@ -1420,8 +1514,11 @@ foundpet: return false; } +#if 0 + // unfortunately, only the focus window is used by XIM, hard to fix if (!XGetICValues (Input_Context, XNFilterEvents, &vt_emask_xim, NULL)) vt_select_input (); +#endif if (input_style & XIMPreeditArea) IMSetStatusPosition (); diff --git a/src/perl/xim-onthespot b/src/perl/xim-onthespot new file mode 100644 index 00000000..08ffb437 --- /dev/null +++ b/src/perl/xim-onthespot @@ -0,0 +1,79 @@ +#! perl + +# +# problems with this implementation include +# +# - primary, secondary, teriary is NOT different to other hilighting +# - if rend values are missing, they are not interpolated +# + +my $SIZEOF_LONG = length pack "l!", 0; + +sub refresh { + my ($self) = @_; + + delete $self->{overlay}; + + my $text = $self->{text}; + + return unless length $text; + + my ($row, $col) = $self->screen_cur; + + my $idx = 0; + + my @rend = map { + my $rstyle = $self->{caret} == $idx ? urxvt::OVERLAY_RSTYLE : $self->rstyle; + + $rstyle |= urxvt::RS_Uline if $_ & (urxvt::XIMUnderline | urxvt::XIMPrimary); + $rstyle |= urxvt::RS_RVid if $_ & (urxvt::XIMReverse | urxvt::XIMSecondary); + $rstyle |= urxvt::RS_Blink if $_ & (urxvt::XIMHighlight | urxvt::XIMTertiary); + + ($rstyle) x ($self->strwidth (substr $text, $idx++, 1)) + } unpack "l!*", $self->{rend}; + + if ($self->{caret} >= length $text) { + $text .= " "; + push @rend, urxvt::OVERLAY_RSTYLE; + } + + $self->{overlay} = $self->overlay ($col, $row, $self->strwidth ($text), 1, $self->rstyle, 0); + $self->{overlay}->set (0, 0, $self->special_encode ($text), \@rend); +} + +sub on_xim_preedit_start { + my ($self) = @_; + + () +} + +sub on_xim_preedit_done { + my ($self) = @_; + + delete $self->{overlay}; + delete $self->{text}; + delete $self->{rend}; + + () +} + +sub on_xim_preedit_caret { + my ($self, $pos, $dir, $style) = @_; + warn "preedit_caret(@_)\n"; + () +} + +sub on_xim_preedit_draw { + my ($self, $caret, $pos, $len, $feedback, $chars) = @_; + + $self->{caret} = $caret; + + substr $self->{rend}, $pos * $SIZEOF_LONG, $len * $SIZEOF_LONG, $feedback; + substr $self->{text}, $pos , $len , $chars if defined $feedback || !defined $chars; + + $self->refresh; + + () +} + + diff --git a/src/rxvt.h b/src/rxvt.h index fec8febf..2a171f5b 100644 --- a/src/rxvt.h +++ b/src/rxvt.h @@ -17,6 +17,7 @@ #if ENABLE_FRILLS # define ENABLE_XEMBED 1 # define ENABLE_EWMH 1 +# define ENABLE_XIM_ONTHESPOT 1 # define CURSOR_BLINK 1 #else # define ENABLE_MINIMAL 1 @@ -738,6 +739,11 @@ enum { #define dLocal(type,name) type const name = this->name #define dDisp Display *disp = this->display->display +// for speed reasons, we assume that all latin1 characters +// are single-width (the first unicdoe combining character +// is actually 0x300, but ascii is what matters most). +#define WCWIDTH(c) ((c) < 0x100 ? 1 : wcwidth (c)) + /* convert pixel dimensions to row/column values. Everything as int32_t */ #define Pixel2Col(x) Pixel2Width((int32_t)(x)) #define Pixel2Row(y) Pixel2Height((int32_t)(y)) diff --git a/src/rxvtfont.C b/src/rxvtfont.C index bd52e398..65d71fe7 100644 --- a/src/rxvtfont.C +++ b/src/rxvtfont.C @@ -175,26 +175,11 @@ rxvt_drawable::operator XftDraw *() ///////////////////////////////////////////////////////////////////////////// -static void *enc_buf; -static uint32_t enc_len; - -static inline void * -get_enc_buf (uint32_t len) -{ - if (len > enc_len) - { - free (enc_buf); - enc_buf = malloc (len); - enc_len = len; - } - - return enc_buf; -} - static const char * enc_char (const text_t *text, uint32_t len, codeset cs, bool &zero) { - uint8_t *buf = (uint8_t *)get_enc_buf (len); + uint8_t *buf = rxvt_temp_buf (len); + uint8_t *res = buf; while (len--) { @@ -209,13 +194,14 @@ enc_char (const text_t *text, uint32_t len, codeset cs, bool &zero) *buf++ = c; } - return (const char *)enc_buf; + return (const char *)res; } static const XChar2b * enc_xchar2b (const text_t *text, uint32_t len, codeset cs, bool &zero) { - XChar2b *buf = (XChar2b *)get_enc_buf (len * sizeof (XChar2b)); + XChar2b *buf = rxvt_temp_buf (len); + XChar2b *res = buf; while (len--) { @@ -232,7 +218,7 @@ enc_xchar2b (const text_t *text, uint32_t len, codeset cs, bool &zero) buf++; } - return (XChar2b *)enc_buf; + return res; } ///////////////////////////////////////////////////////////////////////////// @@ -1282,7 +1268,7 @@ rxvt_font_xft::draw (rxvt_drawable &d, int x, int y, clear_rect (d, x, y, term->fwidth * len, term->fheight, bg); XGlyphInfo extents; - XftGlyphSpec *enc = (XftGlyphSpec *)get_enc_buf (len * sizeof (XftGlyphSpec)); + XftGlyphSpec *enc = (XftGlyphSpec *)rxvt_temp_buf (len * sizeof (XftGlyphSpec)); XftGlyphSpec *ep = enc; dTermDisplay; diff --git a/src/rxvtperl.h b/src/rxvtperl.h index 147b8302..2449a06b 100644 --- a/src/rxvtperl.h +++ b/src/rxvtperl.h @@ -22,7 +22,8 @@ enum data_type { DT_LONG, DT_STR, // 0-terminates string DT_STR_LEN, // string + length - DT_WCS_LEN, // wstring + length + DT_WCS_LEN, // wchar_t* + length + DT_LCS_LEN, // long* + length DT_XEVENT, }; diff --git a/src/rxvtperl.xs b/src/rxvtperl.xs index f7439ab7..9dc23a7f 100644 --- a/src/rxvtperl.xs +++ b/src/rxvtperl.xs @@ -556,8 +556,9 @@ rxvt_perl_interp::invoke (rxvt_term *term, hook_type htype, ...) case DT_STR_LEN: { char *str = va_arg (ap, char *); - int len = va_arg (ap, int); + int len = va_arg (ap, int); + printf ("pushing str %p:%d\n", str,len);//D XPUSHs (sv_2mortal (newSVpvn (str, len))); } break; @@ -565,21 +566,30 @@ rxvt_perl_interp::invoke (rxvt_term *term, hook_type htype, ...) case DT_WCS_LEN: { wchar_t *wstr = va_arg (ap, wchar_t *); - int wlen = va_arg (ap, int); + int wlen = va_arg (ap, int); XPUSHs (sv_2mortal (wcs2sv (wstr, wlen))); } break; + case DT_LCS_LEN: + { + long *lstr = va_arg (ap, long *); + int llen = va_arg (ap, int); + + XPUSHs (sv_2mortal (newSVpvn ((char *)lstr, llen * sizeof (long)))); + } + break; + case DT_XEVENT: { XEvent *xe = va_arg (ap, XEvent *); HV *hv = newHV (); -# define set(name, sv) hv_store (hv, # name, sizeof (# name) - 1, sv, 0) -# define setiv(name, val) hv_store (hv, # name, sizeof (# name) - 1, newSViv (val), 0) -# define setuv(name, val) hv_store (hv, # name, sizeof (# name) - 1, newSVuv (val), 0) -# undef set +# define set(name, sv) hv_store (hv, # name, sizeof (# name) - 1, sv, 0) +# define setiv(name, val) hv_store (hv, # name, sizeof (# name) - 1, newSViv (val), 0) +# define setuv(name, val) hv_store (hv, # name, sizeof (# name) - 1, newSVuv (val), 0) +# undef set setiv (type, xe->type); setiv (send_event, xe->xany.send_event); @@ -718,6 +728,9 @@ rxvt_perl_interp::invoke (rxvt_term *term, hook_type htype, ...) { clearSVptr ((SV *)term->perl.self); SvREFCNT_dec ((SV *)term->perl.self); + + // don't allow further calls + term->perl.self = 0; } swap (perl_environ, environ); @@ -844,6 +857,30 @@ BOOT: const_iv (ColormapNotify), const_iv (ClientMessage), const_iv (MappingNotify), +# if ENABLE_XIM_ONTHESPOT + const_iv (XIMReverse), + const_iv (XIMUnderline), + const_iv (XIMHighlight), + const_iv (XIMPrimary), + const_iv (XIMSecondary), + const_iv (XIMTertiary), + const_iv (XIMVisibleToForward), + const_iv (XIMVisibleToBackword), + const_iv (XIMVisibleToCenter), + + const_iv (XIMForwardChar), + const_iv (XIMBackwardChar), + const_iv (XIMForwardWord), + const_iv (XIMBackwardWord), + const_iv (XIMCaretUp), + const_iv (XIMCaretDown), + const_iv (XIMNextLine), + const_iv (XIMPreviousLine), + const_iv (XIMLineStart), + const_iv (XIMLineEnd), + const_iv (XIMAbsolutePosition), + const_iv (XIMDontChange), +# endif }; for (civ = const_iv + sizeof (const_iv) / sizeof (const_iv [0]); @@ -1246,14 +1283,12 @@ rxvt_term::ROW_t (int row_number, SV *new_text = 0, int start_col = 0, int start if (GIMME_V != G_VOID) { - wchar_t *wstr = new wchar_t [THIS->ncol]; + wchar_t *wstr = rxvt_temp_buf (THIS->ncol); for (int col = 0; col < THIS->ncol; col++) wstr [col] = l.t [col]; XPUSHs (sv_2mortal (wcs2sv (wstr, THIS->ncol))); - - delete [] wstr; } if (new_text) @@ -1356,35 +1391,41 @@ rxvt_term::special_encode (SV *string) { wchar_t *wstr = sv2wcs (string); int wlen = wcslen (wstr); - wchar_t *rstr = new wchar_t [wlen]; // cannot become longer + wchar_t *rstr = rxvt_temp_buf (wlen * 2); // cannot become longer rxvt_push_locale (THIS->locale); wchar_t *r = rstr; for (wchar_t *s = wstr; *s; s++) - if (wcwidth (*s) == 0) - { - if (r == rstr) - croak ("leading combining character unencodable"); + { + int w = WCWIDTH (*s); + + if (w == 0) + { + if (r == rstr) + croak ("leading combining character unencodable"); - unicode_t n = rxvt_compose (r[-1], *s); - if (n == NOCHAR) - n = rxvt_composite.compose (r[-1], *s); + unicode_t n = rxvt_compose (r[-1], *s); + if (n == NOCHAR) + n = rxvt_composite.compose (r[-1], *s); - r[-1] = n; - } + r[-1] = n; + } #if !UNICODE_3 - else if (*s >= 0x10000) - *r++ = rxvt_composite.compose (*s); + else if (*s >= 0x10000) + *r++ = rxvt_composite.compose (*s); #endif - else - *r++ = *s; + else + *r++ = *s; + + // the *2 above only allows wcwidth <= 2 + if (w > 1) + *r++ = NOCHAR; + } rxvt_pop_locale (); RETVAL = wcs2sv (rstr, r - rstr); - - delete [] rstr; } OUTPUT: RETVAL @@ -1406,7 +1447,7 @@ rxvt_term::special_decode (SV *text) else dlen++; - wchar_t *rstr = new wchar_t [dlen]; + wchar_t *rstr = rxvt_temp_buf (dlen); // decode wchar_t *r = rstr; @@ -1419,8 +1460,6 @@ rxvt_term::special_decode (SV *text) *r++ = *s; RETVAL = wcs2sv (rstr, r - rstr); - - delete [] rstr; } OUTPUT: RETVAL diff --git a/src/rxvttoolkit.C b/src/rxvttoolkit.C index 3e79ad42..078c422d 100644 --- a/src/rxvttoolkit.C +++ b/src/rxvttoolkit.C @@ -95,6 +95,7 @@ void refcache::clear () ///////////////////////////////////////////////////////////////////////////// #ifdef USE_XIM + static void #if XIMCB_PROTO_BROKEN im_destroy_cb (XIC unused1, XPointer client_data, XPointer unused3) @@ -135,6 +136,7 @@ rxvt_xim::~rxvt_xim () if (xim) XCloseIM (xim); } + #endif ///////////////////////////////////////////////////////////////////////////// diff --git a/src/rxvtutil.C b/src/rxvtutil.C index 8bb4a3fa..bf1baa41 100644 --- a/src/rxvtutil.C +++ b/src/rxvtutil.C @@ -38,5 +38,20 @@ zero_initialized::operator delete (void *p, size_t s) free (p); } +static void *temp_buf; +static uint32_t temp_len; + +void * +rxvt_temp_buf (int len) +{ + if (len > temp_len) + { + free (temp_buf); + temp_buf = malloc (len); + temp_len = len; + } + + return temp_buf; +} diff --git a/src/rxvtutil.h b/src/rxvtutil.h index 2e62cc36..2a3ef5bc 100644 --- a/src/rxvtutil.h +++ b/src/rxvtutil.h @@ -398,5 +398,15 @@ struct stringvec : simplevec } }; +// return a very temporary (and never deallocated) buffer. keep small. +void *rxvt_temp_buf (int len); + +template +inline T * +rxvt_temp_buf (int len) +{ + return (T *)rxvt_temp_buf (len * sizeof (T)); +} + #endif diff --git a/src/screen.C b/src/screen.C index 0e11e389..19854466 100644 --- a/src/screen.C +++ b/src/screen.C @@ -844,11 +844,11 @@ rxvt_term::scr_add_lines (const wchar_t *str, int len, int minlines) NOTHROW if (IN_RANGE_INC (c, 0xd800, 0xdfff)) c = 0xfffd; - // rely on wcwidth to tell us the character width, at least for non-latin1 - // do wcwidth before further replacements, as wcwidth might return -1 - // for the line drawing characters below as they might be invalid in the current + // rely on wcwidth to tell us the character width, do wcwidth before + // further replacements, as wcwidth might return -1 for the line + // drawing characters below as they might be invalid in the current // locale. - int width = c < 0x100 ? 1 : wcwidth (c); + int width = WCWIDTH (c); if (charsets [screen.charset] == '0') // DEC SPECIAL { diff --git a/src/urxvt.pm b/src/urxvt.pm index 9793c908..f2f314bd 100644 --- a/src/urxvt.pm +++ b/src/urxvt.pm @@ -372,9 +372,8 @@ trying to map (display) the toplevel and returning to the mainloop. =item on_destroy $term -Called whenever something tries to destroy terminal, before doing anything -yet. If this hook returns true, then destruction is skipped, but this is -rarely a good idea. +Called whenever something tries to destroy terminal, when the terminal is +still fully functional (not for long, though). =item on_reset $term -- 2.34.1