diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/command.C | 4155 |
1 files changed, 4155 insertions, 0 deletions
diff --git a/src/command.C b/src/command.C new file mode 100644 index 0000000..ed376ed --- /dev/null +++ b/src/command.C @@ -0,0 +1,4155 @@ +/*----------------------------------------------------------------------* + * File: command.C + *----------------------------------------------------------------------* + * + * All portions of code are copyright by their respective author/s. + * Copyright (c) 1992 John Bovey, University of Kent at Canterbury <jdb@ukc.ac.uk> + * - original version + * Copyright (c) 1994 Robert Nation <nation@rocket.sanders.lockheed.com> + * - extensive modifications + * Copyright (c) 1995 Garrett D'Amore <garrett@netcom.com> + * - vt100 printing + * Copyright (c) 1995 Steven Hirsch <hirsch@emba.uvm.edu> + * - X11 mouse report mode and support for + * DEC "private mode" save/restore functions. + * Copyright (c) 1995 Jakub Jelinek <jj@gnu.ai.mit.edu> + * - key-related changes to handle Shift+function + * keys properly. + * Copyright (c) 1997 MJ Olesen <olesen@me.queensu.ca> + * - extensive modifications + * Copyright (c) 1997 Raul Garcia Garcia <rgg@tid.es> + * - modification and cleanups for Solaris 2.x + * and Linux 1.2.x + * Copyright (c) 1997,1998 Oezguer Kesim <kesim@math.fu-berlin.de> + * Copyright (c) 1998-2001 Geoff Wing <gcw@pobox.com> + * - extensive modifications + * Copyright (c) 1998 Alfredo K. Kojima <kojima@windowmaker.org> + * Copyright (c) 2001 Marius Gedminas + * - Ctrl/Mod4+Tab works like Meta+Tab (options) + * Copyright (c) 2003 Rob McMullen <robm@flipturn.org> + * Copyright (c) 2003-2014 Marc Lehmann <schmorp@schmorp.de> + * Copyright (c) 2007,2015 Emanuele Giaquinta <e.giaquinta@glauco.it> + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + *----------------------------------------------------------------------*/ + +/*{{{ includes: */ +#include "../config.h" +#include "rxvt.h" +#include "rxvtperl.h" +#include "version.h" +#include "command.h" + +#ifdef KEYSYM_RESOURCE +# include "keyboard.h" +#endif + +#include <signal.h> + +#if LINUX_YIELD_HACK +# include <time.h> +#endif + +/*----------------------------------------------------------------------*/ + +#define IS_CONTROL(ch) !((ch) & 0xffffff60UL) + +#if ENABLE_FRILLS || ISO_14755 + +#define ISO_14755_STARTED 0x80000000UL +#define ISO_14755_51 0x40000000UL // basic (section 5.1) +#define ISO_14755_52 0x20000000UL // keycap (section 5.2) +#define ISO_14755_54 0x10000000UL // code feedback (section 5.4) +#define ISO_14755_MASK 0x0fffffffUL + +#if ISO_14755 +static unsigned short iso14755_symtab[] = { + // keysym, unicode + XK_Left, 0x2190, + XK_KP_Left, 0x2190, + XK_Up, 0x2191, + XK_KP_Up, 0x2191, + XK_Right, 0x2192, + XK_KP_Right, 0x2192, + XK_Down, 0x2193, + XK_KP_Down, 0x2193, + XK_Linefeed, 0x21b4, + XK_Return, 0x21b5, + XK_KP_Enter, 0x21b5, + + XK_Prior, 0x21de, + XK_Next, 0x21df, + XK_Tab, 0x21e5, + XK_ISO_Left_Tab, 0x21e6, + XK_Shift_L, 0x21e7, + XK_Shift_R, 0x21e7, + + XK_Shift_Lock, 0x21eb, + XK_ISO_Lock, 0x21eb, + XK_Caps_Lock, 0x21ec, + XK_Num_Lock, 0x21ed, + XK_ISO_Level3_Shift, 0x21ee, + XK_ISO_Level3_Lock, 0x21ef, + XK_ISO_Group_Lock, 0x21f0, + XK_Home, 0x21f1, + XK_End, 0x21f2, + + XK_Execute, 0x2318, + XK_Begin, 0x2320, + XK_Delete, 0x2326, + XK_Clear, 0x2327, + XK_BackSpace, 0x232b, + XK_Insert, 0x2380, + XK_Control_L, 0x2388, + XK_Control_R, 0x2388, + XK_Pause, 0x2389, + XK_Break, 0x238a, + XK_Escape, 0x238b, + XK_Undo, 0x238c, + XK_Print, 0x2399, + + XK_space, 0x2423, + +#ifdef XK_KP_Begin + XK_KP_Prior, 0x21de, + XK_KP_Next, 0x21df, + XK_KP_Begin, 0x2320, + XK_KP_Insert, 0x2380, + XK_KP_Delete, 0x2326, + XK_KP_Space, 0x2422, +#endif + 0, +}; + +void ecb_cold +rxvt_term::iso14755_54 (int x, int y) +{ + x = Pixel2Col (x); + y = Pixel2Row (y); + + if (!IN_RANGE_EXC (x, 0, ncol) + || !IN_RANGE_EXC (y, 0, nrow)) + return; + + for (;;) + { + const line_t &l = ROW(y + view_start); + + text_t t = l.t[x]; + + if (t != NOCHAR || !x) + { + iso14755_51 (l.t[x], l.r[x], x, y, view_start); + iso14755buf = ISO_14755_54; + break; + } + + x--; + } +} + +void ecb_cold +rxvt_term::iso14755_51 (unicode_t ch, rend_t r, int x, int y, int y2) +{ + rxvt_fontset *fs = FONTSET (r); + wchar_t *chr, *alloc, ch2, **fname; + int len; + +# if ENABLE_COMBINING + if (IS_COMPOSE (ch)) + { + len = rxvt_composite.expand (ch, 0); + alloc = chr = new wchar_t[len]; + rxvt_composite.expand (ch, chr); + } + else +# endif + { + ch2 = ch; + + alloc = 0; + chr = &ch2; + len = 1; + } + + char rowcol[40]; + snprintf (rowcol, sizeof rowcol, "col %d row %d @%d", x, y, y2); + + char attr[80]; // plenty + + sprintf (attr, "%08x = fg %d bg %d%s%s%s%s%s%s", + (int)r, + fgcolor_of (r), bgcolor_of (r), + r & RS_Bold ? " bold" : "", + r & RS_Italic ? " italic" : "", + r & RS_Blink ? " blink" : "", + r & RS_RVid ? " rvid" : "", + r & RS_Uline ? " uline" : "", + r & RS_Careful ? " careful" : ""); + + int width = 0; + fname = rxvt_temp_buf<wchar_t *> (len); + for (int i = 0; i < len; i++) + { + rxvt_font *f = (*fs)[fs->find_font_idx (chr[i])]; + fname[i] = rxvt_utf8towcs (f->name); + max_it (width, wcswidth (fname[i], wcslen (fname[i]))); + } + + max_it (width, strlen (attr)); + + if (y >= 0) + { + y = (y >= nrow - len - 5 && x < width + 2) ? 0 : -1; + x = 0; + } + + scr_overlay_new (x, y, width, len * 2 + 2); + + scr_overlay_set (0, 0, rowcol); + + r = SET_STYLE (OVERLAY_RSTYLE, GET_STYLE (r)); + + for (int y = 0; y < len; y++) + { + char buf[9]; + + ch = *chr++; + + sprintf (buf, "%8x", ch); + scr_overlay_set (0, y + 1, buf); + scr_overlay_set (9, y + 1, '='); +# if !UNICODE_3 + if (ch >= 0x10000) + ch = 0xfffd; +# endif + scr_overlay_set (11, y + 1, ch, r); + + if (WCWIDTH (ch) >= 2) + scr_overlay_set (12, y + 1, NOCHAR, r); + } + +// { +// char buf[4+4+3+1]; +// snprintf (buf, sizeof (buf), "(%.4d|%.4d)", x, y); +// scr_overlay_set (0, 0, buf); +// } + scr_overlay_set (0, len + 1, attr); + for (int i = 0; i < len; i++) + { + scr_overlay_set (0, len + 2 + i, fname[i]); + free (fname[i]); + } + +# if ENABLE_COMBINING + if (alloc) + delete [] alloc; +# endif +} +#endif + +void ecb_cold +rxvt_term::commit_iso14755 () +{ + wchar_t ch = iso14755buf & ISO_14755_MASK; + + if (iso14755buf & ISO_14755_51) + { + char mb[MB_LEN_MAX]; + int len; + + // allow verbatim 0-bytes and control-bytes to be entered + if (ch >= 0x20) + len = wctomb (mb, ch); + else + { + mb[0] = ch; + len = 1; + } + + if (len > 0) + tt_write (mb, len); + else + scr_bell (); + } + + iso14755buf = 0; +} + +static int ecb_cold +hex_keyval (XKeyEvent &ev) +{ + // check whether this event corresponds to a hex digit + // if the modifiers had not been pressed. + for (int index = 0; index < 8; index++) + { + KeySym k = XLookupKeysym (&ev, index); + + if (k >= XK_KP_0 && k <= XK_KP_9) return k - XK_KP_0; + else if (k >= XK_0 && k <= XK_9) return k - XK_0; + else if (k >= XK_a && k <= XK_f) return k - XK_a + 10; + else if (k >= XK_A && k <= XK_F) return k - XK_A + 10; + } + + return -1; +} +#endif + +static inline KeySym ecb_cold +translate_keypad (KeySym keysym, bool kp) +{ +#ifdef XK_KP_Home + static const KeySym keypadtrans[] = { + XK_KP_7, // XK_KP_Home + XK_KP_4, // XK_KP_Left + XK_KP_8, // XK_KP_Up + XK_KP_6, // XK_KP_Right + XK_KP_2, // XK_KP_Down + XK_KP_9, // XK_KP_Prior + XK_KP_3, // XK_KP_Next + XK_KP_1, // XK_KP_End + XK_KP_5, // XK_KP_Begin + }; + + if (IN_RANGE_INC (keysym, XK_KP_Home, XK_KP_Begin)) + { + unsigned int index = keysym - XK_KP_Home; + keysym = kp ? keypadtrans[index] : XK_Home + index; + } + else if (keysym == XK_KP_Insert) + keysym = kp ? XK_KP_0 : XK_Insert; +# ifndef NO_DELETE_KEY + else if (keysym == XK_KP_Delete) + keysym = kp ? XK_KP_Decimal : XK_Delete; +# endif +#endif + return keysym; +} + +static inline int ecb_cold +map_function_key (KeySym keysym) +{ + int param = 0; + + if (IN_RANGE_INC (keysym, XK_F1, XK_F35)) + { + param = 11 + keysym - XK_F1; + if (keysym >= XK_F17) + param += 4; + else if (keysym >= XK_F15) + param += 3; + else if (keysym >= XK_F11) + param += 2; + else if (keysym >= XK_F6) + param += 1; + } + else + switch (keysym) + { + case XK_Find: + param = 1; + break; + case XK_Insert: + param = 2; + break; +#ifdef DXK_Remove + case DXK_Remove: +#endif + case XK_Execute: + param = 3; + break; + case XK_Select: + param = 4; + break; + case XK_Prior: + param = 5; + break; + case XK_Next: + param = 6; + break; + case XK_Home: + param = 7; + break; + case XK_End: + param = 8; + break; + case XK_Help: + param = 28; + break; + case XK_Menu: + param = 29; + break; + } + return param; +} + +static inline wchar_t * +rxvt_wcsdup (const wchar_t *str, int len) +{ + wchar_t *r = (wchar_t *)rxvt_malloc ((len + 1) * sizeof (wchar_t)); + memcpy (r, str, len * sizeof (wchar_t)); + r[len] = 0; + return r; +} + +void ecb_cold +rxvt_term::key_press (XKeyEvent &ev) +{ + int ctrl, meta, shft, len; + KeySym keysym = NoSymbol; + char rkbuf[KBUFSZ + 1]; + char *kbuf = rkbuf + 1; + +#if ISO_14755 + if (iso14755buf & ISO_14755_52) + return; +#endif + + /* + * use Num_Lock to toggle Keypad on/off. If Num_Lock is off, allow an + * escape sequence to toggle the Keypad. + * + * Always permit `shift' to override the current setting + */ + shft = ev.state & ShiftMask; + ctrl = ev.state & ControlMask; + meta = ev.state & ModMetaMask; + + kbuf[0] = 0; + +#if USE_XIM + if (Input_Context) + { + Status status_return; + +#if 0 +#ifdef X_HAVE_UTF8_STRING + if (enc_utf8 && 0) // currently disabled, doesn't seem to work, nor is useful + len = Xutf8LookupString (Input_Context, &ev, kbuf, + KBUFSZ, &keysym, &status_return); + else +#endif +#endif + { + wchar_t wkbuf[KBUFSZ + 1]; + + // the XOpenIM manpage lies about hardcoding the locale + // at the point of XOpenIM, so temporarily switch locales + if (rs[Rs_imLocale]) + SET_LOCALE (rs[Rs_imLocale]); + + // assume wchar_t == unicode or better + len = XwcLookupString (Input_Context, &ev, wkbuf, + KBUFSZ, &keysym, &status_return); + + if (rs[Rs_imLocale]) + SET_LOCALE (locale); + + if (status_return == XLookupChars + || status_return == XLookupBoth) + { + /* make sure the user can type ctrl-@, i.e. NUL */ + if (len == 1 && *wkbuf == 0) + { + kbuf[0] = 0; + len = 1; + } + else + { + wkbuf[len] = 0; + len = wcstombs ((char *)kbuf, wkbuf, KBUFSZ); + if (len < 0) + len = 0; + } + } + else + len = 0; + } + } + else +#endif + { + len = XLookupString (&ev, kbuf, KBUFSZ, &keysym, &compose); + } + + if (keysym != NoSymbol) + { + KeySym orig_keysym = keysym; + + /* Shift + F1 - F10 generates F11 - F20 */ + if (shft && keysym >= XK_F1 && keysym <= XK_F10) + { + keysym += (XK_F11 - XK_F1); + shft = 0; /* turn off Shift */ + } + + if (keysym >= 0xFF00 && keysym <= 0xFFFF) + { + bool kp = priv_modes & PrivMode_aplKP ? !shft : shft; + unsigned int newlen = 1; + + if (ev.state & ModNumLockMask) + kp = false; + + keysym = translate_keypad (keysym, kp); + + switch (keysym) + { +#ifndef NO_BACKSPACE_KEY + case XK_BackSpace: + if (priv_modes & PrivMode_HaveBackSpace) + { + kbuf[0] = (!! (priv_modes & PrivMode_BackSpace) + ^ !!ctrl) ? '\b' : '\177'; + kbuf[1] = '\0'; + } + else + strcpy (kbuf, rs[Rs_backspace_key]); + break; +#endif +#ifndef NO_DELETE_KEY + case XK_Delete: + strcpy (kbuf, rs[Rs_delete_key]); + break; +#endif + case XK_Tab: + if (shft) + strcpy (kbuf, "\033[Z"); + else + { +#ifdef CTRL_TAB_MAKES_META + if (ctrl) + meta = 1; +#endif +#ifdef MOD4_TAB_MAKES_META + if (ev.state & Mod4Mask) + meta = 1; +#endif + newlen = 0; + } + break; + + case XK_Up: /* "\033[A" */ + case XK_Down: /* "\033[B" */ + case XK_Right: /* "\033[C" */ + case XK_Left: /* "\033[D" */ + strcpy (kbuf, "\033[Z"); + kbuf[2] = "DACB"[keysym - XK_Left]; + /* do Shift first */ + if (shft) + kbuf[2] = "dacb"[keysym - XK_Left]; + else if (ctrl) + { + kbuf[1] = 'O'; + kbuf[2] = "dacb"[keysym - XK_Left]; + } + else if (priv_modes & PrivMode_aplCUR) + kbuf[1] = 'O'; + break; + + case XK_KP_Enter: + /* allow shift to override */ + if (kp) + { + strcpy (kbuf, "\033OM"); + break; + } + + /* FALLTHROUGH */ + + case XK_Return: + if (priv_modes & PrivMode_LFNL) + { + kbuf[0] = '\015'; + kbuf[1] = '\012'; + kbuf[2] = '\0'; + } + else + { + kbuf[0] = '\015'; + kbuf[1] = '\0'; + } + break; + + case XK_KP_F1: /* "\033OP" */ + case XK_KP_F2: /* "\033OQ" */ + case XK_KP_F3: /* "\033OR" */ + case XK_KP_F4: /* "\033OS" */ + strcpy (kbuf, "\033OP"); + kbuf[2] += (keysym - XK_KP_F1); + break; + + case XK_KP_Multiply: /* "\033Oj" : "*" */ + case XK_KP_Add: /* "\033Ok" : "+" */ + case XK_KP_Separator: /* "\033Ol" : "," */ + case XK_KP_Subtract: /* "\033Om" : "-" */ + case XK_KP_Decimal: /* "\033On" : "." */ + case XK_KP_Divide: /* "\033Oo" : "/" */ + case XK_KP_0: /* "\033Op" : "0" */ + case XK_KP_1: /* "\033Oq" : "1" */ + case XK_KP_2: /* "\033Or" : "2" */ + case XK_KP_3: /* "\033Os" : "3" */ + case XK_KP_4: /* "\033Ot" : "4" */ + case XK_KP_5: /* "\033Ou" : "5" */ + case XK_KP_6: /* "\033Ov" : "6" */ + case XK_KP_7: /* "\033Ow" : "7" */ + case XK_KP_8: /* "\033Ox" : "8" */ + case XK_KP_9: /* "\033Oy" : "9" */ + /* allow shift to override */ + if (kp) + { + strcpy (kbuf, "\033Oj"); + kbuf[2] += (keysym - XK_KP_Multiply); + } + else + { + kbuf[0] = ('*' + (keysym - XK_KP_Multiply)); + kbuf[1] = '\0'; + } + break; + + default: + { + int param = map_function_key (keysym); + if (param > 0) + sprintf (kbuf,"\033[%d~", param); + else + newlen = 0; + } + break; + } + + if (newlen) + len = strlen (kbuf); + + if (len > 0) + { + /* + * pass Shift/Control indicators for function keys ending with `~' + * + * eg, + * Prior = "ESC[5~" + * Shift+Prior = "ESC[5$" + * Ctrl+Prior = "ESC[5^" + * Ctrl+Shift+Prior = "ESC[5@" + */ + if (kbuf[0] == C0_ESC && kbuf[1] == '[' && kbuf[len - 1] == '~') + kbuf[len - 1] = (shft ? (ctrl ? '@' : '$') : (ctrl ? '^' : '~')); + + /* + * Pass meta for all function keys, if 'meta' option set + */ +#ifdef META8_OPTION + if (meta && (meta_char == 0x80)) + kbuf[len - 1] |= 0x80; +#endif + } + + } + else if (ctrl && keysym == XK_minus) + { + len = 1; + kbuf[0] = '\037'; /* Ctrl-Minus generates ^_ (31) */ + } + else if (keysym == XK_ISO_Left_Tab) + { + strcpy (kbuf, "\033[Z"); + len = 3; + } + else + { +#ifdef META8_OPTION + /* set 8-bit on */ + if (meta && (meta_char == 0x80)) + { + char *ch; + + for (ch = kbuf; ch < kbuf + len; ch++) + *ch |= 0x80; + } +#endif + /* nil */ ; + } + + keysym = orig_keysym; + } + + /* escape prefix */ + if (len && meta +#ifdef META8_OPTION + && meta_char == C0_ESC +#endif + ) + { + *--kbuf = C0_ESC; + len++; + } + + if (HOOK_INVOKE ((this, HOOK_KEY_PRESS, DT_XEVENT, &ev, DT_INT, keysym, DT_STR_LEN, kbuf, len, DT_END))) + return; + + if (keysym != NoSymbol) + { +#ifdef KEYSYM_RESOURCE + if (keyboard->dispatch (this, keysym, ev.state, kbuf, len)) + return; +#endif + + if (saveLines) + { +#ifdef UNSHIFTED_SCROLLKEYS + if (!ctrl && !meta) +#else + if (IS_SCROLL_MOD) +#endif + { + int lnsppg; + +#ifdef PAGING_CONTEXT_LINES + lnsppg = nrow - PAGING_CONTEXT_LINES; +#else + lnsppg = nrow * 4 / 5; +#endif + max_it (lnsppg, 1); + + if (keysym == XK_Prior) + { + scr_page (lnsppg); + return; + } + else if (keysym == XK_Next) + { + scr_page (-lnsppg); + return; + } + } +#ifdef SCROLL_ON_UPDOWN_KEYS + if (IS_SCROLL_MOD) + { + if (keysym == XK_Up) + { + scr_page (1); + return; + } + else if (keysym == XK_Down) + { + scr_page (-1); + return; + } + } +#endif +#ifdef SCROLL_ON_HOMEEND_KEYS + if (IS_SCROLL_MOD) + { + if (keysym == XK_Home) + { + scr_changeview (top_row); + return; + } + else if (keysym == XK_End) + { + scr_changeview (0); + return; + } + } +#endif + } + + if (shft) + { + if (!ctrl && !meta && (priv_modes & PrivMode_ShiftKeys)) + { + switch (keysym) + { + /* normal XTerm key bindings */ + case XK_Insert: /* Shift+Insert = paste mouse selection */ + selection_request (ev.time); + return; +#if TODO + /* rxvt extras */ + case XK_KP_Add: /* Shift+KP_Add = bigger font */ + return; + case XK_KP_Subtract: /* Shift+KP_Subtract = smaller font */ + return; +#endif + } + } + } + + if (ctrl && meta && (keysym == XK_c || keysym == XK_v)) + { + if (keysym == XK_v) + selection_request (ev.time, Sel_Clipboard); + else if (selection.len > 0) + { + free (selection.clip_text); + selection.clip_text = rxvt_wcsdup (selection.text, selection.len); + selection.clip_len = selection.len; + selection_grab (CurrentTime, true); + } + + return; + } + +#if ENABLE_FRILLS || ISO_14755 + // ISO 14755 support + if (iso14755buf & (ISO_14755_STARTED | ISO_14755_51)) + { + int hv; + + if (iso14755buf & ISO_14755_51 + && (keysym == XK_space || keysym == XK_KP_Space + || keysym == XK_Return || keysym == XK_KP_Enter)) + { + commit_iso14755 (); + iso14755buf = ISO_14755_51; +# if ISO_14755 + iso14755_51 (0); +# endif + return; + } + else if (keysym == XK_BackSpace) + { + iso14755buf = ((iso14755buf & ISO_14755_MASK) >> 4) | ISO_14755_51; +# if ISO_14755 + iso14755_51 (iso14755buf & ISO_14755_MASK); +# endif + return; + } + else if ((hv = hex_keyval (ev)) >= 0) + { + iso14755buf = ((iso14755buf << 4) & ISO_14755_MASK) + | hv | ISO_14755_51; +# if ISO_14755 + iso14755_51 (iso14755buf & ISO_14755_MASK); +# endif + return; + } + else + { +# if ISO_14755 + scr_overlay_off (); +# endif + iso14755buf = 0; + } + } + else if (option (Opt_iso14755) && + ((ctrl && (keysym == XK_Shift_L || keysym == XK_Shift_R)) + || (shft && (keysym == XK_Control_L || keysym == XK_Control_R)))) + if (!(iso14755buf & ISO_14755_STARTED)) + { + iso14755buf |= ISO_14755_STARTED; +# if ISO_14755 + scr_overlay_new (0, -1, sizeof ("ISO 14755 mode") - 1, 1); + scr_overlay_set (0, 0, "ISO 14755 mode"); +# endif + } +#endif + +#ifdef PRINTPIPE + if (keysym == XK_Print) + { + scr_printscreen (ctrl | shft); + return; + } +#endif + } + + if (len <= 0) + return; /* not mapped */ + + tt_write_user_input (kbuf, (unsigned int)len); +} + +void ecb_cold +rxvt_term::key_release (XKeyEvent &ev) +{ +#if (MOUSE_WHEEL && MOUSE_SLIP_WHEELING) || ISO_14755 || ENABLE_PERL + KeySym keysym; + + keysym = XLookupKeysym (&ev, ev.state & ShiftMask ? 1 : 0); // sorry, only shift supported :/ +#endif + +#if ENABLE_FRILLS || ISO_14755 + // ISO 14755 support + if (iso14755buf) + if (iso14755buf & ISO_14755_52) + { +# if ISO_14755 + scr_overlay_off (); +# endif +# if ISO_14755 + // iso14755 part 5.2 handling: release time + // first: controls + if ((ev.state & ControlMask) + && ((keysym >= 0x40 && keysym <= 0x5f) + || (keysym >= 0x61 && keysym <= 0x7f))) + { + iso14755buf = ISO_14755_51 | 0x2400 | (keysym & 0x1f); + commit_iso14755 (); + + return; + } + + for (unsigned short *i = iso14755_symtab; i[0]; i += 2) + if (i[0] == keysym) + { + iso14755buf = ISO_14755_51 | i[1]; + commit_iso14755 (); + + return; + } + + scr_bell (); +# endif + iso14755buf = 0; + + return; + } + else if ((ev.state & (ShiftMask | ControlMask)) != (ShiftMask | ControlMask)) + { +# if ISO_14755 + scr_overlay_off (); +# endif + if (iso14755buf & ISO_14755_51) + commit_iso14755 (); +#if ISO_14755 + else if (option (Opt_iso14755_52) && iso14755buf & ISO_14755_STARTED) + { + iso14755buf = ISO_14755_52; // iso14755 part 5.2: remember empty begin/end pair + + scr_overlay_new (0, -1, sizeof ("KEYCAP PICTURE INSERT MODE") - 1, 1); + scr_overlay_set (0, 0, "KEYCAP PICTURE INSERT MODE"); + } +# endif + else + iso14755buf = 0; + } +#endif + + if (HOOK_INVOKE ((this, HOOK_KEY_RELEASE, DT_XEVENT, &ev, DT_INT, keysym, DT_END))) + return; + +#if defined(MOUSE_WHEEL) && defined(MOUSE_SLIP_WHEELING) + if (!(ev.state & ControlMask)) + slip_wheel_ev.stop (); + else if (keysym == XK_Control_L || keysym == XK_Control_R) + mouse_slip_wheel_speed = 0; +#endif +} + +void +rxvt_term::flush () +{ + flush_ev.stop (); + +#ifdef HAVE_IMG + if (bg_flags & BG_NEEDS_REFRESH) + { + bg_flags &= ~BG_NEEDS_REFRESH; + scr_touch (false); + } +#endif + + if (want_refresh) + { + if (SHOULD_INVOKE (HOOK_LINE_UPDATE)) + { + int row = view_start; + int end_row = row + nrow; + + while (row > top_row && ROW (row - 1).is_longer ()) + --row; + + do + { + int start_row = row; + line_t *l; + + do + { + l = &ROW (row++); + + if (!(l->f & LINE_FILTERED)) + { + // line not filtered, mark it as filtered + l->f |= LINE_FILTERED; + while (l->is_longer ()) + { + l = &ROW (row++); + l->f |= LINE_FILTERED; + } + + // and filter it + HOOK_INVOKE ((this, HOOK_LINE_UPDATE, DT_INT, start_row, DT_END)); + + break; + } + } + while (l->is_longer () && row < end_row); + } + while (row < end_row); + } + + scr_refresh (); + scrollBar.show (1); +#if USE_XIM + im_send_spot (); +#endif + } + + display->flush (); +} + +/* checks whether a refresh is requested and starts the refresh timer */ +void +rxvt_term::refresh_check () +{ + if (want_refresh && !flush_ev.is_active ()) + flush_ev.start (1. / 60.); // refresh at max. 60 Hz normally + + display->flush (); +} + +void +rxvt_term::flush_cb (ev::timer &w, int revents) +{ + make_current (); + + refresh_count = 0; + flush (); +} + +#ifdef CURSOR_BLINK +void +rxvt_term::cursor_blink_reset () +{ + if (!focus) + return; + + if (hidden_cursor) + { + hidden_cursor = 0; + want_refresh = 1; + } + + if (option (Opt_cursorBlink) || (priv_modes & PrivMode_BlinkingCursor)) + cursor_blink_ev.again (); + else + cursor_blink_ev.stop (); +} + +void +rxvt_term::cursor_blink_cb (ev::timer &w, int revents) +{ + hidden_cursor = !hidden_cursor; + want_refresh = 1; + refresh_check (); +} +#endif + +#ifdef TEXT_BLINK +void +rxvt_term::text_blink_cb (ev::timer &w, int revents) +{ + if (scr_refresh_rend (RS_Blink, RS_Blink)) + { + hidden_text = !hidden_text; + want_refresh = 1; + refresh_check (); + } + else + w.stop (); +} +#endif + +#ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING +void +rxvt_term::cont_scroll_cb (ev::timer &w, int revents) +{ + if ((scrollBar.state == SB_STATE_UP || scrollBar.state == SB_STATE_DOWN) + && scr_page (scrollBar.state == SB_STATE_UP ? UP : DN, 1)) + { + want_refresh = 1; + refresh_check (); + } + else + w.stop (); +} +#endif + +#ifdef SELECTION_SCROLLING +void +rxvt_term::sel_scroll_cb (ev::timer &w, int revents) +{ + if (scr_page (scroll_selection_lines)) + { + selection_extend (selection_save_x, selection_save_y, selection_save_state); + want_refresh = 1; + refresh_check (); + } + else + w.stop (); +} +#endif + +#if defined(MOUSE_WHEEL) && defined(MOUSE_SLIP_WHEELING) +void +rxvt_term::slip_wheel_cb (ev::timer &w, int revents) +{ + if (scr_page (mouse_slip_wheel_speed)) + { + want_refresh = 1; + refresh_check (); + } + + if (view_start == top_row || view_start == 0 || mouse_slip_wheel_speed == 0) + { + mouse_slip_wheel_speed = 0; + w.stop (); + } +} +#endif + +#if LINUX_YIELD_HACK +static struct event_handler +{ + ev::prepare yield_ev; + + void yield_cb (ev::prepare &w, int revents) + { + // this should really be sched_yield(), but the linux guys thought + // that giving a process calling sched_yield () less cpu time than + // ones with high nice levels is a useful thing to do. It surely is is + // allowed by the sus... as is returning ENOSYS. + // since the linux guys additionally thought that breaking the only + // known workaround against their unusable sched_yield hack is cool, + // we just nanosleep a bit and hope for the best. + + struct timespec ts = { 0, 1000 }; + nanosleep (&ts, 0); + + w.stop (); + } + + event_handler () + : yield_ev (this, &event_handler::yield_cb) + { + } +} event_handler; +#endif + +/* make sure all the cmd data is at beginning of cmdbuf */ +void +rxvt_term::cmdbuf_reify () +{ + if (cmdbuf_ptr == cmdbuf_base) + return; + + ssize_t used = cmdbuf_endp - cmdbuf_ptr; + + memmove (cmdbuf_base, cmdbuf_ptr, used); + cmdbuf_ptr = cmdbuf_base; + cmdbuf_endp = cmdbuf_ptr + used; + +} + +#if defined (KEYSYM_RESOURCE) +void +rxvt_term::cmdbuf_append (const char *str, size_t count) +{ + cmdbuf_reify (); + + size_t avail = cmdbuf_base + CBUFSIZ - cmdbuf_endp; + + if (count > avail) + return; + + memcpy (cmdbuf_endp, str, count); + cmdbuf_endp += count; + + cmd_parse (); +} +#endif + +bool +rxvt_term::pty_fill () +{ + cmdbuf_reify (); + + size_t avail = cmdbuf_base + CBUFSIZ - cmdbuf_endp; + + if (!avail) + { + // normally this indicates a "too long" command sequence - just drop the data we have + cmdbuf_ptr = cmdbuf_base; + cmdbuf_endp = cmdbuf_ptr; + avail = CBUFSIZ; + } + + ssize_t r = read (pty->pty, cmdbuf_endp, avail); + + if (r > 0) + { + cmdbuf_endp += r; + return true; + } + else if (r < 0 && (errno == EAGAIN || errno == EINTR)) + { +#if LINUX_YIELD_HACK + if (display->is_local) + event_handler.yield_ev.start (); +#endif + } + else + { + pty_ev.stop (); + + if (!option (Opt_hold)) + destroy (); + } + + return false; +} + +void +rxvt_term::pty_cb (ev::io &w, int revents) +{ + make_current (); + + if (revents & ev::READ) + // loop, but don't allow a single term to monopolize us + for (int i = CBUFCNT; i-- && pty_fill (); ) |