From 0ffbeb50b0a7b1bbe6b2f8fc613dd3605205c827 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 18 May 2017 02:43:18 +0000 Subject: *** empty log message *** --- src/command.C | 4155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4155 insertions(+) create mode 100644 src/command.C 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 + * - original version + * Copyright (c) 1994 Robert Nation + * - extensive modifications + * Copyright (c) 1995 Garrett D'Amore + * - vt100 printing + * Copyright (c) 1995 Steven Hirsch + * - X11 mouse report mode and support for + * DEC "private mode" save/restore functions. + * Copyright (c) 1995 Jakub Jelinek + * - key-related changes to handle Shift+function + * keys properly. + * Copyright (c) 1997 MJ Olesen + * - extensive modifications + * Copyright (c) 1997 Raul Garcia Garcia + * - modification and cleanups for Solaris 2.x + * and Linux 1.2.x + * Copyright (c) 1997,1998 Oezguer Kesim + * Copyright (c) 1998-2001 Geoff Wing + * - extensive modifications + * Copyright (c) 1998 Alfredo K. Kojima + * Copyright (c) 2001 Marius Gedminas + * - Ctrl/Mod4+Tab works like Meta+Tab (options) + * Copyright (c) 2003 Rob McMullen + * Copyright (c) 2003-2014 Marc Lehmann + * Copyright (c) 2007,2015 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 + * 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 + +#if LINUX_YIELD_HACK +# include +#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 (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 (); ) + cmd_parse (); + + if (revents & ev::WRITE) + pty_write (); + + refresh_check (); +} + +void ecb_cold +rxvt_term::pointer_unblank () +{ + XDefineCursor (dpy, vt, TermWin_cursor); + recolor_cursor (); + +#ifdef POINTER_BLANK + hidden_pointer = 0; + + if (option (Opt_pointerBlank)) + pointer_ev.start (pointerBlankDelay); +#endif +} + +#ifdef POINTER_BLANK +void ecb_cold +rxvt_term::pointer_blank () +{ + if (!option (Opt_pointerBlank)) + return; + + XDefineCursor (dpy, vt, display->blank_cursor); + XFlush (dpy); + + hidden_pointer = 1; +} + +void ecb_cold +rxvt_term::pointer_cb (ev::timer &w, int revents) +{ + make_current (); + + pointer_blank (); +} +#endif + +void +rxvt_term::mouse_report (XButtonEvent &ev) +{ + int button_number, key_state = 0; + int x, y; + int code = 32; + + x = Pixel2Col (ev.x) + 1; + y = Pixel2Row (ev.y) + 1; + + if (ev.type == MotionNotify) + { + if (x == mouse_row && y == mouse_col) + return; + + mouse_row = x; + mouse_col = y; + code += 32; + } + + if (MEvent.button == AnyButton) + button_number = 3; + else + { + button_number = MEvent.button - Button1; + /* add 0x3D for wheel events, like xterm does */ + if (button_number >= 3) + button_number += 64 - 3; + } + + if (priv_modes & PrivMode_MouseX10) + { + /* + * do not report ButtonRelease + * no state info allowed + */ + key_state = 0; + if (button_number == 3) + return; + } + else + { + /* XTerm mouse reporting needs these values: + * 4 = Shift + * 8 = Meta + * 16 = Control + * plus will add in our own Double-Click reporting + * 32 = Double Click + */ + key_state = ((MEvent.state & ShiftMask) ? 4 : 0) + + ((MEvent.state & ModMetaMask) ? 8 : 0) + + ((MEvent.state & ControlMask) ? 16 : 0); +#ifdef MOUSE_REPORT_DOUBLECLICK + key_state += ((MEvent.clicks > 1) ? 32 : 0); +#endif + } + +#if DEBUG_MOUSEREPORT + fprintf (stderr, "Mouse ["); + if (key_state & 16) + fputc ('C', stderr); + if (key_state & 4) + fputc ('S', stderr); + if (key_state & 8) + fputc ('A', stderr); + if (key_state & 32) + fputc ('2', stderr); + fprintf (stderr, "]: <%d>, %d/%d\n", + button_number, + x, + y); +#endif + +#if ENABLE_FRILLS + if (priv_modes & PrivMode_ExtMouseRight) + tt_printf ("\033[%d;%d;%dM", + code + button_number + key_state, + x, + y); + else if (priv_modes & PrivMode_ExtModeMouse) + tt_printf ("\033[M%c%lc%lc", + code + button_number + key_state, + wint_t (32 + x), + wint_t (32 + y)); + else +#endif + tt_printf ("\033[M%c%c%c", + code + button_number + key_state, + 32 + x, + 32 + y); +} + +/*{{{ process an X event */ +void ecb_hot +rxvt_term::x_cb (XEvent &ev) +{ + make_current (); + + dLocal (Display *, dpy); + + if (ev.xany.window == vt + && SHOULD_INVOKE (HOOK_X_EVENT) + && HOOK_INVOKE ((this, HOOK_X_EVENT, DT_XEVENT, &ev, DT_END))) + return; + + // for XQueryPointer + Window unused_root, unused_child; + int unused_root_x, unused_root_y; + unsigned int unused_mask; + + switch (ev.type) + { + case KeyPress: + key_press (ev.xkey); + break; + + case KeyRelease: + key_release (ev.xkey); + break; + + case ButtonPress: + button_press (ev.xbutton); + break; + + case ButtonRelease: + button_release (ev.xbutton); + break; + + case ClientMessage: + if (ev.xclient.format == 32 + && !HOOK_INVOKE ((this, HOOK_CLIENT_MESSAGE, DT_XEVENT, &ev, DT_END))) + { + if (ev.xclient.message_type == xa[XA_WM_PROTOCOLS]) + { + if (!HOOK_INVOKE ((this, HOOK_WM_PROTOCOLS, DT_XEVENT, &ev, DT_END))) + { + if (ev.xclient.data.l[0] == xa[XA_WM_DELETE_WINDOW]) + { + if (!HOOK_INVOKE ((this, HOOK_WM_DELETE_WINDOW, DT_XEVENT, &ev, DT_END))) + destroy (); + } +#if ENABLE_EWMH + else if (ev.xclient.data.l[0] == xa[XA_NET_WM_PING]) + XSendEvent (dpy, ev.xclient.window = display->root, + False, SubstructureRedirectMask | SubstructureNotifyMask, + &ev); +#endif + } + } +#if ENABLE_XEMBED + else if (ev.xclient.format == 32 && ev.xclient.message_type == xa[XA_XEMBED]) + { + if (ev.xclient.data.l[1] == XEMBED_FOCUS_IN) + focus_in (); + else if (ev.xclient.data.l[1] == XEMBED_FOCUS_OUT) + focus_out (); + } +#endif + } + break; + + /* + * XXX: this is not the _current_ arrangement + * Here's my conclusion: + * If the window is completely unobscured, use bitblt's + * to scroll. Even then, they're only used when doing partial + * screen scrolling. When partially obscured, we have to fill + * in the GraphicsExpose parts, which means that after each refresh, + * we need to wait for the graphics expose or Noexpose events, + * which ought to make things real slow! + */ + case VisibilityNotify: + switch (ev.xvisibility.state) + { + case VisibilityUnobscured: + refresh_type = FAST_REFRESH; + break; + case VisibilityPartiallyObscured: + refresh_type = SLOW_REFRESH; + break; + default: + refresh_type = NO_REFRESH; + break; + } + break; + + case FocusIn: + if (ev.xfocus.detail != NotifyInferior + && ev.xfocus.detail != NotifyPointer + && ev.xfocus.mode != NotifyGrab) + focus_in (); + break; + + case FocusOut: + if (ev.xfocus.detail != NotifyInferior + && ev.xfocus.detail != NotifyPointer + && ev.xfocus.mode != NotifyGrab) + focus_out (); + break; + + case ConfigureNotify: + if (ev.xconfigure.window == parent) + { + while (XCheckTypedWindowEvent (dpy, ev.xconfigure.window, ConfigureNotify, &ev)) + ; + + bool want_position_change = SHOULD_INVOKE (HOOK_POSITION_CHANGE); + + if (want_position_change) + { + int x, y; + + if (ev.xconfigure.send_event) + { + x = ev.xconfigure.x; + y = ev.xconfigure.y; + } + else + get_window_origin (x, y); + + if (x != parent_x || y != parent_y) + { + parent_x = x; + parent_y = y; + HOOK_INVOKE ((this, HOOK_POSITION_CHANGE, DT_INT, x, DT_INT, y, DT_END)); + } + } + + if (szHint.width != ev.xconfigure.width || szHint.height != ev.xconfigure.height) + { + seen_resize = 1; + resize_all_windows (ev.xconfigure.width, ev.xconfigure.height, 1); + } + + HOOK_INVOKE ((this, HOOK_CONFIGURE_NOTIFY, DT_XEVENT, &ev, DT_END)); + } + break; + + case PropertyNotify: + HOOK_INVOKE ((this, HOOK_PROPERTY_NOTIFY, DT_XEVENT, &ev, DT_END)); + break; + + case SelectionClear: + selection_clear (ev.xselectionclear.selection == xa[XA_CLIPBOARD]); + break; + + case SelectionRequest: + selection_send (ev.xselectionrequest); + break; + + case MapNotify: + mapped = 1; +#ifdef TEXT_BLINK + text_blink_ev.start (); +#endif + HOOK_INVOKE ((this, HOOK_MAP_NOTIFY, DT_XEVENT, &ev, DT_END)); + break; + + case UnmapNotify: + mapped = 0; +#ifdef TEXT_BLINK + text_blink_ev.stop (); +#endif + HOOK_INVOKE ((this, HOOK_UNMAP_NOTIFY, DT_XEVENT, &ev, DT_END)); + break; + + case GraphicsExpose: + case Expose: + if (ev.xany.window == vt) + { + do + { + scr_expose (ev.xexpose.x, ev.xexpose.y, + ev.xexpose.width, ev.xexpose.height, false); + } + while (XCheckTypedWindowEvent (dpy, vt, ev.xany.type, &ev)); + + ev.xany.type = ev.xany.type == Expose ? GraphicsExpose : Expose; + + while (XCheckTypedWindowEvent (dpy, vt, ev.xany.type, &ev)) + { + scr_expose (ev.xexpose.x, ev.xexpose.y, + ev.xexpose.width, ev.xexpose.height, false); + } + + want_refresh = 1; + } + else + { + XEvent unused_event; + + while (XCheckTypedWindowEvent (dpy, ev.xany.window, Expose, &unused_event)) + ; + while (XCheckTypedWindowEvent (dpy, ev.xany.window, GraphicsExpose, &unused_event)) + ; + + if (scrollBar.state && ev.xany.window == scrollBar.win) + { + scrollBar.state = SB_STATE_IDLE; + scrollBar.show (0); + } + } + break; + + case MotionNotify: +#ifdef POINTER_BLANK + if (hidden_pointer) + pointer_unblank (); +#endif + if (!bypass_keystate + && ((priv_modes & PrivMode_MouseBtnEvent && ev.xbutton.state & (Button1Mask|Button2Mask|Button3Mask)) + || priv_modes & PrivMode_MouseAnyEvent)) + mouse_report (ev.xbutton); + if ((priv_modes & PrivMode_mouse_report) && !bypass_keystate) + break; + + if (ev.xany.window == vt) + { + if (SHOULD_INVOKE (HOOK_MOTION_NOTIFY) + && HOOK_INVOKE ((this, HOOK_MOTION_NOTIFY, DT_XEVENT, &ev, DT_END))) + ; // nop + else if (ev.xbutton.state & (Button1Mask | Button3Mask)) + { + while (XCheckTypedWindowEvent (dpy, vt, MotionNotify, &ev)) + ; + + XQueryPointer (dpy, vt, + &unused_root, &unused_child, + &unused_root_x, &unused_root_y, + &ev.xbutton.x, &ev.xbutton.y, + &ev.xbutton.state); +#ifdef MOUSE_THRESHOLD + /* deal with a `jumpy' mouse */ + if (ev.xmotion.time - MEvent.time > MOUSE_THRESHOLD) +#endif + { +#if ISO_14755 + // 5.4 + if (iso14755buf & (ISO_14755_STARTED | ISO_14755_54)) + { + iso14755_54 (ev.xbutton.x, ev.xbutton.y); + break; + } +#endif + selection_extend (ev.xbutton.x, ev.xbutton.y, + ev.xbutton.state & Button3Mask ? 2 : 0); + +#ifdef SELECTION_SCROLLING + if (ev.xbutton.y < int_bwidth + || Pixel2Row (ev.xbutton.y) > (nrow-1)) + { + page_dirn scroll_selection_dir; + int dist; + + /* don't clobber the current delay if we are + * already in the middle of scrolling. + */ + if (!sel_scroll_ev.is_active ()) + sel_scroll_ev.start (SCROLLBAR_INITIAL_DELAY, SCROLLBAR_CONTINUOUS_DELAY); + + /* save the event params so we can highlight + * the selection in the pending-scroll loop + */ + selection_save_x = ev.xbutton.x; + selection_save_y = ev.xbutton.y; + selection_save_state = (ev.xbutton.state & Button3Mask) ? 2 : 0; + + /* calc number of lines to scroll */ + if (ev.xbutton.y < int_bwidth) + { + scroll_selection_dir = UP; + dist = int_bwidth - ev.xbutton.y; + } + else + { + scroll_selection_dir = DN; + dist = ev.xbutton.y - (int_bwidth + vt_height); + } + + scroll_selection_lines = Pixel2Height (dist) + / SELECTION_SCROLL_LINE_SPEEDUP + + 1; + min_it (scroll_selection_lines, + SELECTION_SCROLL_MAX_LINES); + scroll_selection_lines *= scroll_selection_dir; + } + else + { + /* we are within the text window, so we + * shouldn't be scrolling + */ + sel_scroll_ev.stop(); + } +#endif + } + } + } + else if (scrollBar.state == SB_STATE_MOTION && ev.xany.window == scrollBar.win) + { + while (XCheckTypedWindowEvent (dpy, scrollBar.win, + MotionNotify, &ev)) + ; + + XQueryPointer (dpy, scrollBar.win, + &unused_root, &unused_child, + &unused_root_x, &unused_root_y, + &ev.xbutton.x, &ev.xbutton.y, + &unused_mask); + scr_move_to (scrollBar.position (ev.xbutton.y) - csrO, + scrollBar.size ()); + want_refresh = 1; + scrollBar.show (1); + } + break; + } + +#if defined(CURSOR_BLINK) + if (ev.type == KeyPress) + cursor_blink_reset (); +#endif + +#if defined(POINTER_BLANK) + if (option (Opt_pointerBlank) && pointerBlankDelay > 0) + { + if (ev.type == MotionNotify + || ev.type == ButtonPress + || ev.type == ButtonRelease) + if (hidden_pointer) + pointer_unblank (); + + if (ev.type == KeyPress && hidden_pointer == 0) + pointer_blank (); + } +#endif + + refresh_check (); +} + +#if ENABLE_FRILLS +void ecb_cold +rxvt_term::set_urgency (bool enable) +{ + if (enable == urgency_hint) + return; + + if (XWMHints *h = XGetWMHints (dpy, parent)) + { + h->flags = h->flags & ~XUrgencyHint | (enable ? XUrgencyHint : 0); + XSetWMHints (dpy, parent, h); + urgency_hint = enable; + XFree (h); + } +} +#endif + +void ecb_cold +rxvt_term::focus_in () +{ + if (!focus) + { + focus = 1; + want_refresh = 1; + +#if USE_XIM + if (Input_Context != NULL) + { + im_set_position (); + XSetICFocus (Input_Context); + } +#endif +#if CURSOR_BLINK + if (option (Opt_cursorBlink)) + cursor_blink_ev.again (); +#endif +#if OFF_FOCUS_FADING + if (rs[Rs_fade]) + { + pix_colors = pix_colors_focused; + scr_recolor (); + } +#endif +#if ENABLE_FRILLS + if (option (Opt_urgentOnBell)) + set_urgency (0); + + if (priv_modes & PrivMode_FocusEvent) + tt_printf ("\x1b[I"); +#endif + + HOOK_INVOKE ((this, HOOK_FOCUS_IN, DT_END)); + } +} + +void ecb_cold +rxvt_term::focus_out () +{ + if (focus) + { + focus = 0; + want_refresh = 1; + +#if ENABLE_FRILLS + if (option (Opt_urgentOnBell)) + set_urgency (0); + + if (priv_modes & PrivMode_FocusEvent) + tt_printf ("\x1b[O"); +#endif +#if ENABLE_FRILLS || ISO_14755 + if (iso14755buf) + { + iso14755buf = 0; +# if ISO_14755 + scr_overlay_off (); +# endif + } +#endif +#if USE_XIM + if (Input_Context != NULL) + XUnsetICFocus (Input_Context); +#endif +#if CURSOR_BLINK + if (option (Opt_cursorBlink)) + cursor_blink_ev.stop (); + + hidden_cursor = 0; +#endif +#if OFF_FOCUS_FADING + if (rs[Rs_fade]) + { + pix_colors = pix_colors_unfocused; + scr_recolor (); + } +#endif + + HOOK_INVOKE ((this, HOOK_FOCUS_OUT, DT_END)); + } +} + +void ecb_cold +rxvt_term::update_fade_color (unsigned int idx, bool first_time) +{ +#if OFF_FOCUS_FADING + if (rs[Rs_fade]) + { + if (!first_time) + pix_colors_focused [idx].free (this); + + rgba c; + pix_colors [Color_fade].get (c); + pix_colors_focused [idx].fade (this, atoi (rs[Rs_fade]), pix_colors_unfocused [idx], c); + } +#endif +} + +#if ENABLE_PERL +void ecb_hot +rxvt_term::rootwin_cb (XEvent &ev) +{ + make_current (); + + if (SHOULD_INVOKE (HOOK_ROOT_EVENT) + && HOOK_INVOKE ((this, HOOK_ROOT_EVENT, DT_XEVENT, &ev, DT_END))) + return; + + switch (ev.type) + { + case PropertyNotify: + /* + * if user used some Esetroot compatible prog to set the root bg, + * use the property to determine the pixmap. We use it later on. + */ + if (ev.xproperty.atom == xa[XA_XROOTPMAP_ID] + || ev.xproperty.atom == xa[XA_ESETROOT_PMAP_ID]) + { + HOOK_INVOKE ((this, HOOK_ROOTPMAP_CHANGE, DT_END)); + } + + break; + } + + refresh_check (); +} +#endif + +void +rxvt_term::button_press (XButtonEvent &ev) +{ + int reportmode = 0, clickintime; + + bypass_keystate = ev.state & (ModMetaMask | ShiftMask); + + if (!bypass_keystate) + reportmode = !! (priv_modes & PrivMode_mouse_report); + + /* + * VT window processing of button press + */ + if (ev.window == vt) + { + if (HOOK_INVOKE ((this, HOOK_BUTTON_PRESS, DT_XEVENT, &ev, DT_END))) + return; + +#if ISO_14755 + // 5.4 + if (iso14755buf & (ISO_14755_STARTED | ISO_14755_54)) + { + iso14755_54 (ev.x, ev.y); + return; + } +#endif + + clickintime = ev.time - MEvent.time < multiClickTime; + + if (reportmode) + { + /* mouse report from vt window */ + /* save the xbutton state (for ButtonRelease) */ + MEvent.state = ev.state; +#ifdef MOUSE_REPORT_DOUBLECLICK + if (ev.button == MEvent.button && clickintime) + { + /* same button, within allowed time */ + MEvent.clicks++; + + if (MEvent.clicks > 1) + { + /* only report double clicks */ + MEvent.clicks = 2; + mouse_report (ev); + + /* don't report the release */ + MEvent.clicks = 0; + MEvent.button = AnyButton; + } + } + else + { + /* different button, or time expired */ + MEvent.clicks = 1; + MEvent.button = ev.button; + mouse_report (ev); + } +#else + MEvent.button = ev.button; + mouse_report (ev); +#endif /* MOUSE_REPORT_DOUBLECLICK */ + + } + else + { + if (ev.button != MEvent.button) + MEvent.clicks = 0; + + switch (ev.button) + { + case Button1: + /* allow meta + click to select rectangular areas */ + /* should be done in screen.C */ +#if ENABLE_FRILLS + selection.rect = !!(ev.state & ModMetaMask); +#else + selection.rect = false; +#endif + + /* allow shift+left click to extend selection */ + if (ev.state & ShiftMask && !(priv_modes & PrivMode_mouse_report)) + { + if (MEvent.button == Button1 && clickintime) + selection_rotate (ev.x, ev.y); + else + selection_extend (ev.x, ev.y, 1); + } + else + { + if (MEvent.button == Button1 && clickintime) + MEvent.clicks++; + else + MEvent.clicks = 1; + + selection_click (MEvent.clicks, ev.x, ev.y); + } + + MEvent.button = Button1; + break; + + case Button3: + if (MEvent.button == Button3 && clickintime) + selection_rotate (ev.x, ev.y); + else + selection_extend (ev.x, ev.y, 1); + + MEvent.button = Button3; + break; + } + } + + MEvent.time = ev.time; + return; + } + + /* + * Scrollbar window processing of button press + */ + if (scrollBar.state && ev.window == scrollBar.win) + { + page_dirn direction = NO_DIR; + + if (scrollBar.upButton (ev.y)) + direction = UP; /* up */ + else if (scrollBar.dnButton (ev.y)) + direction = DN; /* down */ + + scrollBar.state = SB_STATE_IDLE; + /* + * Rxvt-style scrollbar: + * move up if mouse is above slider + * move dn if mouse is below slider + * + * XTerm-style scrollbar: + * Move display proportional to pointer location + * pointer near top -> scroll one line + * pointer near bot -> scroll full page + */ +#ifndef NO_SCROLLBAR_REPORT + if (reportmode) + { + /* + * Mouse report disabled scrollbar: + * arrow buttons - send up/down + * click on scrollbar - send pageup/down + */ + if (direction == UP) + tt_printf ("\033[A"); + else if (direction == DN) + tt_printf ("\033[B"); + else + switch (ev.button) + { + case Button2: + tt_printf ("\014"); + break; + case Button1: + tt_printf ("\033[6~"); + break; + case Button3: + tt_printf ("\033[5~"); + break; + } + } + else +#endif /* NO_SCROLLBAR_REPORT */ + { + if (direction != NO_DIR) + { +#ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING + if (!cont_scroll_ev.is_active ()) + cont_scroll_ev.start (SCROLLBAR_INITIAL_DELAY, SCROLLBAR_CONTINUOUS_DELAY); +#endif + if (scr_page (direction, 1)) + { + if (direction == UP) + scrollBar.state = SB_STATE_UP; + else + scrollBar.state = SB_STATE_DOWN; + } + } + else + switch (ev.button) + { + case Button2: + switch (scrollBar.align) + { + case SB_ALIGN_TOP: + csrO = 0; + break; + case SB_ALIGN_CENTRE: + csrO = (scrollBar.bot - scrollBar.top) / 2; + break; + case SB_ALIGN_BOTTOM: + csrO = scrollBar.bot - scrollBar.top; + break; + } + + if (scrollBar.style == SB_STYLE_XTERM + || scrollBar.above_slider (ev.y) + || scrollBar.below_slider (ev.y)) + scr_move_to (scrollBar.position (ev.y) - csrO, scrollBar.size ()); + + scrollBar.state = SB_STATE_MOTION; + break; + + case Button1: + if (scrollBar.align == SB_ALIGN_CENTRE) + csrO = ev.y - scrollBar.top; + /* FALLTHROUGH */ + + case Button3: + if (scrollBar.style != SB_STYLE_XTERM) + { + if (scrollBar.above_slider (ev.y)) +# ifdef RXVT_SCROLL_FULL + scr_page (UP, nrow - 1); +# else + scr_page (UP, nrow / 4); +# endif + else if (scrollBar.below_slider (ev.y)) +# ifdef RXVT_SCROLL_FULL + scr_page (DN, nrow - 1); +# else + scr_page (DN, nrow / 4); +# endif + else + scrollBar.state = SB_STATE_MOTION; + } + else + { + scr_page ((ev.button == Button1 ? DN : UP), + (nrow + * scrollBar.position (ev.y) + / scrollBar.size ())); + } + + break; + } + } + + return; + } +} + +void +rxvt_term::button_release (XButtonEvent &ev) +{ + int reportmode = 0; + + csrO = 0; /* reset csr Offset */ + if (!bypass_keystate) + reportmode = !! (priv_modes & PrivMode_mouse_report); + + if (scrollBar.state == SB_STATE_UP || scrollBar.state == SB_STATE_DOWN) + { + scrollBar.state = SB_STATE_IDLE; + scrollBar.show (0); + } + +#ifdef SELECTION_SCROLLING + sel_scroll_ev.stop(); +#endif + + if (ev.window == vt) + { + if (HOOK_INVOKE ((this, HOOK_BUTTON_RELEASE, DT_XEVENT, &ev, DT_END))) + return; + +#if ISO_14755 + // 5.4 + if (iso14755buf & (ISO_14755_STARTED | ISO_14755_54)) + return; +#endif + + if (reportmode) + { + /* mouse report from vt window */ + /* don't report release of wheel "buttons" */ + if (ev.button >= 4) + return; +#ifdef MOUSE_REPORT_DOUBLECLICK + /* only report the release of 'slow' single clicks */ + if (MEvent.button != AnyButton + && (ev.button != MEvent.button + || (ev.time - MEvent.time + > multiClickTime / 2))) + { + MEvent.clicks = 0; + MEvent.button = AnyButton; + mouse_report (ev); + } +#else /* MOUSE_REPORT_DOUBLECLICK */ + MEvent.button = AnyButton; + mouse_report (ev); +#endif /* MOUSE_REPORT_DOUBLECLICK */ + return; + } + + /* + * dumb hack to compensate for the failure of click-and-drag + * when overriding mouse reporting + */ + if (priv_modes & PrivMode_mouse_report + && bypass_keystate + && ev.button == Button1 && MEvent.clicks <= 1) + selection_extend (ev.x, ev.y, 0); + + switch (ev.button) + { + case Button1: + case Button3: + selection_make (ev.time); + break; + + case Button2: + if (IN_RANGE_EXC (ev.x, 0, vt_width) && IN_RANGE_EXC (ev.y, 0, vt_height)) // inside window? + selection_request (ev.time, ev.state & ModMetaMask ? Sel_Clipboard : Sel_Primary); + break; + +#ifdef MOUSE_WHEEL + case Button4: + case Button5: + { + int lines; + page_dirn dirn; + + dirn = ev.button == Button4 ? UP : DN; + + if (ev.state & ShiftMask) + lines = 1; + else if (option (Opt_mouseWheelScrollPage)) + lines = nrow - 1; + else + lines = 5; + +# ifdef MOUSE_SLIP_WHEELING + if (ev.state & ControlMask) + { + mouse_slip_wheel_speed += dirn; + clamp_it (mouse_slip_wheel_speed, -nrow, nrow); + + if (!slip_wheel_ev.is_active ()) + slip_wheel_ev.start (SCROLLBAR_CONTINUOUS_DELAY, SCROLLBAR_CONTINUOUS_DELAY); + } + else +# endif + { + scr_page (dirn, lines); + scrollBar.show (1); + } + } + break; +#endif + } + } +} + +/*}}} */ + +void ecb_hot +rxvt_term::cmd_parse () +{ + wchar_t ch = NOCHAR; + char *seq_begin; // remember start of esc-sequence here + + for (;;) + { + if (ecb_unlikely (ch == NOCHAR)) + { + seq_begin = cmdbuf_ptr; + ch = next_char (); + + if (ch == NOCHAR) + break; + } + + if (ecb_likely (!IS_CONTROL (ch) || ch == C0_LF || ch == C0_CR || ch == C0_HT)) + { + if (ecb_unlikely (!seen_input)) + { + seen_input = 1; + // many badly-written programs (e.g. jed) contain a race condition: + // they first read the screensize and then install a SIGWINCH handler. + // some window managers resize the window early, and these programs + // then sometimes get the size wrong. + // unfortunately other programs are even more buggy and dislike + // being sent SIGWINCH, so only do it when we were in fact being + // resized. + if (seen_resize && cmd_pid) + kill (-cmd_pid, SIGWINCH); + } + + /* Read a text string from the input buffer */ + wchar_t buf[UBUFSIZ]; + bool refreshnow = false; + int nlines = 0; + wchar_t *str = buf; + wchar_t *eol = str + min (ncol, UBUFSIZ); + + for (;;) + { + if (ecb_unlikely (ch == NOCHAR || (IS_CONTROL (ch) && ch != C0_LF && ch != C0_CR && ch != C0_HT))) + break; + + *str++ = ch; + + if (ecb_unlikely (ch == C0_LF || str >= eol)) + { + if (ch == C0_LF) + nlines++; + + refresh_count++; + + if (!option (Opt_jumpScroll) || refresh_count >= nrow - 1) + { + refresh_count = 0; + + if (!option (Opt_skipScroll) || ev_time () > ev::now () + 1. / 60.) + { + refreshnow = true; + ch = NOCHAR; + break; + } + } + + // scr_add_lines only works for nlines <= nrow - 1. + if (nlines >= nrow - 1) + { + if (!(SHOULD_INVOKE (HOOK_ADD_LINES) + && HOOK_INVOKE ((this, HOOK_ADD_LINES, DT_WCS_LEN, buf, str - buf, DT_END)))) + scr_add_lines (buf, str - buf, nlines); + + nlines = 0; + str = buf; + eol = str + min (ncol, UBUFSIZ); + } + + if (str >= eol) + { + if (eol >= buf + UBUFSIZ) + { + ch = NOCHAR; + break; + } + else + eol = min (eol + ncol, buf + UBUFSIZ); + } + + } + + seq_begin = cmdbuf_ptr; + ch = next_char (); + } + + if (!(SHOULD_INVOKE (HOOK_ADD_LINES) + && HOOK_INVOKE ((this, HOOK_ADD_LINES, DT_WCS_LEN, buf, str - buf, DT_END)))) + scr_add_lines (buf, str - buf, nlines); + + /* + * If there have been a lot of new lines, then update the screen + * What the heck we'll cheat and only refresh less than every page-full. + * if skipScroll is enabled. + */ + if (refreshnow) + { + scr_refresh (); + want_refresh = 1; + } + } + else + { + try + { + process_nonprinting (ch); + } + catch (const class out_of_input &o) + { + // we ran out of input, retry later + cmdbuf_ptr = seq_begin; + break; + } + + ch = NOCHAR; + } + } +} + +// read the next character +wchar_t ecb_hot +rxvt_term::next_char () NOTHROW +{ + while (cmdbuf_ptr < cmdbuf_endp) + { + // assume 7-bit to be ascii ALWAYS + if (ecb_likely ((unsigned char)*cmdbuf_ptr <= 0x7f && *cmdbuf_ptr != 0x1b)) + return *cmdbuf_ptr++; + + wchar_t wc; + size_t len = mbrtowc (&wc, cmdbuf_ptr, cmdbuf_endp - cmdbuf_ptr, mbstate); + + if