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.
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
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?
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 ();
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
const char *p;
char **s;
XIMStyles *xim_styles;
+#ifdef ENABLE_XIM_ONTHESPOT
+ XIMCallback xcb[4];
+#endif
set_environ (envv);
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])
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,
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 ();
--- /dev/null
+#! 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;
+
+ ()
+}
+
+
#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
#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))
/////////////////////////////////////////////////////////////////////////////
-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<uint8_t> (len);
+ uint8_t *res = buf;
while (len--)
{
*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<XChar2b> (len);
+ XChar2b *res = buf;
while (len--)
{
buf++;
}
- return (XChar2b *)enc_buf;
+ return res;
}
/////////////////////////////////////////////////////////////////////////////
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;
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,
};
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;
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);
{
clearSVptr ((SV *)term->perl.self);
SvREFCNT_dec ((SV *)term->perl.self);
+
+ // don't allow further calls
+ term->perl.self = 0;
}
swap (perl_environ, environ);
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]);
if (GIMME_V != G_VOID)
{
- wchar_t *wstr = new wchar_t [THIS->ncol];
+ wchar_t *wstr = rxvt_temp_buf<wchar_t> (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)
{
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<wchar_t> (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
else
dlen++;
- wchar_t *rstr = new wchar_t [dlen];
+ wchar_t *rstr = rxvt_temp_buf<wchar_t> (dlen);
// decode
wchar_t *r = rstr;
*r++ = *s;
RETVAL = wcs2sv (rstr, r - rstr);
-
- delete [] rstr;
}
OUTPUT:
RETVAL
/////////////////////////////////////////////////////////////////////////////
#ifdef USE_XIM
+
static void
#if XIMCB_PROTO_BROKEN
im_destroy_cb (XIC unused1, XPointer client_data, XPointer unused3)
if (xim)
XCloseIM (xim);
}
+
#endif
/////////////////////////////////////////////////////////////////////////////
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;
+}
}
};
+// return a very temporary (and never deallocated) buffer. keep small.
+void *rxvt_temp_buf (int len);
+
+template<typename T>
+inline T *
+rxvt_temp_buf (int len)
+{
+ return (T *)rxvt_temp_buf (len * sizeof (T));
+}
+
#endif
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
{
=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