summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/command.C4155
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 (); )