[vte] emulation: Unify mode handling



commit b0bb1540a5e1321aa4944310416b3d072596a002
Author: Christian Persch <chpe src gnome org>
Date:   Tue Mar 27 19:40:12 2018 +0200

    emulation: Unify mode handling

 src/Makefile.am      |   23 ++-
 src/debug.cc         |    1 +
 src/debug.h          |    1 +
 src/modes-ecma.hh    |   65 ++++++
 src/modes-private.hh |  250 ++++++++++++++++++++
 src/modes-test.cc    |   90 ++++++++
 src/modes.hh         |  250 ++++++++++++++++++++
 src/vte.cc           |  131 ++++-------
 src/vteinternal.hh   |   68 ++----
 src/vteseq.cc        |  612 +++++++++++++++-----------------------------------
 10 files changed, 934 insertions(+), 557 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index c82f02f..a17d441 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -55,6 +55,9 @@ libvte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_la_SOURCES = \
        iso2022.h \
        keymap.cc \
        keymap.h \
+       modes.hh \
+       modes-ecma.hh \
+       modes-private.hh \
        parser.cc \
        parser.hh \
        parser-arg.hh \
@@ -175,7 +178,7 @@ vteresources.cc: vte.gresource.xml Makefile $(shell $(GLIB_COMPILE_RESOURCES) --
 
 # Misc unit tests and utilities
 
-noinst_PROGRAMS += parser-cat slowcat test-parser
+noinst_PROGRAMS += parser-cat slowcat test-modes test-parser
 noinst_SCRIPTS = decset osc window
 EXTRA_DIST += $(noinst_SCRIPTS)
 
@@ -196,6 +199,7 @@ dist_check_SCRIPTS = \
        $(NULL)
 
 TESTS = \
+       test-modes \
        test-parser \
        reaper \
        test-vtetypes \
@@ -289,6 +293,23 @@ test_parser_LDADD = \
        $(VTE_LIBS) \
        $(NULL)
 
+test_modes_SOURCES = \
+       modes-test.cc \
+       modes.hh \
+       modes-ecma.hh \
+       modes-private.hh \
+       $(NULL)
+test_modes_CPPFLAGS = \
+       -I$(builddir) \
+       -I$(srcdir) \
+       $(AM_CPPFLAGS)
+test_modes_CXXFLAGS = \
+       $(GLIB_CFLAGS) \
+       $(AM_CXXFLAGS)
+test_modes_LDADD = \
+       $(GLIB_LIBS) \
+       $(NULL)
+
 test_vtetypes_SOURCES = \
        vtetypes.cc \
        vtetypes.hh \
diff --git a/src/debug.cc b/src/debug.cc
index 1352f8b..0101f69 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -55,6 +55,7 @@ _vte_debug_init(void)
     { "resize",       VTE_DEBUG_RESIZE       },
     { "regex",        VTE_DEBUG_REGEX        },
     { "hyperlink",    VTE_DEBUG_HYPERLINK    },
+    { "modes",        VTE_DEBUG_MODES        },
   };
 
   _vte_debug_flags = g_parse_debug_string (g_getenv("VTE_DEBUG"),
diff --git a/src/debug.h b/src/debug.h
index e156477..e5222bd 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -62,6 +62,7 @@ typedef enum {
        VTE_DEBUG_RESIZE        = 1 << 22,
         VTE_DEBUG_REGEX         = 1 << 23,
         VTE_DEBUG_HYPERLINK     = 1 << 24,
+        VTE_DEBUG_MODES         = 1 << 25,
 } VteDebugFlags;
 
 void _vte_debug_init(void);
diff --git a/src/modes-ecma.hh b/src/modes-ecma.hh
new file mode 100644
index 0000000..68d7876
--- /dev/null
+++ b/src/modes-ecma.hh
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2018 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Modes for SM_ECMA/RM_ECMA.
+ *
+ * Most of these are not implemented in VTE.
+ *
+ * References: ECMA-48 § 7
+ *             WY370
+ */
+
+MODE(IRM,  4)
+MODE(SRM, 12)
+
+/* Unsupported */
+
+MODE_FIXED(GATM,  1, ALWAYS_RESET)
+MODE_FIXED(KAM,   2, ALWAYS_RESET)
+MODE_FIXED(CRM,   3, ALWAYS_RESET)
+MODE_FIXED(SRTM,  5, ALWAYS_RESET)
+MODE_FIXED(ERM,   6, ALWAYS_RESET)
+MODE_FIXED(VEM,   7, ALWAYS_RESET)
+MODE_FIXED(BDSM,  8, ALWAYS_RESET)
+MODE_FIXED(DCSM,  9, ALWAYS_RESET)
+MODE_FIXED(HEM,  10, ALWAYS_RESET)
+MODE_FIXED(PUM,  11, ALWAYS_RESET) /* ECMA-48 § F.4.1 Deprecated */
+MODE_FIXED(FEAM, 13, ALWAYS_RESET)
+MODE_FIXED(FETM, 14, ALWAYS_RESET)
+MODE_FIXED(MATM, 15, ALWAYS_RESET)
+MODE_FIXED(TTM,  16, ALWAYS_RESET)
+MODE_FIXED(SATM, 17, ALWAYS_RESET)
+MODE_FIXED(TSM,  18, ALWAYS_RESET)
+MODE_FIXED(EBM,  19, ALWAYS_RESET) /* ECMA-48 § F.5.1 Removed */
+MODE_FIXED(LNM,  20, ALWAYS_RESET) /* ECMA-48 § F.5.2 Removed */
+MODE_FIXED(GRCM, 21, ALWAYS_SET)
+MODE_FIXED(ZDM,  22, ALWAYS_RESET) /* ECMA-48 § F.4.2 Deprecated */
+
+#if 0
+MODE_FIXED(WYDSCM,    30, ALWAYS_SET)
+MODE_FIXED(WYSTLINM,  31, ALWAYS_RESET)
+MODE_FIXED(WYCRTSAVM, 32, ALWAYS_RESET)
+MODE_FIXED(WYSTCURM,  33, ?)
+MODE_FIXED(WYULCURM,  34, ?)
+MODE_FIXED(WYCLRM,    35, ALWAYS_SET)
+MODE_FIXED(WYDELKM,   36, ALWAYS_RESET) /* Same as DECBKM */
+MODE_FIXED(WYGATM,    37, ?)
+MODE_FIXED(WYTEXM,    38, ?)
+MODE_FIXED(WYEXTDM,   40, ALWAYS_SET)
+MODE_FIXED(WYASCII,   42, ALWAYS_SET)
+#endif
diff --git a/src/modes-private.hh b/src/modes-private.hh
new file mode 100644
index 0000000..f7a26e7
--- /dev/null
+++ b/src/modes-private.hh
@@ -0,0 +1,250 @@
+/*
+ * Copyright © 2018 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Modes for SM_DEC/RM_DEC.
+ *
+ * Most of these are not implemented in VTE.
+ *
+ * References: VT525
+ *             XTERM
+ *             KITTY
+ *             MINTTY
+ *             MLTERM
+ *             RLogin
+ *             URXVT
+ *             WY370
+ */
+
+/* Supported modes: */
+
+/* DEC */
+
+/*
+ * DECCKM - cursor keys mode
+ */
+MODE(DEC_APPLICATION_CURSOR_KEYS, 1)
+
+/*
+ * DECCOLM: 132 column mode
+ */
+MODE(DEC_132_COLUMN, 3)
+
+/*
+ * DECSCNM - screen mode
+ */
+MODE(DEC_REVERSE_IMAGE,  5)
+
+/*
+ * DECOM - origin mode
+ */
+MODE(DEC_ORIGIN, 6)
+
+/*
+ * DECAWM - auto wrap mode
+ */
+MODE(DEC_AUTOWRAP, 7)
+
+/*
+ * DECTCEM - text cursor enable
+ */
+MODE(DEC_TEXT_CURSOR, 25)
+
+/*
+ * DECNKM - numeric/application keypad mode
+ */
+MODE(DEC_APPLICATION_KEYPAD, 66)
+
+/* XTERM */
+
+MODE(XTERM_MOUSE_X10,                   9)
+MODE(XTERM_DECCOLM,                    40)
+MODE(XTERM_ALTBUF,                     47)
+MODE(XTERM_MOUSE_VT220,              1000)
+MODE(XTERM_MOUSE_VT220_HIGHLIGHT,    1001)
+MODE(XTERM_MOUSE_BUTTON_EVENT,       1002)
+MODE(XTERM_MOUSE_ANY_EVENT,          1003)
+MODE(XTERM_FOCUS,                    1004)
+MODE(XTERM_MOUSE_EXT_SGR,            1006)
+MODE(XTERM_ALTBUF_SCROLL,            1007)
+MODE(XTERM_META_SENDS_ESCAPE,        1036)
+MODE(XTERM_OPT_ALTBUF,               1047)
+MODE(XTERM_SAVE_CURSOR,              1048)
+MODE(XTERM_OPT_ALTBUF_SAVE_CURSOR,   1049)
+MODE(XTERM_READLINE_BRACKETED_PASTE, 2004)
+
+/* URXVT */
+
+MODE(URXVT_MOUSE_EXT, 1015)
+
+/* Not supported modes: */
+
+/* DEC */
+
+MODE_FIXED(DECANM,      2, ALWAYS_SET)
+MODE_FIXED(DECSCLM,     4, ALWAYS_RESET)
+MODE_FIXED(DECARM,      8, ALWAYS_SET)
+MODE_FIXED(DECLTM,     11, ALWAYS_RESET)
+MODE_FIXED(DECEKEM,    16, ALWAYS_RESET)
+MODE_FIXED(DECCPFF,    18, ALWAYS_RESET)
+MODE_FIXED(DECPEX,     19, ALWAYS_RESET)
+MODE_FIXED(DECRLM,     34, ALWAYS_RESET)
+MODE_FIXED(DECHEBM,    35, ALWAYS_RESET)
+MODE_FIXED(DECHEM,     36, ALWAYS_RESET)
+MODE_FIXED(DECNRCM,    42, ALWAYS_RESET)
+MODE_FIXED(DECGEPM,    43, ALWAYS_RESET) /* from VT330 */
+/* MODE_FIXED(DECGPCM,    44, ALWAYS_RESET) * from VT330, conflicts with XTERM_MARGIN_BELL */
+/* MODE_FIXED(DECGPCS,    45, ALWAYS_RESET) * from VT330, conflicts with XTERM_REVERSE_WRAP */
+/* MODE_FIXED(DECGPBM,    46, ALWAYS_RESET) * from VT330, conflicts with XTERM_LOGGING */
+/* MODE_FIXED(DECGRPM,    47, ALWAYS_RESET) * from VT330, conflicts with XTERM_ALTBUF */
+MODE_FIXED(DEC131TM,   53, ALWAYS_RESET)
+MODE_FIXED(DECNAKB,    57, ALWAYS_RESET)
+/* MODE_FIXED(DECKKDM,    59, ALWAYS_SET) * Kanji/Katakana Display Mode, from VT382-Kanji */
+MODE_FIXED(DECHCCM,    60, ALWAYS_RESET)
+MODE_FIXED(DECVCCM,    61, ALWAYS_RESET)
+MODE_FIXED(DECPCCM,    64, ALWAYS_RESET)
+MODE_FIXED(DECBKM,     67, ALWAYS_RESET)
+MODE_FIXED(DECKBUM,    68, ALWAYS_RESET)
+MODE_FIXED(DECVSSM,    69, ALWAYS_RESET) /* aka DECLRMM */
+MODE_FIXED(DECXRLM,    73, ALWAYS_RESET)
+/* MODE_FIXED(DECSDM,    80, ALWAYS_RESET) ! Conflicts with WY161 */
+MODE_FIXED(DECKPM,     81, ALWAYS_RESET)
+MODE_FIXED(DECTHAISCM, 90, ALWAYS_RESET) /* Thai Space Compensating Mode, from VT382-Thai */
+MODE_FIXED(DECNCSM,    95, ALWAYS_RESET)
+MODE_FIXED(DECRLCM,    96, ALWAYS_RESET)
+MODE_FIXED(DECRCRTSM,  97, ALWAYS_RESET)
+MODE_FIXED(DECARSM,    98, ALWAYS_RESET)
+MODE_FIXED(DECMCM,     99, ALWAYS_RESET)
+MODE_FIXED(DECAAM,    100, ALWAYS_RESET)
+MODE_FIXED(DECANSM,   101, ALWAYS_RESET)
+MODE_FIXED(DECNULM,   102, ALWAYS_RESET)
+MODE_FIXED(DECHDPXM,  103, ALWAYS_RESET)
+MODE_FIXED(DECESKM,   104, ALWAYS_RESET)
+MODE_FIXED(DECOSCNM,  106, ALWAYS_RESET)
+MODE_FIXED(DECCAPSLK, 109, ALWAYS_RESET)
+MODE_FIXED(DECFWM,    111, ALWAYS_RESET)
+MODE_FIXED(DECRPL,    112, ALWAYS_RESET)
+MODE_FIXED(DECHWUM,   113, ALWAYS_RESET)
+MODE_FIXED(DECATCUM,  114, ALWAYS_RESET)
+MODE_FIXED(DECATCBM,  115, ALWAYS_RESET)
+MODE_FIXED(DECBBSM,   116, ALWAYS_RESET)
+MODE_FIXED(DECECM,    117, ALWAYS_RESET)
+
+/* DRCSTerm */
+/* Modes 8800…8804 */
+
+/* KITTY */
+
+MODE_FIXED(KITTY_STYLED_UNDERLINES, 2016, ALWAYS_SET)
+MODE_FIXED(KITTY_EXTENDED_KEYBOARD, 2017, ALWAYS_RESET)
+
+/* MinTTY */
+
+MODE_FIXED(MINTTY_REPORT_CJK_AMBIGUOUS_WIDTH,           7700, ALWAYS_RESET)
+MODE_FIXED(MINTTY_REPORT_SCROLL_MARKER_IN_CURRENT_LINE, 7711, ALWAYS_RESET)
+MODE_FIXED(MINTTY_APPLICATION_ESCAPE,                   7727, ALWAYS_RESET)
+MODE_FIXED(MINTTY_ESCAPE_SENDS_FS,                      7728, ALWAYS_RESET)
+MODE_FIXED(MINTTY_SIXEL_SCROLLING_END_POSITION,         7730, ALWAYS_RESET)
+MODE_FIXED(MINTTY_SCROLLBAR,                            7766, ALWAYS_RESET)
+MODE_FIXED(MINTTY_REPORT_FONT_CHANGES,                  7767, ALWAYS_RESET)
+MODE_FIXED(MINTTY_SHORTCUT_OVERRIDE,                    7783, ALWAYS_RESET)
+MODE_FIXED(MINTTY_ALBUF_MOUSEWHEEL_TO_CURSORKEYS,       7786, ALWAYS_RESET)
+MODE_FIXED(MINTTY_MOUSEWHEEL_APPLICATION_KEYS,          7787, ALWAYS_RESET)
+MODE_FIXED(MINTTY_BIDI_DISABLE_IN_CURRENT_LINE,         7796, ALWAYS_RESET)
+MODE_FIXED(MINTTY_SIXEL_SCROLL_CURSOR_RIGHT,            8452, ALWAYS_RESET)
+/* MinTTY also knows mode 77096 'BIDI disable", and 77000..77031
+ * "Application control key" which are outside of the supported range
+ * for CSI parameters.
+ */
+
+/* RLogin */
+
+/* RLogin appears to use many modes
+ * [https://github.com/kmiya-culti/RLogin/blob/master/RLogin/TextRam.h#L131]:
+ * 1406..1415, 1420..1425, 1430..1434, 1436, 1452..1481,
+ * 8400..8406, 8416..8417, 8428..8429, 8435, 8437..8443,
+ * 8446..8458,
+ * and modes 7727, 7786, 8200 (home cursor on [ED 2]),
+ * 8800 (some weird Unicode plane 17 mapping?), 8840 (same as 8428).
+ *
+ * We're not going to implement them, but avoid these ranges
+ * when assigning new mode numbers.
+ *
+ * The following are the ones from RLogin that MLTerm knows about:
+ */
+
+/* MODE_FIXED(RLOGIN_APPLICATION_ESCAPE,                7727, ALWAYS_RESET) */
+/* MODE_FIXED(RLOGIN_MOUSEWHEEL_TO_CURSORKEYS,          7786, ALWAYS_RESET) */
+
+/* Ambiguous-width characters are wide (reset) or narrow (set) */
+MODE_FIXED(RLOGIN_AMBIGUOUS_WIDTH_CHARACTERS_NARROW, 8428, ALWAYS_RESET)
+
+/* MODE_FIXED(RLOGIN_CURSOR_TO_RIGHT_OF_SIXEL,          8452, ALWAYS_RESET) */
+
+/* XTERM also knows this one */
+/* MODE_FIXED(RLOGIN_SIXEL_SCROLL_CURSOR_RIGHT,         8452, ALWAYS_RESET) */
+
+/* RXVT */
+
+MODE_FIXED(RXVT_TOOLBAR,            10, ALWAYS_RESET)
+MODE_FIXED(RXVT_SCROLLBAR,          30, ALWAYS_RESET)
+/* MODE_FIXED(RXVT_SHIFT_KEYS,        35, ALWAYS_RESET) ! Conflicts with DECHEBM */
+MODE_FIXED(RXVT_SCROLL_OUTPUT,    1010, ALWAYS_RESET)
+MODE_FIXED(RXVT_SCROLL_KEYPRES,   1011, ALWAYS_RESET)
+/* Bold/blink uses normal (reset) or high intensity (set) colour */
+MODE_FIXED(RXVT_INTENSITY_STYLES, 1021, ALWAYS_SET)
+
+/* Wyse */
+
+MODE_FIXED(WYTEK,  38, ALWAYS_RESET)
+MODE_FIXED(WY161,  80, ALWAYS_RESET)
+MODE_FIXED(WY52,   83, ALWAYS_RESET)
+MODE_FIXED(WYENAT, 84, ALWAYS_RESET)
+MODE_FIXED(WYREPL, 85, ALWAYS_RESET)
+
+/* XTERM */
+
+MODE_FIXED(XTERM_ATT610_BLINK,                    12, ALWAYS_RESET)
+MODE_FIXED(XTERM_CURSOR_BLINK,                    13, ALWAYS_RESET)
+MODE_FIXED(XTERM_CURSOR_BLINK_XOR,                14, ALWAYS_RESET)
+MODE_FIXED(XTERM_CURSES_HACK,                     41, ALWAYS_RESET)
+MODE_FIXED(XTERM_MARGIN_BELL,                     44, ALWAYS_RESET)
+MODE_FIXED(XTERM_REVERSE_WRAP,                    45, ALWAYS_RESET)
+MODE_FIXED(XTERM_LOGGING,                         46, ALWAYS_RESET)
+MODE_FIXED(XTERM_MOUSE_EXT,                     1005, ALWAYS_RESET)
+MODE_FIXED(XTERM_8BIT_META,                     1034, ALWAYS_RESET)
+MODE_FIXED(XTERM_NUMLOCK,                       1035, ALWAYS_RESET)
+MODE_FIXED(XTERM_DELETE_IS_DEL,                 1037, ALWAYS_RESET)
+MODE_FIXED(XTERM_ALT_SENDS_ESCAPE,              1039, ALWAYS_RESET)
+MODE_FIXED(XTERM_KEEP_SELECTION,                1040, ALWAYS_RESET)
+MODE_FIXED(XTERM_KEEP_CLIPBOARD,                1044, ALWAYS_RESET)
+MODE_FIXED(XTERM_SELECT_TO_CLIPBOARD,           1041, ALWAYS_RESET)
+MODE_FIXED(XTERM_BELL_URGENT,                   1042, ALWAYS_RESET)
+MODE_FIXED(XTERM_PRESENT_ON_BELL,               1043, ALWAYS_RESET)
+MODE_FIXED(XTERM_ALLOW_ALTBUF,                  1046, ALWAYS_SET)
+MODE_FIXED(XTERM_FKEYS_TERMCAP,                 1050, ALWAYS_RESET)
+MODE_FIXED(XTERM_FKEYS_SUN,                     1051, ALWAYS_RESET)
+MODE_FIXED(XTERM_FKEYS_HP,                      1052, ALWAYS_RESET)
+MODE_FIXED(XTERM_FKEYS_SCO,                     1053, ALWAYS_RESET)
+MODE_FIXED(XTERM_FKEYS_LEGACY,                  1060, ALWAYS_RESET)
+MODE_FIXED(XTERM_FKEYS_VT220,                   1061, ALWAYS_RESET)
+MODE_FIXED(XTERM_SIXEL_PRIVATE_COLOR_REGISTERS, 1070, ALWAYS_SET)
+MODE_FIXED(XTERM_READLINE_BUTTON1_MOVE_POINT,   2001, ALWAYS_RESET)
+MODE_FIXED(XTERM_READLINE_BUTTON2_MOVE_POINT,   2002, ALWAYS_RESET)
+MODE_FIXED(XTERM_READLINE_DBLBUTTON3_DELETE,    2003, ALWAYS_RESET)
+MODE_FIXED(XTERM_READLINE_PASTE_QUOTE,          2005, ALWAYS_RESET)
+MODE_FIXED(XTERM_READLINE_PASTE_LITERAL_NL,     2006, ALWAYS_RESET)
diff --git a/src/modes-test.cc b/src/modes-test.cc
new file mode 100644
index 0000000..39f4cfb
--- /dev/null
+++ b/src/modes-test.cc
@@ -0,0 +1,90 @@
+/*
+ * Copyright © 2018 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include <glib.h>
+
+#include "modes.hh"
+
+static void
+test_modes_ecma(void)
+{
+        vte::terminal::modes::ECMA modes{};
+
+        g_assert_false(modes.IRM());
+        g_assert_true(modes.SRM());
+        modes.set_IRM(true);
+        g_assert_true(modes.IRM());
+        g_assert_true(modes.SRM());
+        modes.set_SRM(false);
+        g_assert_true(modes.IRM());
+        g_assert_false(modes.SRM());
+
+        vte::terminal::modes::ECMA copy{modes};
+        g_assert_cmpuint(copy.get_modes(), ==, modes.get_modes());
+        g_assert_cmpint(copy.IRM(), ==, modes.IRM());
+        g_assert_cmpint(copy.SRM(), ==, modes.SRM());
+
+        modes.reset();
+        g_assert_false(modes.IRM());
+        g_assert_true(modes.SRM());
+}
+
+static void
+test_modes_private(void)
+{
+        vte::terminal::modes::Private modes{};
+
+        g_assert_true(modes.DEC_AUTOWRAP());
+        g_assert_true(modes.XTERM_META_SENDS_ESCAPE());
+
+        g_assert_false(modes.XTERM_FOCUS());
+        modes.set_XTERM_FOCUS(true);
+        g_assert_true(modes.XTERM_FOCUS());
+        modes.push_saved(vte::terminal::modes::Private::eXTERM_FOCUS);
+        modes.set_XTERM_FOCUS(false);
+        g_assert_false(modes.XTERM_FOCUS());
+        bool set = modes.pop_saved(vte::terminal::modes::Private::eXTERM_FOCUS);
+        g_assert_true(set);
+        modes.set_XTERM_FOCUS(set);
+        g_assert_true(modes.XTERM_FOCUS());
+        modes.push_saved(vte::terminal::modes::Private::eXTERM_FOCUS);
+        modes.clear_saved();
+        set = modes.pop_saved(vte::terminal::modes::Private::eXTERM_FOCUS);
+        g_assert_false(set);
+}
+
+int
+main(int argc,
+     char* argv[])
+{
+        g_test_init(&argc, &argv, nullptr);
+
+        g_test_add_func("/vte/modes/ecma", test_modes_ecma);
+        g_test_add_func("/vte/modes/private", test_modes_private);
+
+        return g_test_run();
+}
diff --git a/src/modes.hh b/src/modes.hh
new file mode 100644
index 0000000..b7fe53f
--- /dev/null
+++ b/src/modes.hh
@@ -0,0 +1,250 @@
+/*
+ * Copyright © 2018 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+namespace vte {
+namespace terminal {
+namespace modes {
+
+#define VTE_MODES_MASK(shift) (1U << (shift))
+
+#define MODE_ACCESSOR(name) \
+        inline void set_##name(bool value) noexcept \
+        { \
+                set(e##name, value); \
+        } \
+        \
+        inline constexpr bool name() const noexcept \
+        { \
+                return get(e##name); \
+        }
+
+template <typename T>
+static inline void vte_modes_set_bool(T* modes,
+                                      unsigned int shift,
+                                      bool value)
+{
+        if (value)
+                *modes |= T(1U) << shift;
+        else
+                *modes &= ~(T(1U) << shift);
+}
+
+template <typename T>
+static constexpr inline bool vte_modes_get_bool(T modes,
+                                                unsigned int shift)
+{
+        return (modes >> shift) & 1U;
+}
+
+template <typename T>
+static constexpr inline bool vte_modes_unset_bool(T* modes,
+                                                  unsigned int shift)
+{
+        bool set = vte_modes_get_bool<T>(*modes, shift);
+        vte_modes_set_bool<T>(modes, shift, false);
+        return set;
+}
+
+template <typename T>
+class Base
+{
+public:
+        using Self = Base<T>;
+        using Storage = T;
+
+        constexpr Base(std::initializer_list<int> modes)
+        {
+                for (auto i : modes)
+                        m_default_modes |= VTE_MODES_MASK(i);
+                m_modes = m_default_modes;
+        }
+
+        ~Base() = default;
+        Base(Self const&) = default;
+        Base(Self&&) = default;
+        Self& operator= (Self const&) = default;
+        Self& operator= (Self&&) = default;
+
+        inline void set(int bit,
+                        bool value) noexcept
+        {
+                vte_modes_set_bool<Storage>(&m_modes, bit, value);
+        }
+
+        constexpr inline bool get(int bit) const noexcept
+        {
+                return vte_modes_get_bool<Storage>(m_modes, bit);
+        }
+
+        inline void set_modes(Storage value) noexcept
+        {
+                m_modes = value;
+        }
+
+        constexpr inline Storage get_modes() const noexcept
+        {
+                return m_modes;
+        }
+
+        void reset() noexcept
+        {
+                set_modes(m_default_modes);
+        }
+
+private:
+        T m_modes{0};
+        T m_default_modes{0};
+};
+
+class ECMA : public Base<uint8_t>
+{
+public:
+        enum Modes {
+                eUNKNOWN      = -3,
+                eALWAYS_SET   = -2,
+                eALWAYS_RESET = -1,
+
+#define MODE(name,param) e##name,
+#define MODE_FIXED(name,param,value) e##name,
+#include "modes-ecma.hh"
+#undef MODE
+#undef MODE_FIXED
+        };
+
+        int mode_from_param(int param) const noexcept
+        {
+                switch (param) {
+#define MODE(name,param) case param: return e##name;
+#define MODE_FIXED(name,param,value) case param: return e##value;
+#include "modes-ecma.hh"
+#undef MODE
+#undef MODE_FIXED
+                default:
+                        return eUNKNOWN;
+                }
+        }
+
+        char const* mode_to_cstring(int param) const noexcept
+        {
+                switch (param) {
+                case eUNKNOWN: return "UNKNOWN";
+                case eALWAYS_SET: return "ALWAYS_SET";
+                case eALWAYS_RESET: return "ALWAYS_RESET";
+#define MODE(name,param) case e##name: return #name;
+#define MODE_FIXED(name,param,value)
+#include "modes-ecma.hh"
+#undef MODE
+#undef MODE_FIXED
+                default:
+                        return "INVALID";
+                }
+        }
+
+#define MODE(name,param) MODE_ACCESSOR(name)
+#define MODE_FIXED(name,param,value)
+#include "modes-ecma.hh"
+#undef MODE
+#undef MODE_FIXED
+
+        constexpr ECMA() : Self{eSRM} { }
+
+}; // class ECMA
+
+class Private : public Base<uint32_t>
+{
+public:
+        enum Modes {
+                eUNKNOWN      = -3,
+                eALWAYS_SET   = -2,
+                eALWAYS_RESET = -1,
+
+#define MODE(name,param) e##name,
+#define MODE_FIXED(name,param,value) e##name,
+#include "modes-private.hh"
+#undef MODE
+#undef MODE_FIXED
+        };
+
+        int mode_from_param(int param) const noexcept
+        {
+                switch (param) {
+#define MODE(name,param) case param: return e##name;
+#define MODE_FIXED(name,param,value) case param: return e##value;
+#include "modes-private.hh"
+#undef MODE
+#undef MODE_FIXED
+                default:
+                        return eUNKNOWN;
+                }
+        }
+
+        char const* mode_to_cstring(int param) const noexcept
+        {
+                switch (param) {
+                case eUNKNOWN: return "UNKNOWN";
+                case eALWAYS_SET: return "ALWAYS_SET";
+                case eALWAYS_RESET: return "ALWAYS_RESET";
+#define MODE(name,param) case e##name: return #name;
+#define MODE_FIXED(name,param,value)
+#include "modes-private.hh"
+#undef MODE
+#undef MODE_FIXED
+                default:
+                        return "INVALID";
+                }
+        }
+
+#define MODE(name,param) MODE_ACCESSOR(name)
+#define MODE_FIXED(name,param,value)
+#include "modes-private.hh"
+#undef MODE
+#undef MODE_FIXED
+
+        constexpr Private() : Self{eDEC_AUTOWRAP,
+                                   eDEC_TEXT_CURSOR,
+                                   eXTERM_ALTBUF_SCROLL,
+                                   eXTERM_META_SENDS_ESCAPE} { }
+
+        inline void push_saved(int mode)
+        {
+                vte_modes_set_bool<Storage>(&m_saved_modes, mode, get(mode));
+        }
+
+        constexpr inline bool pop_saved(int mode)
+        {
+                return vte_modes_unset_bool<Storage>(&m_saved_modes, mode);
+        }
+
+        inline void clear_saved()
+        {
+                m_saved_modes = 0;
+        }
+
+private:
+        Storage m_saved_modes{0};
+
+}; // class Private
+
+#undef MODE_ACCESSOR
+
+} // namespace modes
+} // namespace terminal
+} // namespace vte
diff --git a/src/vte.cc b/src/vte.cc
index 5864b6d..cf1c55c 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -666,7 +666,7 @@ VteTerminalPrivate::invalidate_cursor_once(bool periodic)
                }
        }
 
-       if (m_cursor_visible) {
+       if (m_modes_private.DEC_TEXT_CURSOR()) {
                auto preedit_width = get_preedit_width(false);
                 auto row = m_screen->cursor.row;
                 auto column = m_screen->cursor.col;
@@ -2889,10 +2889,11 @@ VteTerminalPrivate::restore_cursor(VteScreen *screen__)
         screen__->cursor.row = screen__->insert_delta + CLAMP(screen__->saved.cursor.row,
                                                               0, m_row_count - 1);
 
-        m_reverse_mode = screen__->saved.reverse_mode;
-        m_origin_mode = screen__->saved.origin_mode;
-        m_sendrecv_mode = screen__->saved.sendrecv_mode;
-        m_insert_mode = screen__->saved.insert_mode;
+        m_modes_ecma.set_modes(screen__->saved.modes_ecma);
+
+        m_modes_private.set_DEC_REVERSE_IMAGE(screen__->saved.reverse_mode);
+        m_modes_private.set_DEC_ORIGIN(screen__->saved.origin_mode);
+
         m_defaults = screen__->saved.defaults;
         m_color_defaults = screen__->saved.color_defaults;
         m_fill_defaults = screen__->saved.fill_defaults;
@@ -2908,10 +2909,11 @@ VteTerminalPrivate::save_cursor(VteScreen *screen__)
         screen__->saved.cursor.col = screen__->cursor.col;
         screen__->saved.cursor.row = screen__->cursor.row - screen__->insert_delta;
 
-        screen__->saved.reverse_mode = m_reverse_mode;
-        screen__->saved.origin_mode = m_origin_mode;
-        screen__->saved.sendrecv_mode = m_sendrecv_mode;
-        screen__->saved.insert_mode = m_insert_mode;
+        screen__->saved.modes_ecma = m_modes_ecma.get_modes();
+
+        screen__->saved.reverse_mode = m_modes_private.DEC_REVERSE_IMAGE();
+        screen__->saved.origin_mode = m_modes_private.DEC_ORIGIN();
+
         screen__->saved.defaults = m_defaults;
         screen__->saved.color_defaults = m_color_defaults;
         screen__->saved.fill_defaults = m_fill_defaults;
@@ -2968,7 +2970,7 @@ VteTerminalPrivate::insert_char(gunichar c,
                 0x00b7,  /* ~ => bullet */
         };
 
-        insert |= m_insert_mode;
+        insert |= m_modes_ecma.IRM();
        invalidate_now |= insert;
 
        /* If we've enabled the special drawing set, map the characters to
@@ -2987,7 +2989,7 @@ VteTerminalPrivate::insert_char(gunichar c,
        /* If we're autowrapping here, do it. */
         col = m_screen->cursor.col;
        if (G_UNLIKELY (columns && col + columns > m_column_count)) {
-               if (m_autowrap) {
+               if (m_modes_private.DEC_AUTOWRAP()) {
                        _vte_debug_print(VTE_DEBUG_ADJ,
                                        "Autowrapping before character\n");
                        /* Wrap. */
@@ -3517,7 +3519,7 @@ VteTerminalPrivate::process_incoming()
 
        /* Save the current cursor position. */
         saved_cursor = m_screen->cursor;
-       saved_cursor_visible = m_cursor_visible;
+       saved_cursor_visible = m_modes_private.DEC_TEXT_CURSOR();
         saved_cursor_style = m_cursor_style;
 
         in_scroll_region = m_scrolling_restricted
@@ -3832,7 +3834,7 @@ skip_chunk:
                check_cursor_blink();
                /* Signal that the cursor moved. */
                queue_cursor_moved();
-        } else if ((saved_cursor_visible != m_cursor_visible) ||
+        } else if ((saved_cursor_visible != m_modes_private.DEC_TEXT_CURSOR()) ||
                    (saved_cursor_style != m_cursor_style)) {
                invalidate_cell(saved_cursor.col, saved_cursor.row);
                check_cursor_blink();
@@ -4122,7 +4124,7 @@ VteTerminalPrivate::pty_io_write(GIOChannel *channel,
 void
 VteTerminalPrivate::send_child(char const* data,
                                gssize length,
-                               bool local_echo)
+                               bool local_echo) noexcept
 {
        gsize icount, ocount;
        const guchar *ibuf;
@@ -4265,8 +4267,7 @@ VteTerminalPrivate::feed_child_using_modes(char const* data,
                length = strlen(data);
 
        if (length > 0)
-               send_child(data, length,
-                           !m_sendrecv_mode);
+               send_child(data, length, !m_modes_ecma.SRM());
 }
 
 /* Send text from the input method to the child. */
@@ -4484,7 +4485,7 @@ VteTerminalPrivate::check_cursor_blink()
 {
        if (m_has_focus &&
            m_cursor_blinks &&
-           m_cursor_visible)
+           m_modes_private.DEC_TEXT_CURSOR())
                add_cursor_timeout();
        else
                remove_cursor_timeout();
@@ -4900,8 +4901,8 @@ VteTerminalPrivate::widget_key_press(GdkEventKey *event)
                 * it to a literal or capability name. */
                 if (handled == FALSE) {
                        _vte_keymap_map(keyval, m_modifiers,
-                                       m_cursor_mode == VTE_KEYMODE_APPLICATION,
-                                       m_keypad_mode == VTE_KEYMODE_APPLICATION,
+                                        m_modes_private.DEC_APPLICATION_CURSOR_KEYS(),
+                                        m_modes_private.DEC_APPLICATION_KEYPAD(),
                                        &normal,
                                        &normal_length);
                        /* If we found something this way, suppress
@@ -4958,11 +4959,11 @@ VteTerminalPrivate::widget_key_press(GdkEventKey *event)
                         if (add_modifiers) {
                                 _vte_keymap_key_add_key_modifiers(keyval,
                                                                   m_modifiers,
-                                                                  m_cursor_mode == VTE_KEYMODE_APPLICATION,
+                                                                  
m_modes_private.DEC_APPLICATION_CURSOR_KEYS(),
                                                                   &normal,
                                                                   &normal_length);
                         }
-                       if (m_meta_sends_escape &&
+                       if (m_modes_private.XTERM_META_SENDS_ESCAPE() &&
                            !suppress_meta_esc &&
                            (normal_length > 0) &&
                            (m_modifiers & VTE_META_MASK)) {
@@ -5245,11 +5246,13 @@ VteTerminalPrivate::widget_paste_received(char const* text)
                         break;
                 }
         }
-        if (m_bracketed_paste_mode)
+
+        bool const bracketed_paste = m_modes_private.XTERM_READLINE_BRACKETED_PASTE();
+        if (bracketed_paste)
                 feed_child("\e[200~", -1);
         // FIXMEchpe add a way to avoid the extra string copy done here
         feed_child(paste, p - paste);
-        if (m_bracketed_paste_mode)
+        if (bracketed_paste)
                 feed_child("\e[201~", -1);
         g_free(paste);
 }
@@ -5296,7 +5299,7 @@ VteTerminalPrivate::feed_mouse_event(vte::grid::coords const& rowcol /* confined
 
        /* With the exception of the 1006 mode, button release is also encoded here. */
        /* Note that if multiple extensions are enabled, the 1006 is used, so it's okay to check for only 
that. */
-       if (is_release && !m_mouse_xterm_extension) {
+       if (is_release && !m_modes_private.XTERM_MOUSE_EXT_SGR()) {
                cb = 3;
        }
 
@@ -5317,10 +5320,10 @@ VteTerminalPrivate::feed_mouse_event(vte::grid::coords const& rowcol /* confined
        }
 
        /* Check the extensions in decreasing order of preference. Encoding the release event above assumes 
that 1006 comes first. */
-       if (m_mouse_xterm_extension) {
+       if (m_modes_private.XTERM_MOUSE_EXT_SGR()) {
                /* xterm's extended mode (1006) */
                len = g_snprintf(buf, sizeof(buf), _VTE_CAP_CSI "<%d;%ld;%ld%c", cb, cx, cy, is_release ? 'm' 
: 'M');
-       } else if (m_mouse_urxvt_extension) {
+       } else if (m_modes_private.URXVT_MOUSE_EXT()) {
                /* urxvt's extended mode (1015) */
                len = g_snprintf(buf, sizeof(buf), _VTE_CAP_CSI "%d;%ld;%ldM", 32 + cb, cx, cy);
        } else if (cx <= 231 && cy <= 231) {
@@ -5356,7 +5359,7 @@ VteTerminalPrivate::feed_focus_event_initial()
 void
 VteTerminalPrivate::maybe_feed_focus_event(bool in)
 {
-        if (m_focus_tracking_mode)
+        if (m_modes_private.XTERM_FOCUS())
                 feed_focus_event(in);
 }
 
@@ -8021,11 +8024,6 @@ VteTerminalPrivate::VteTerminalPrivate(VteTerminal *t) :
         m_last_graphic_character = 0;
 
         /* Set up the emulation. */
-       m_keypad_mode = VTE_KEYMODE_NORMAL;
-       m_cursor_mode = VTE_KEYMODE_NORMAL;
-        m_autowrap = TRUE;
-        m_sendrecv_mode = TRUE;
-       m_dec_saved = g_hash_table_new(NULL, NULL);
 
         if (vte_parser_new(&m_parser) != 0)
                 g_assert_not_reached();
@@ -8040,7 +8038,6 @@ VteTerminalPrivate::VteTerminalPrivate(VteTerminal *t) :
 
        /* Scrolling options. */
        m_scroll_on_keystroke = TRUE;
-       m_alternate_screen_scroll = TRUE;
         m_scrollback_lines = -1; /* force update in vte_terminal_set_scrollback_lines */
        set_scrollback_lines(VTE_SCROLLBACK_INIT);
 
@@ -8054,12 +8051,10 @@ VteTerminalPrivate::VteTerminalPrivate(VteTerminal *t) :
        /* Miscellaneous options. */
        set_backspace_binding(VTE_ERASE_AUTO);
        set_delete_binding(VTE_ERASE_AUTO);
-       m_meta_sends_escape = TRUE;
        m_audible_bell = TRUE;
         m_text_blink_mode = VTE_TEXT_BLINK_ALWAYS;
        m_allow_bold = TRUE;
         m_bold_is_bright = TRUE;
-        m_deccolm_mode = FALSE;
         m_rewrap_on_resize = TRUE;
        set_default_tabstops();
 
@@ -8070,7 +8065,6 @@ VteTerminalPrivate::VteTerminalPrivate(VteTerminal *t) :
         m_cursor_aspect_ratio = 0.04;
 
        /* Cursor blinking. */
-       m_cursor_visible = TRUE;
        m_cursor_blink_timeout = 500;
         m_cursor_blinks = FALSE;
         m_cursor_blink_mode = VTE_CURSOR_BLINK_SYSTEM;
@@ -8536,11 +8530,6 @@ VteTerminalPrivate::~VteTerminalPrivate()
                 g_object_unref(m_pty);
        }
 
-       /* Remove hash tables. */
-       if (m_dec_saved != NULL) {
-               g_hash_table_destroy(m_dec_saved);
-       }
-
        /* Clean up emulation structures. */
         m_parser = vte_parser_free(m_parser);
         g_assert_null(m_parser);
@@ -8705,7 +8694,7 @@ VteTerminalPrivate::determine_colors(VteCellAttr const* attr,
         vte_color_triple_get(attr->colors(), &fore, &back, &deco);
 
        /* Reverse-mode switches default fore and back colors */
-        if (G_UNLIKELY (m_reverse_mode)) {
+        if (G_UNLIKELY (m_modes_private.DEC_REVERSE_IMAGE())) {
                if (fore == VTE_DEFAULT_FG)
                        fore = VTE_DEFAULT_BG;
                if (back == VTE_DEFAULT_BG)
@@ -9656,12 +9645,19 @@ VteTerminalPrivate::paint_cursor()
        int x, y;
        gboolean blink, selected, focus;
 
-       if (!m_cursor_visible)
+        //FIXMEchpe this should already be reflected in the m_cursor_blink_state below
+       if (!m_modes_private.DEC_TEXT_CURSOR())
                return;
 
         if (m_im_preedit_active)
                 return;
 
+       focus = m_has_focus;
+       blink = m_cursor_blink_state;
+
+       if (focus && !blink)
+               return;
+
         col = m_screen->cursor.col;
         drow = m_screen->cursor.row;
        width = m_cell_width;
@@ -9671,12 +9667,6 @@ VteTerminalPrivate::paint_cursor()
        if (CLAMP(col, 0, m_column_count - 1) != col)
                return;
 
-       focus = m_has_focus;
-       blink = m_cursor_blink_state;
-
-       if (focus && !blink)
-               return;
-
         /* Find the first cell of the character "under" the cursor.
          * This is for CJK.  For TAB, paint the cursor where it really is. */
        auto cell = find_charcell(col, drow);
@@ -10107,7 +10097,7 @@ VteTerminalPrivate::widget_scroll(GdkEventScroll *event)
                        "Scroll speed is %d lines per non-smooth scroll unit\n",
                        (int) v);
        if (m_screen == &m_alternate_screen &&
-            m_alternate_screen_scroll) {
+            m_modes_private.XTERM_ALTBUF_SCROLL()) {
                char *normal;
                gssize normal_length;
 
@@ -10125,8 +10115,8 @@ VteTerminalPrivate::widget_scroll(GdkEventScroll *event)
                _vte_keymap_map (
                                cnt > 0 ? GDK_KEY_Down : GDK_KEY_Up,
                                m_modifiers,
-                               m_cursor_mode == VTE_KEYMODE_APPLICATION,
-                               m_keypad_mode == VTE_KEYMODE_APPLICATION,
+                                m_modes_private.DEC_APPLICATION_CURSOR_KEYS(),
+                                m_modes_private.DEC_APPLICATION_KEYPAD(),
                                &normal,
                                &normal_length);
                if (cnt < 0)
@@ -10491,20 +10481,13 @@ VteTerminalPrivate::reset(bool clear_tabstops,
         vte_parser_reset(m_parser);
         m_last_graphic_character = 0;
 
-       /* Reset keypad/cursor key modes. */
-       m_keypad_mode = VTE_KEYMODE_NORMAL;
-       m_cursor_mode = VTE_KEYMODE_NORMAL;
-        /* Enable autowrap. */
-        m_autowrap = TRUE;
-       /* Enable meta-sends-escape. */
-       m_meta_sends_escape = TRUE;
-        /* Disable DECCOLM mode. */
-        m_deccolm_mode = FALSE;
-       /* Reset saved settings. */
-       if (m_dec_saved != NULL) {
-               g_hash_table_destroy(m_dec_saved);
-               m_dec_saved = g_hash_table_new(NULL, NULL);
-       }
+        /* Reset modes */
+        m_modes_ecma.reset();
+        m_modes_private.clear_saved();
+        m_modes_private.reset();
+
+        update_mouse_protocol();
+
        /* Reset the color palette. Only the 256 indexed colors, not the special ones, as per xterm. */
        for (int i = 0; i < 256; i++)
                m_palette[i].sources[VTE_COLOR_SOURCE_ESCAPE].is_set = FALSE;
@@ -10542,13 +10525,6 @@ VteTerminalPrivate::reset(bool clear_tabstops,
        /* Reset restricted scrolling regions, leave insert mode, make
         * the cursor visible again. */
         m_scrolling_restricted = FALSE;
-        m_sendrecv_mode = TRUE;
-        m_insert_mode = FALSE;
-        m_origin_mode = FALSE;
-        m_reverse_mode = FALSE;
-       m_cursor_visible = TRUE;
-        /* For some reason, xterm doesn't reset alternateScroll, but we do. */
-        m_alternate_screen_scroll = TRUE;
         /* Reset the visual bits of selection on hard reset, see bug 789954. */
         if (clear_history) {
                 deselect_all();
@@ -10567,19 +10543,12 @@ VteTerminalPrivate::reset(bool clear_tabstops,
         }
 
        /* Reset mouse motion events. */
-       m_mouse_tracking_mode = MOUSE_TRACKING_NONE;
-        apply_mouse_cursor();
         m_mouse_pressed_buttons = 0;
         m_mouse_handled_buttons = 0;
-       m_mouse_xterm_extension = FALSE;
-       m_mouse_urxvt_extension = FALSE;
+       m_mouse_last_position = vte::view::coords(-1, -1);
        m_mouse_smooth_scroll_delta = 0.;
-        /* Reset focus tracking */
-        m_focus_tracking_mode = FALSE;
        /* Clear modifiers. */
        m_modifiers = 0;
-       /* Reset miscellaneous stuff. */
-       m_bracketed_paste_mode = FALSE;
         /* Reset the saved cursor. */
         save_cursor(&m_normal_screen);
         save_cursor(&m_alternate_screen);
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index d8bcd4e..bf38fbd 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -28,6 +28,7 @@
 #include "buffer.h"
 #include "parser.hh"
 #include "parser-glue.hh"
+#include "modes.hh"
 
 #include "vtepcre2.h"
 #include "vteregexinternal.hh"
@@ -71,13 +72,6 @@ typedef enum _VteCharacterReplacement {
         VTE_CHARACTER_REPLACEMENT_BRITISH
 } VteCharacterReplacement;
 
-/* The terminal's keypad/cursor state.  A terminal can either be using the
- * normal keypad, or the "application" keypad. */
-typedef enum _VteKeymode {
-       VTE_KEYMODE_NORMAL,
-       VTE_KEYMODE_APPLICATION
-} VteKeymode;
-
 typedef struct _VtePaletteColor {
        struct {
                vte::color::rgb color;
@@ -126,10 +120,9 @@ struct _VteScreen {
         /* Stuff saved along with the cursor */
         struct {
                 VteVisualPosition cursor;  /* onscreen coordinate, that is, relative to insert_delta */
-                gboolean reverse_mode;
-                gboolean origin_mode;
-                gboolean sendrecv_mode;
-                gboolean insert_mode;
+                uint8_t modes_ecma;
+                bool reverse_mode;
+                bool origin_mode;
                 VteCell defaults;
                 VteCell color_defaults;
                 VteCell fill_defaults;
@@ -277,10 +270,8 @@ public:
        /* Emulation setup data. */
         struct vte_parser* m_parser; /* control sequence state machine */
 
-        gboolean m_autowrap;              /* auto wraparound at right margin */
-        int m_keypad_mode, m_cursor_mode; /* these would be VteKeymodes, but we
-                                          need to guarantee its type */
-        GHashTable *m_dec_saved;
+        vte::terminal::modes::ECMA m_modes_ecma{};
+        vte::terminal::modes::Private m_modes_private{};
 
        /* PTY handling data. */
         VtePty *m_pty;
@@ -322,11 +313,6 @@ public:
         * screen, which seems to be a DEC-specific feature. */
         struct _VteScreen m_normal_screen, m_alternate_screen, *m_screen;
 
-        /* Values we save along with the cursor */
-        gboolean m_reverse_mode;  /* reverse mode */
-        gboolean m_origin_mode;   /* origin mode */
-        gboolean m_sendrecv_mode; /* sendrecv mode */
-        gboolean m_insert_mode;   /* insert mode */
         VteCell m_defaults;       /* default characteristics
                                      for insertion of any new
                                      characters */
@@ -369,22 +355,18 @@ public:
        /* Miscellaneous options. */
         VteEraseBinding m_backspace_binding;
         VteEraseBinding m_delete_binding;
-        gboolean m_meta_sends_escape;
         gboolean m_audible_bell;
         gboolean m_allow_bold;
         gboolean m_bold_is_bright;
-        gboolean m_deccolm_mode; /* DECCOLM allowed */
         GHashTable *m_tabstops;
         gboolean m_text_modified_flag;
         gboolean m_text_inserted_flag;
         gboolean m_text_deleted_flag;
         gboolean m_rewrap_on_resize;
-        gboolean m_bracketed_paste_mode;
 
        /* Scrolling options. */
         gboolean m_scroll_on_output;
         gboolean m_scroll_on_keystroke;
-        gboolean m_alternate_screen_scroll;
         vte::grid::row_t m_scrollback_lines;
 
         /* Restricted scrolling */
@@ -403,7 +385,6 @@ public:
         gint m_cursor_blink_timeout;        /* gtk-cursor-blink-timeout */
         gboolean m_cursor_blinks;           /* whether the cursor is actually blinking */
         gint64 m_cursor_blink_time;         /* how long the cursor has been blinking yet */
-        gboolean m_cursor_visible;
         gboolean m_has_focus;               /* is the terminal window focused */
 
         /* Contents blinking */
@@ -421,8 +402,7 @@ public:
         gboolean m_input_enabled;
         time_t m_last_keypress_time;
 
-        int m_mouse_tracking_mode; /* this is of type MouseTrackingMode,
-                                      but we need to guarantee its type. */
+        MouseTrackingMode m_mouse_tracking_mode{MOUSE_TRACKING_NONE};
         guint m_mouse_pressed_buttons;      /* bits 0, 1, 2 resp. for buttons 1, 2, 3 */
         guint m_mouse_handled_buttons;      /* similar bitmap for buttons we handled ourselves */
         /* The last known position the mouse pointer from an event. We don't store
@@ -431,11 +411,7 @@ public:
          */
         vte::view::coords m_mouse_last_position;
         guint m_mouse_autoscroll_tag;
-        gboolean m_mouse_xterm_extension;
-        gboolean m_mouse_urxvt_extension;
-        double m_mouse_smooth_scroll_delta;
-
-        gboolean m_focus_tracking_mode;
+        double m_mouse_smooth_scroll_delta{0.0};
 
        /* State variables for handling match checks. */
         char* m_match_contents;
@@ -790,7 +766,7 @@ public:
         void feed_chunks(struct _vte_incoming_chunk *chunks);
         void send_child(char const* data,
                         gssize length,
-                        bool local_echo);
+                        bool local_echo) noexcept;
         void feed_child_using_modes(char const* data,
                                     gssize length);
 
@@ -1171,23 +1147,20 @@ public:
         inline void switch_alternate_screen();
         inline void save_cursor();
         inline void restore_cursor();
-        inline void switch_normal_screen_and_restore_cursor();
-        inline void save_cursor_and_switch_alternate_screen();
         void set_title_internal(vte::parser::Params const& params,
                                 bool icon_title,
                                 bool window_title);
-        inline void set_mode(vte::parser::Params const& params,
-                             bool value);
-        inline void reset_mouse_smooth_scroll_delta();
-        inline void enter_focus_tracking_mode();
-        inline void decset(long setting,
-                           bool restore,
-                           bool save,
-                           bool set);
-        inline void decset(vte::parser::Params const& params,
-                           bool restore,
-                           bool save,
-                           bool set);
+
+        inline void set_mode_ecma(vte::parser::Sequence const& seq,
+                                  bool set) noexcept;
+        inline void set_mode_private(vte::parser::Sequence const& seq,
+                                     bool set) noexcept;
+        inline void set_mode_private(int mode,
+                                     bool set) noexcept;
+        inline void save_mode_private(vte::parser::Sequence const& seq,
+                                      bool save) noexcept;
+        void update_mouse_protocol() noexcept;
+
         inline void set_character_replacements(unsigned slot,
                                                VteCharacterReplacement replacement);
         inline void set_character_replacement(unsigned slot);
@@ -1222,7 +1195,6 @@ public:
                                  char const* terminator);
         inline void line_feed();
         inline void set_current_hyperlink(char* hyperlink_params /* adopted */, char* uri /* adopted */);
-        inline void set_keypad_mode(VteKeymode mode);
         inline void erase_in_display(vte::parser::Sequence const& seq);
         inline void erase_in_line(vte::parser::Sequence const& seq);
         inline void insert_lines(vte::grid::row_t param);
diff --git a/src/vteseq.cc b/src/vteseq.cc
index 2c9238c..692f560 100644
--- a/src/vteseq.cc
+++ b/src/vteseq.cc
@@ -410,22 +410,6 @@ VteTerminalPrivate::switch_alternate_screen()
         switch_screen(&m_alternate_screen);
 }
 
-/* Switch to normal screen and restore cursor (in this order). */
-void
-VteTerminalPrivate::switch_normal_screen_and_restore_cursor()
-{
-        switch_normal_screen();
-        restore_cursor();
-}
-
-/* Save cursor and switch to alternate screen (in this order). */
-void
-VteTerminalPrivate::save_cursor_and_switch_alternate_screen()
-{
-        save_cursor();
-        switch_alternate_screen();
-}
-
 /* Set icon/window titles. */
 void
 VteTerminalPrivate::set_title_internal(vte::parser::Params const& params,
@@ -471,398 +455,209 @@ VteTerminalPrivate::set_title_internal(vte::parser::Params const& params,
         g_free(title);
 }
 
-/* Toggle a terminal mode. */
 void
-VteTerminalPrivate::set_mode(vte::parser::Sequence const& params,
-                             bool value)
+VteTerminalPrivate::set_mode_ecma(vte::parser::Sequence const& seq,
+                                  bool set) noexcept
 {
-        auto n_params = params.size();
-        if (n_params == 0)
-                return;
+        auto const n_params = seq.size();
+        for (unsigned int i = 0; i < n_params; i = seq.next(i)) {
+                auto const param = seq.collect1(i);
+                auto const mode = m_modes_ecma.mode_from_param(param);
+
+                _vte_debug_print(VTE_DEBUG_MODES,
+                                 "Mode %d (%s) %s\n",
+                                 param, m_modes_ecma.mode_to_cstring(mode),
+                                 set ? "set" : "reset");
 
-       for (unsigned int i = 0; i < n_params; i++) {
-                int setting;
-                if (!params.number_at_unchecked(i, setting))
+                if (mode < 0)
                         continue;
 
-                switch (setting) {
-                case 2:                /* keyboard action mode (?) */
-                        break;
-                case 4:                /* insert/overtype mode */
-                        m_insert_mode = value;
-                        break;
-                case 12:       /* send/receive mode (local echo) */
-                        m_sendrecv_mode = value;
-                        break;
-                default:
-                        break;
-                }
+                m_modes_ecma.set(mode, set);
         }
 }
 
 void
-VteTerminalPrivate::reset_mouse_smooth_scroll_delta()
+VteTerminalPrivate::update_mouse_protocol() noexcept
 {
-       m_mouse_smooth_scroll_delta = 0.0;
-}
+        if (m_modes_private.XTERM_MOUSE_ANY_EVENT())
+                m_mouse_tracking_mode = MOUSE_TRACKING_ALL_MOTION_TRACKING;
+        else if (m_modes_private.XTERM_MOUSE_BUTTON_EVENT())
+                m_mouse_tracking_mode = MOUSE_TRACKING_CELL_MOTION_TRACKING;
+        else if (m_modes_private.XTERM_MOUSE_VT220_HIGHLIGHT())
+                m_mouse_tracking_mode = MOUSE_TRACKING_HILITE_TRACKING;
+        else if (m_modes_private.XTERM_MOUSE_VT220())
+                m_mouse_tracking_mode = MOUSE_TRACKING_SEND_XY_ON_BUTTON;
+        else if (m_modes_private.XTERM_MOUSE_X10())
+                m_mouse_tracking_mode = MOUSE_TRACKING_SEND_XY_ON_CLICK;
+        else
+                m_mouse_tracking_mode = MOUSE_TRACKING_NONE;
+
+        m_mouse_smooth_scroll_delta = 0.0;
 
-typedef void (VteTerminalPrivate::* decset_handler_t)();
+        /* Mouse pointer might change */
+        apply_mouse_cursor();
 
-struct decset_t {
-        gint16 setting;
-        /* offset in VteTerminalPrivate (> 0) or VteScreen (< 0) */
-        gint16 boffset;
-        gint16 ioffset;
-        gint16 poffset;
-        gint16 fvalue;
-        gint16 tvalue;
-        decset_handler_t reset, set;
-};
+        _vte_debug_print(VTE_DEBUG_MODES,
+                         "Mouse protocol is now %d\n", m_mouse_tracking_mode);
+}
 
-static int
-decset_cmp(const void *va,
-           const void *vb)
+void
+VteTerminalPrivate::set_mode_private(int mode,
+                                     bool set) noexcept
 {
-        const struct decset_t *a = (const struct decset_t *)va;
-        const struct decset_t *b = (const struct decset_t *)vb;
+        /* Pre actions */
+        switch (mode) {
+        default:
+                break;
+        }
+
+        m_modes_private.set(mode, set);
+
+        /* Post actions */
+        switch (mode) {
+        case vte::terminal::modes::Private::eDEC_132_COLUMN:
+                /* DECCOLM: set/reset to 132/80 columns mode, clear screen and cursor home */
+                // FIXMEchpe don't do clear screen if DECNCSM is set
+                if (m_modes_private.XTERM_DECCOLM()) {
+                        emit_resize_window(set ? 132 : 80, m_row_count);
+                        clear_screen();
+                        home_cursor();
+                }
+                break;
+
+        case vte::terminal::modes::Private::eDEC_REVERSE_IMAGE:
+                invalidate_all();
+                break;
+
+        case vte::terminal::modes::Private::eDEC_ORIGIN:
+                /* Reposition the cursor in its new home position. */
+                home_cursor();
+                break;
+
+        case vte::terminal::modes::Private::eDEC_TEXT_CURSOR:
+                /* No need to invalidate the cursor here, this is done
+                 * in process_incoming().
+                 */
+                break;
+
+        case vte::terminal::modes::Private::eXTERM_ALTBUF:
+                /* [[fallthrough]]; */
+        case vte::terminal::modes::Private::eXTERM_OPT_ALTBUF:
+                /* [[fallthrough]]; */
+        case vte::terminal::modes::Private::eXTERM_OPT_ALTBUF_SAVE_CURSOR:
+                if (set) {
+                        if (mode == vte::terminal::modes::Private::eXTERM_OPT_ALTBUF_SAVE_CURSOR)
+                                save_cursor();
+
+                        switch_alternate_screen();
+
+                        /* Clear the alternate screen */
+                        if (mode == vte::terminal::modes::Private::eXTERM_OPT_ALTBUF_SAVE_CURSOR)
+                                clear_screen();
+                } else {
+                        if (mode == vte::terminal::modes::Private::eXTERM_OPT_ALTBUF &&
+                            m_screen == &m_alternate_screen)
+                                clear_screen();
+
+                        switch_normal_screen();
 
-        return a->setting < b->setting ? -1 : a->setting > b->setting;
+                        if (mode == vte::terminal::modes::Private::eXTERM_OPT_ALTBUF_SAVE_CURSOR)
+                                restore_cursor();
+                }
+
+                /* Reset scrollbars and repaint everything. */
+                gtk_adjustment_set_value(m_vadjustment,
+                                         m_screen->scroll_delta);
+                set_scrollback_lines(m_scrollback_lines);
+                queue_contents_changed();
+                invalidate_all();
+                break;
+
+        case vte::terminal::modes::Private::eXTERM_SAVE_CURSOR:
+                if (set)
+                        save_cursor();
+                else
+                        restore_cursor();
+                break;
+
+        case vte::terminal::modes::Private::eXTERM_MOUSE_X10:
+        case vte::terminal::modes::Private::eXTERM_MOUSE_VT220:
+        case vte::terminal::modes::Private::eXTERM_MOUSE_VT220_HIGHLIGHT:
+        case vte::terminal::modes::Private::eXTERM_MOUSE_BUTTON_EVENT:
+        case vte::terminal::modes::Private::eXTERM_MOUSE_ANY_EVENT:
+        case vte::terminal::modes::Private::eXTERM_MOUSE_EXT:
+        case vte::terminal::modes::Private::eXTERM_MOUSE_EXT_SGR:
+        case vte::terminal::modes::Private::eURXVT_MOUSE_EXT:
+                update_mouse_protocol();
+                break;
+
+        case vte::terminal::modes::Private::eXTERM_FOCUS:
+                if (set)
+                        feed_focus_event_initial();
+                break;
+
+        default:
+                break;
+        }
 }
 
-/* Manipulate certain terminal attributes. */
 void
-VteTerminalPrivate::decset(vte::parser::Sequence const& params,
-                           bool restore,
-                           bool save,
-                           bool set)
+VteTerminalPrivate::set_mode_private(vte::parser::Sequence const& seq,
+                                     bool set) noexcept
 {
+        auto const n_params = seq.size();
+        for (unsigned int i = 0; i < n_params; i = seq.next(i)) {
+                auto const param = seq.collect1(i);
+                auto const mode = m_modes_private.mode_from_param(param);
 
-        auto n_params = params.size();
-        for (unsigned int i = 0; i < n_params; i++) {
-                int setting;
+                _vte_debug_print(VTE_DEBUG_MODES,
+                                 "Private mode %d (%s) %s\n",
+                                 param, m_modes_private.mode_to_cstring(mode),
+                                 set ? "set" : "reset");
 
-                if (!params.number_at(i, setting))
+                if (mode < 0)
                         continue;
 
-               decset(setting, restore, save, set);
-       }
+                set_mode_private(mode, set);
+        }
 }
 
 void
-VteTerminalPrivate::decset(long setting,
-                           bool restore,
-                           bool save,
-                           bool set)
-{
-       static const struct decset_t settings[] = {
-#define PRIV_OFFSET(member) (G_STRUCT_OFFSET(VteTerminalPrivate, member))
-#define SCREEN_OFFSET(member) (-G_STRUCT_OFFSET(VteScreen, member))
-               /* 1: Application/normal cursor keys. */
-               {1, 0, PRIV_OFFSET(m_cursor_mode), 0,
-                VTE_KEYMODE_NORMAL,
-                VTE_KEYMODE_APPLICATION,
-                nullptr, nullptr,},
-               /* 2: disallowed, we don't do VT52. */
-               {2, 0, 0, 0, 0, 0, nullptr, nullptr,},
-                /* 3: DECCOLM set/reset to and from 132/80 columns */
-                {3, 0, 0, 0,
-                 FALSE,
-                 TRUE,
-                 nullptr, nullptr,},
-               /* 5: Reverse video. */
-                {5, PRIV_OFFSET(m_reverse_mode), 0, 0,
-                FALSE,
-                TRUE,
-                nullptr, nullptr,},
-               /* 6: Origin mode: when enabled, cursor positioning is
-                * relative to the scrolling region. */
-                {6, PRIV_OFFSET(m_origin_mode), 0, 0,
-                FALSE,
-                TRUE,
-                nullptr, nullptr,},
-               /* 7: Wraparound mode. */
-                {7, PRIV_OFFSET(m_autowrap), 0, 0,
-                FALSE,
-                TRUE,
-                nullptr, nullptr,},
-               /* 8: disallowed, keyboard repeat is set by user. */
-               {8, 0, 0, 0, 0, 0, nullptr, nullptr,},
-               /* 9: Send-coords-on-click. */
-               {9, 0, PRIV_OFFSET(m_mouse_tracking_mode), 0,
-                0,
-                MOUSE_TRACKING_SEND_XY_ON_CLICK,
-                 &VteTerminalPrivate::reset_mouse_smooth_scroll_delta,
-                 &VteTerminalPrivate::reset_mouse_smooth_scroll_delta,},
-               /* 12: disallowed, cursor blinks is set by user. */
-               {12, 0, 0, 0, 0, 0, nullptr, nullptr,},
-               /* 18: print form feed. */
-               /* 19: set print extent to full screen. */
-               /* 25: Cursor visible. */
-               {25, PRIV_OFFSET(m_cursor_visible), 0, 0,
-                FALSE,
-                TRUE,
-                nullptr, nullptr,},
-               /* 30/rxvt: disallowed, scrollbar visibility is set by user. */
-               {30, 0, 0, 0, 0, 0, nullptr, nullptr,},
-               /* 35/rxvt: disallowed, fonts set by user. */
-               {35, 0, 0, 0, 0, 0, nullptr, nullptr,},
-               /* 38: enter Tektronix mode. */
-                /* 40: Enable DECCOLM mode. */
-                {40, PRIV_OFFSET(m_deccolm_mode), 0, 0,
-                 FALSE,
-                 TRUE,
-                 nullptr, nullptr,},
-               /* 41: more(1) fix. */
-               /* 42: Enable NLS replacements. */
-               /* 44: Margin bell. */
-               /* 47: Alternate screen. */
-                {47, 0, 0, 0,
-                 0,
-                 0,
-                 &VteTerminalPrivate::switch_normal_screen,
-                 &VteTerminalPrivate::switch_alternate_screen,},
-               /* 66: Keypad mode. */
-               {66, PRIV_OFFSET(m_keypad_mode), 0, 0,
-                VTE_KEYMODE_NORMAL,
-                VTE_KEYMODE_APPLICATION,
-                nullptr, nullptr,},
-               /* 67: disallowed, backspace key policy is set by user. */
-               {67, 0, 0, 0, 0, 0, nullptr, nullptr,},
-               /* 1000: Send-coords-on-button. */
-               {1000, 0, PRIV_OFFSET(m_mouse_tracking_mode), 0,
-                0,
-                MOUSE_TRACKING_SEND_XY_ON_BUTTON,
-                 &VteTerminalPrivate::reset_mouse_smooth_scroll_delta,
-                 &VteTerminalPrivate::reset_mouse_smooth_scroll_delta,},
-               /* 1001: Hilite tracking. */
-               {1001, 0, PRIV_OFFSET(m_mouse_tracking_mode), 0,
-                (0),
-                (MOUSE_TRACKING_HILITE_TRACKING),
-                 &VteTerminalPrivate::reset_mouse_smooth_scroll_delta,
-                 &VteTerminalPrivate::reset_mouse_smooth_scroll_delta,},
-               /* 1002: Cell motion tracking. */
-               {1002, 0, PRIV_OFFSET(m_mouse_tracking_mode), 0,
-                (0),
-                (MOUSE_TRACKING_CELL_MOTION_TRACKING),
-                 &VteTerminalPrivate::reset_mouse_smooth_scroll_delta,
-                 &VteTerminalPrivate::reset_mouse_smooth_scroll_delta,},
-               /* 1003: All motion tracking. */
-               {1003, 0, PRIV_OFFSET(m_mouse_tracking_mode), 0,
-                (0),
-                (MOUSE_TRACKING_ALL_MOTION_TRACKING),
-                 &VteTerminalPrivate::reset_mouse_smooth_scroll_delta,
-                 &VteTerminalPrivate::reset_mouse_smooth_scroll_delta,},
-               /* 1004: Focus tracking. */
-               {1004, PRIV_OFFSET(m_focus_tracking_mode), 0, 0,
-                FALSE,
-                TRUE,
-                 nullptr,
-                 &VteTerminalPrivate::feed_focus_event_initial,},
-               /* 1006: Extended mouse coordinates. */
-               {1006, PRIV_OFFSET(m_mouse_xterm_extension), 0, 0,
-                FALSE,
-                TRUE,
-                nullptr, nullptr,},
-               /* 1007: Alternate screen scroll. */
-               {1007, PRIV_OFFSET(m_alternate_screen_scroll), 0, 0,
-                FALSE,
-                TRUE,
-                nullptr, nullptr,},
-               /* 1010/rxvt: disallowed, scroll-on-output is set by user. */
-               {1010, 0, 0, 0, 0, 0, nullptr, nullptr,},
-               /* 1011/rxvt: disallowed, scroll-on-keypress is set by user. */
-               {1011, 0, 0, 0, 0, 0, nullptr, nullptr,},
-               /* 1015/urxvt: Extended mouse coordinates. */
-               {1015, PRIV_OFFSET(m_mouse_urxvt_extension), 0, 0,
-                FALSE,
-                TRUE,
-                 nullptr, nullptr,},
-               /* 1035: disallowed, don't know what to do with it. */
-               {1035, 0, 0, 0, 0, 0, nullptr, nullptr,},
-               /* 1036: Meta-sends-escape. */
-               {1036, PRIV_OFFSET(m_meta_sends_escape), 0, 0,
-                FALSE,
-                TRUE,
-                nullptr, nullptr,},
-               /* 1037: disallowed, delete key policy is set by user. */
-               {1037, 0, 0, 0, 0, 0, nullptr, nullptr,},
-               /* 1047: Use alternate screen buffer. */
-                {1047, 0, 0, 0,
-                 0,
-                 0,
-                 &VteTerminalPrivate::switch_normal_screen,
-                 &VteTerminalPrivate::switch_alternate_screen,},
-               /* 1048: Save/restore cursor position. */
-               {1048, 0, 0, 0,
-                0,
-                0,
-                 &VteTerminalPrivate::restore_cursor,
-                 &VteTerminalPrivate::save_cursor,},
-               /* 1049: Use alternate screen buffer, saving the cursor
-                * position. */
-                {1049, 0, 0, 0,
-                 0,
-                 0,
-                 &VteTerminalPrivate::switch_normal_screen_and_restore_cursor,
-                 &VteTerminalPrivate::save_cursor_and_switch_alternate_screen,},
-               /* 2004: Bracketed paste mode. */
-               {2004, PRIV_OFFSET(m_bracketed_paste_mode), 0, 0,
-                FALSE,
-                TRUE,
-                nullptr, nullptr,},
-#undef PRIV_OFFSET
-#undef SCREEN_OFFSET
-       };
-        struct decset_t key;
-        struct decset_t *found;
-
-       /* Handle the setting. */
-        key.setting = setting;
-        found = (struct decset_t *)bsearch(&key, settings, G_N_ELEMENTS(settings), sizeof(settings[0]), 
decset_cmp);
-        if (!found) {
-               _vte_debug_print (VTE_DEBUG_MISC,
-                                 "DECSET/DECRESET mode %ld not recognized, ignoring.\n",
-                                 setting);
-                return;
-       }
-
-        key = *found;
-        do {
-                gboolean *bvalue = NULL;
-                gint *ivalue = NULL;
-                gpointer *pvalue = NULL, pfvalue = NULL, ptvalue = NULL;
-                gpointer p;
-
-               /* Handle settings we want to ignore. */
-               if ((key.fvalue == key.tvalue) &&
-                   (!key.set) &&
-                   (!key.reset)) {
-                       break;
-               }
+VteTerminalPrivate::save_mode_private(vte::parser::Sequence const& seq,
+                                      bool save) noexcept
+{
+        auto const n_params = seq.size();
+        for (unsigned int i = 0; i < n_params; i = seq.next(i)) {
+                auto const param = seq.collect1(i);
+                auto const mode = m_modes_private.mode_from_param(param);
 
-#define STRUCT_MEMBER_P(type,total_offset) \
-                (type) (total_offset >= 0 ? G_STRUCT_MEMBER_P(this, total_offset) : 
G_STRUCT_MEMBER_P(m_screen, -total_offset))
-
-                if (key.boffset) {
-                        bvalue = STRUCT_MEMBER_P(gboolean*, key.boffset);
-                } else if (key.ioffset) {
-                        ivalue = STRUCT_MEMBER_P(int*, key.ioffset);
-                } else if (key.poffset) {
-                        pvalue = STRUCT_MEMBER_P(gpointer*, key.poffset);
-                        pfvalue = STRUCT_MEMBER_P(gpointer, key.fvalue);
-                        ptvalue = STRUCT_MEMBER_P(gpointer, key.tvalue);
+                if (mode < 0) {
+                        _vte_debug_print(VTE_DEBUG_MODES,
+                                         "Saving private mode %d (%s)\n",
+                                         param, m_modes_private.mode_to_cstring(mode));
+                        continue;
                 }
-#undef STRUCT_MEMBER_P
-
-               /* Read the old setting. */
-               if (restore) {
-                       p = g_hash_table_lookup(m_dec_saved,
-                                               GINT_TO_POINTER(setting));
-                       set = (p != NULL);
-                       _vte_debug_print(VTE_DEBUG_PARSER,
-                                       "Setting %ld was %s.\n",
-                                       setting, set ? "set" : "unset");
-               }
-               /* Save the current setting. */
-               if (save) {
-                       if (bvalue) {
-                               set = *(bvalue) != FALSE;
-                       } else
-                       if (ivalue) {
-                                set = *(ivalue) == (int)key.tvalue;
-                       } else
-                       if (pvalue) {
-                               set = *(pvalue) == ptvalue;
-                       }
-                       _vte_debug_print(VTE_DEBUG_PARSER,
-                                       "Setting %ld is %s, saving.\n",
-                                       setting, set ? "set" : "unset");
-                       g_hash_table_insert(m_dec_saved,
-                                           GINT_TO_POINTER(setting),
-                                           GINT_TO_POINTER(set));
-               }
-               /* Change the current setting to match the new/saved value. */
-               if (!save) {
-                       _vte_debug_print(VTE_DEBUG_PARSER,
-                                       "Setting %ld to %s.\n",
-                                       setting, set ? "set" : "unset");
-                       if (key.set && set) {
-                               (this->*key.set)();
-                       }
-                       if (bvalue) {
-                               *(bvalue) = set;
-                       } else
-                       if (ivalue) {
-                                *(ivalue) = set ? (int)key.tvalue : (int)key.fvalue;
-                       } else
-                       if (pvalue) {
-                                *(pvalue) = set ? ptvalue : pfvalue;
-                       }
-                       if (key.reset && !set) {
-                               (this->*key.reset)();
-                       }
-               }
-       } while (0);
 
-       /* Do whatever's necessary when the setting changes. */
-       switch (setting) {
-       case 1:
-               _vte_debug_print(VTE_DEBUG_KEYBOARD, set ?
-                               "Entering application cursor mode.\n" :
-                               "Leaving application cursor mode.\n");
-               break;
-       case 3:
-                /* 3: DECCOLM set/reset to 132/80 columns mode, clear screen and cursor home */
-                if (m_deccolm_mode) {
-                        emit_resize_window(set ? 132 : 80,
-                                           m_row_count);
-                        clear_screen();
-                        home_cursor();
+                if (save) {
+                        _vte_debug_print(VTE_DEBUG_MODES,
+                                         "Saving private mode %d (%s) is %s\n",
+                                         param, m_modes_private.mode_to_cstring(mode),
+                                         m_modes_private.get(mode) ? "set" : "reset");
+
+                        m_modes_private.push_saved(mode);
+                } else {
+                        bool const set = m_modes_private.pop_saved(mode);
+
+                        _vte_debug_print(VTE_DEBUG_MODES,
+                                         "Restoring private mode %d (%s) to %s\n",
+                                         param, m_modes_private.mode_to_cstring(mode),
+                                         set ? "set" : "reset");
+
+                        set_mode_private(mode, set);
                 }
-               break;
-       case 5:
-               /* Repaint everything in reverse mode. */
-                invalidate_all();
-               break;
-       case 6:
-               /* Reposition the cursor in its new home position. */
-                home_cursor();
-               break;
-       case 47:
-       case 1047:
-       case 1049:
-                /* Clear the alternate screen if we're switching to it */
-               if (set) {
-                       clear_screen();
-               }
-               /* Reset scrollbars and repaint everything. */
-               gtk_adjustment_set_value(m_vadjustment,
-                                        m_screen->scroll_delta);
-               set_scrollback_lines(m_scrollback_lines);
-                queue_contents_changed();
-                invalidate_all();
-               break;
-       case 9:
-       case 1000:
-       case 1001:
-       case 1002:
-       case 1003:
-                /* Mouse pointer might change. */
-                apply_mouse_cursor();
-               break;
-       case 66:
-               _vte_debug_print(VTE_DEBUG_KEYBOARD, set ?
-                               "Entering application keypad mode.\n" :
-                               "Leaving application keypad mode.\n");
-               break;
-       default:
-               break;
-       }
+        }
 }
 
-/* THE HANDLERS */
-
-/* Do nothing. */
 void
 VteTerminalPrivate::set_character_replacement(unsigned slot)
 {
@@ -1024,7 +819,7 @@ void
 VteTerminalPrivate::set_cursor_row(vte::grid::row_t row)
 {
         vte::grid::row_t start_row, end_row;
-        if (m_origin_mode &&
+        if (m_modes_private.DEC_ORIGIN() &&
             m_scrolling_restricted) {
                 start_row = m_scrolling_region.start;
                 end_row = m_scrolling_region.end;
@@ -1054,7 +849,7 @@ vte::grid::row_t
 VteTerminalPrivate::get_cursor_row() const
 {
         auto row = m_screen->cursor.row - m_screen->insert_delta;
-        /* Note that we do NOT check m_origin_mode here! */
+        /* Note that we do NOT check DEC_ORIGIN mode here! */
         if (m_scrolling_restricted) {
                 row -= m_scrolling_region.start;
         }
@@ -1140,7 +935,7 @@ VteTerminalPrivate::move_cursor_down(vte::grid::row_t rows)
         ensure_cursor_is_onscreen();
 
         vte::grid::row_t end;
-        // FIXMEchpe why not check m_origin_mode here?
+        // FIXMEchpe why not check DEC_ORIGIN here?
         if (m_scrolling_restricted) {
                 end = m_screen->insert_delta + m_scrolling_region.end;
        } else {
@@ -1398,7 +1193,7 @@ VteTerminalPrivate::move_cursor_up(vte::grid::row_t rows)
         ensure_cursor_is_onscreen();
 
         vte::grid::row_t start;
-        //FIXMEchpe why not check m_origin_mode here?
+        //FIXMEchpe why not check DEC_ORIGIN mode here?
         if (m_scrolling_restricted) {
                 start = m_screen->insert_delta + m_scrolling_region.start;
        } else {
@@ -1666,12 +1461,6 @@ VteTerminalPrivate::set_current_hyperlink(char *hyperlink_params /* adopted */,
 }
 
 void
-VteTerminalPrivate::set_keypad_mode(VteKeymode mode)
-{
-        m_keypad_mode = mode;
-}
-
-void
 VteTerminalPrivate::erase_in_display(vte::parser::Sequence const& seq)
 {
         /* We don't implement the protected attribute, so we can ignore selective:
@@ -2946,11 +2735,8 @@ VteTerminalPrivate::DECKPAM(vte::parser::Sequence const& seq)
          * top-row or on the keypad.
          * Default is keypad-numeric-mode.
          */
-#if 0
-        screen->flags |= VTE_FLAG_KEYPAD_MODE;
-#endif
 
-        set_keypad_mode(VTE_KEYMODE_APPLICATION);
+        set_mode_private(vte::terminal::modes::Private::eDEC_APPLICATION_KEYPAD, true);
 }
 
 void
@@ -2963,11 +2749,7 @@ VteTerminalPrivate::DECKPNM(vte::parser::Sequence const& seq)
          * sequences as corresponding keypresses on the main keyboard.
          * Default is keypad-numeric-mode.
          */
-#if 0
-        screen->flags &= ~VTE_FLAG_KEYPAD_MODE;
-#endif
-
-       set_keypad_mode(VTE_KEYMODE_NORMAL);
+        set_mode_private(vte::terminal::modes::Private::eDEC_APPLICATION_KEYPAD, false);
 }
 
 void
@@ -3948,7 +3730,7 @@ VteTerminalPrivate::DSR_ECMA(vte::parser::Sequence const& seq)
 
                 /* Send the cursor position. */
                 vte::grid::row_t rowval, origin, rowmax;
-                if (m_origin_mode &&
+                if (m_modes_private.DEC_ORIGIN() &&
                     m_scrolling_restricted) {
                         origin = m_scrolling_region.start;
                         rowmax = m_scrolling_region.end;
@@ -3991,7 +3773,7 @@ VteTerminalPrivate::DSR_DEC(vte::parser::Sequence const& seq)
         case 6:
                 /* Send the cursor position. */
                 vte::grid::row_t rowval, origin, rowmax;
-                if (m_origin_mode &&
+                if (m_modes_private.DEC_ORIGIN() &&
                     m_scrolling_restricted) {
                         origin = m_scrolling_region.start;
                         rowmax = m_scrolling_region.end;
@@ -4733,14 +4515,8 @@ VteTerminalPrivate::RM_ECMA(vte::parser::Sequence const& seq)
          *
          * References: ECMA-48 § 8.3.106
          */
-#if 0
-        unsigned int i;
-
-        for (i = 0; i < seq->n_args; ++i)
-                screen_mode_change_ansi(screen, seq->args[i], false);
-#endif
 
-        set_mode(seq, false);
+        set_mode_ecma(seq, false);
 }
 
 void
@@ -4754,14 +4530,8 @@ VteTerminalPrivate::RM_DEC(vte::parser::Sequence const& seq)
          *
          * References: VT525
          */
-#if 0
-        unsigned int i;
-
-        for (i = 0; i < seq->n_args; ++i)
-                screen_mode_change_dec(screen, seq->args[i], false);
-#endif
 
-        decset(seq, false, false, false);
+        set_mode_private(seq, false);
 }
 
 void
@@ -4954,14 +4724,8 @@ VteTerminalPrivate::SM_ECMA(vte::parser::Sequence const& seq)
          *
          * References: ECMA-48 § 8.3.125
          */
-#if 0
-        unsigned int i;
-
-        for (i = 0; i < seq->n_args; ++i)
-                screen_mode_change_ansi(screen, seq->args[i], true);
-#endif
 
-        set_mode(seq, true);
+        set_mode_ecma(seq, true);
 }
 
 void
@@ -4975,14 +4739,8 @@ VteTerminalPrivate::SM_DEC(vte::parser::Sequence const& seq)
          *
          * References: VT525
          */
-#if 0
-        unsigned int i;
-
-        for (i = 0; i < seq->n_args; ++i)
-                screen_mode_change_dec(screen, seq->args[i], true);
-#endif
 
-        decset(seq, false, false, true);
+        set_mode_private(seq, true);
 }
 
 void
@@ -5267,7 +5025,7 @@ VteTerminalPrivate::XTERM_RPM(vte::parser::Sequence const& seq)
          * References: XTERM
          */
 
-        decset(seq, true, false, false);
+        save_mode_private(seq, false);
 }
 
 void
@@ -5311,7 +5069,7 @@ VteTerminalPrivate::XTERM_SPM(vte::parser::Sequence const& seq)
          * References: XTERM
          */
 
-        decset(seq, false, true, false);
+        save_mode_private(seq, true);
 }
 
 void


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]