commit 0d564d06a5ff4381cb14c5e20c488b22c4b98add
Author: Julius Huelsmann <[email protected]>
Date:   Wed Feb 12 20:34:11 2020 +0100

    [st][patch][vim_browse]: port patch to master HEAD.

diff --git a/st.suckless.org/patches/vim_browse/index.md 
b/st.suckless.org/patches/vim_browse/index.md
index bf1cb9a0..08010f44 100644
--- a/st.suckless.org/patches/vim_browse/index.md
+++ b/st.suckless.org/patches/vim_browse/index.md
@@ -94,15 +94,29 @@ Bugs
 Download
 --------
 
-**All versions (from old to new) **:
-Based on commmit `2b8333f` of `st`.
+The current version of the `vim` patch is developped on the commit `2b8333f` 
of st, ports of the
+most recent version of the patch to other commits of `st` are listed below.
+
+### Patch based on `2b8333f` (10-2019)
+
+**All versions based on `2b8333f` (from old to new)**:
 * [st-vimBrowse-20191107-2b8333f.diff 
(attached)](st-vimBrowse-20191107-2b8333f.diff)
 * [st-vimBrowse-20191203-2b8333f.diff 
(attached)](st-vimBrowse-20191203-2b8333f.diff)
 * [st-vimBrowse-20200212-2b8333f.diff 
(Github)](https://github.com/juliusHuelsmann/st/releases/download/patchesV1/st-vimBrowse-20200212-2b8333f.diff)
 
-**Most Recent**:
+**Most Recent based on `2b8333f`**:
 * [st-vimBrowse-20200212-2b8333f.diff 
(Github)](https://github.com/juliusHuelsmann/st/releases/download/patchesV1/st-vimBrowse-20200212-2b8333f.diff)
 
+### Patch based on `26cdfeb` (02-2020)
+While porting to the current version is straight-forward, one vital change is 
overridden when
+operating e.g. with the current head of master (`26cdfeb`, 02-2020). 
+
+**All versions based on `26cdfeb` (from old to new) **:
+* [st-vimBrowse-20200212-26cdfeb.diff 
(attached)](st-vimBrowse-20200212-26cdfeb.diff)
+* [st-vimBrowse-20200212-26cdfeb.diff 
(Github)](https://github.com/juliusHuelsmann/st/releases/download/patchesV1/st-vimBrowse-20200212-26cdfeb.diff)
+
+**Most Recent based on `26cdfeb` **:
+* [st-vimBrowse-20200212-26cdfeb.diff 
(Github)](https://github.com/juliusHuelsmann/st/releases/download/patchesV1/st-vimBrowse-20200212-26cdfeb.diff)
 
 Authors of the [Scrollback patch](https://st.suckless.org/patches/scrollback/)
 ------------------------------------------------------------------------------
diff --git 
a/st.suckless.org/patches/vim_browse/st-vimBrowse-20200212-26cdfeb.diff 
b/st.suckless.org/patches/vim_browse/st-vimBrowse-20200212-26cdfeb.diff
new file mode 100644
index 00000000..44e0f740
--- /dev/null
+++ b/st.suckless.org/patches/vim_browse/st-vimBrowse-20200212-26cdfeb.diff
@@ -0,0 +1,2615 @@
+From 22471790fcaf1505890bcb53752a6df87cfd3592 Mon Sep 17 00:00:00 2001
+From: Julius Huelsmann <[email protected]>
+Date: Wed, 12 Feb 2020 19:50:06 +0100
+Subject: [PATCH] port: vim patch 2020-02 to current HEAD of st
+
+---
+ Makefile       |   9 +-
+ config.def.h   |  54 ++++
+ config.h       | 513 +++++++++++++++++++++++++++++++++
+ dynamicArray.h | 175 ++++++++++++
+ error.h        |  47 ++++
+ glyph.h        |  30 ++
+ normalMode.c   | 750 +++++++++++++++++++++++++++++++++++++++++++++++++
+ normalMode.h   |  36 +++
+ st.c           | 249 +++++++++-------
+ st.h           |  36 ++-
+ term.h         |  73 +++++
+ win.h          |   2 +
+ x.c            |  48 +++-
+ 13 files changed, 1894 insertions(+), 128 deletions(-)
+ create mode 100644 config.h
+ create mode 100644 dynamicArray.h
+ create mode 100644 error.h
+ create mode 100644 glyph.h
+ create mode 100644 normalMode.c
+ create mode 100644 normalMode.h
+ create mode 100644 term.h
+
+diff --git a/Makefile b/Makefile
+index 470ac86..af8f0be 100644
+--- a/Makefile
++++ b/Makefile
+@@ -4,7 +4,7 @@
+ 
+ include config.mk
+ 
+-SRC = st.c x.c
++SRC = st.c x.c normalMode.c
+ OBJ = $(SRC:.c=.o)
+ 
+ all: options st
+@@ -21,8 +21,8 @@ config.h:
+ .c.o:
+       $(CC) $(STCFLAGS) -c $<
+ 
+-st.o: config.h st.h win.h
+-x.o: arg.h config.h st.h win.h
++st.o: config.h st.h win.h dynamicArray.h normalMode.h term.h glyph.h error.h
++x.o: arg.h config.h st.h win.h dynamicArray.h normalMode.h term.h glyph.h 
error.h
+ 
+ $(OBJ): config.h config.mk
+ 
+@@ -35,7 +35,8 @@ clean:
+ dist: clean
+       mkdir -p st-$(VERSION)
+       cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\
+-              config.def.h st.info st.1 arg.h st.h win.h $(SRC)\
++              config.def.h st.info st.1 arg.h st.h win.h dynamicArray.h\
++              normalMode.h term.h error.h $(SRC)\
+               st-$(VERSION)
+       tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz
+       rm -rf st-$(VERSION)
+diff --git a/config.def.h b/config.def.h
+index 546edda..92b541d 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -149,6 +149,14 @@ static unsigned int mousebg = 0;
+  * doesn't match the ones requested.
+  */
+ static unsigned int defaultattr = 11;
++/// Colors for the entities that are 'highlighted' in normal mode (search
++/// results currently on screen) [Vim Browse].
++static unsigned int highlightBg = 160;
++static unsigned int highlightFg = 15;
++/// Colors for highlighting the current cursor position (row + col) in normal
++/// mode [Vim Browse].
++static unsigned int currentBg = 8;
++static unsigned int currentFg = 15;
+ 
+ /*
+  * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set).
+@@ -170,10 +178,12 @@ static MouseShortcut mshortcuts[] = {
+ 
+ /* Internal keyboard shortcuts. */
+ #define MODKEY Mod1Mask
++#define AltMask Mod1Mask
+ #define TERMMOD (ControlMask|ShiftMask)
+ 
+ static Shortcut shortcuts[] = {
+       /* mask                 keysym          function        argument */
++      { AltMask,              XK_c,           normalMode,     {.i =  0} },
+       { XK_ANY_MOD,           XK_Break,       sendbreak,      {.i =  0} },
+       { ControlMask,          XK_Print,       toggleprinter,  {.i =  0} },
+       { ShiftMask,            XK_Print,       printscreen,    {.i =  0} },
+@@ -186,6 +196,8 @@ static Shortcut shortcuts[] = {
+       { TERMMOD,              XK_Y,           selpaste,       {.i =  0} },
+       { ShiftMask,            XK_Insert,      selpaste,       {.i =  0} },
+       { TERMMOD,              XK_Num_Lock,    numlock,        {.i =  0} },
++      { ShiftMask,            XK_Page_Up,     kscrollup,      {.i = -1} },
++      { ShiftMask,            XK_Page_Down,   kscrolldown,    {.i = -1} },
+ };
+ 
+ /*
+@@ -457,3 +469,45 @@ static char ascii_printable[] =
+       " !\"#$%&'()*+,-./0123456789:;<=>?"
+       "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_"
+       "`abcdefghijklmnopqrstuvwxyz{|}~";
++
++
++/// word sepearors normal mode
++/// [Vim Browse].
++char wordDelimSmall[] = "     !\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~";
++char wordDelimLarge[] = "     "; /// <Word sepearors normal mode (capital W)
++
++/// Shortcusts executed in normal mode (which should not already be in use)
++/// [Vim Browse].
++struct NormalModeShortcuts normalModeShortcuts [] = {
++      { 'R', "?Building
" },
++      { 'r', "/Building
" },
++      { 'F', "?: error:
" },
++      { 'f', "/: error:
" },
++      { 'Q', "?[Leaving vim, starting execution]
" },
++      { 'S', "Qf" },
++      { 'X', "?juli@machine
" },
++      { 'x', "/juli@machine
" },
++};
++
++size_t const amountNormalModeShortcuts = sizeof(normalModeShortcuts) / 
sizeof(*normalModeShortcuts);
++
++/// Style of the command string visualized in normal mode in the right corner
++/// [Vim Browse].
++Glyph const styleCommand = {' ', ATTR_ITALIC | ATTR_FAINT, 7, 16};
++/// Style of the search string visualized in normal mode in the right corner.
++/// [Vim Browse].
++Glyph const styleSearch = {' ', ATTR_ITALIC | ATTR_BOLD_FAINT, 7, 16};
++
++/// Colors used in normal mode in order to highlight different operations and
++/// empathise the current position on screen  in  the status area [Vim 
Browse].
++unsigned int bgCommandYank = 11;
++unsigned int bgCommandVisual = 4;
++unsigned int bgCommandVisualLine = 12;
++
++unsigned int fgCommandYank = 232;
++unsigned int fgCommandVisual = 232;
++unsigned int fgCommandVisualLine = 232;
++
++unsigned int bgPos = 15;
++unsigned int fgPos = 16;
++
+diff --git a/config.h b/config.h
+new file mode 100644
+index 0000000..92b541d
+--- /dev/null
++++ b/config.h
+@@ -0,0 +1,513 @@
++/* See LICENSE file for copyright and license details. */
++
++/*
++ * appearance
++ *
++ * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
++ */
++static char *font = "Liberation 
Mono:pixelsize=12:antialias=true:autohint=true";
++static int borderpx = 2;
++
++/*
++ * What program is execed by st depends of these precedence rules:
++ * 1: program passed with -e
++ * 2: utmp option
++ * 3: SHELL environment variable
++ * 4: value of shell in /etc/passwd
++ * 5: value of shell in config.h
++ */
++static char *shell = "/bin/sh";
++char *utmp = NULL;
++char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400";
++
++/* identification sequence returned in DA and DECID */
++char *vtiden = "[?6c";
++
++/* Kerning / character bounding-box multipliers */
++static float cwscale = 1.0;
++static float chscale = 1.0;
++
++/*
++ * word delimiter string
++ *
++ * More advanced example: L" `'\"()[]{}"
++ */
++wchar_t *worddelimiters = L" ";
++
++/* selection timeouts (in milliseconds) */
++static unsigned int doubleclicktimeout = 300;
++static unsigned int tripleclicktimeout = 600;
++
++/* alt screens */
++int allowaltscreen = 1;
++
++/* frames per second st should at maximum draw to the screen */
++static unsigned int xfps = 120;
++static unsigned int actionfps = 30;
++
++/*
++ * blinking timeout (set to 0 to disable blinking) for the terminal blinking
++ * attribute.
++ */
++static unsigned int blinktimeout = 800;
++
++/*
++ * thickness of underline and bar cursors
++ */
++static unsigned int cursorthickness = 2;
++
++/*
++ * bell volume. It must be a value between -100 and 100. Use 0 for disabling
++ * it
++ */
++static int bellvolume = 0;
++
++/* default TERM value */
++char *termname = "st-256color";
++
++/*
++ * spaces per tab
++ *
++ * When you are changing this value, don't forget to adapt the »it« value in
++ * the st.info and appropriately install the st.info in the environment where
++ * you use this st version.
++ *
++ *    it#$tabspaces,
++ *
++ * Secondly make sure your kernel is not expanding tabs. When running `stty
++ * -a` »tab0« should appear. You can tell the terminal to not expand tabs by
++ *  running following command:
++ *
++ *    stty tabs
++ */
++unsigned int tabspaces = 8;
++
++/* Terminal colors (16 first used in escape sequence) */
++static const char *colorname[] = {
++      /* 8 normal colors */
++      "black",
++      "red3",
++      "green3",
++      "yellow3",
++      "blue2",
++      "magenta3",
++      "cyan3",
++      "gray90",
++
++      /* 8 bright colors */
++      "gray50",
++      "red",
++      "green",
++      "yellow",
++      "#5c5cff",
++      "magenta",
++      "cyan",
++      "white",
++
++      [255] = 0,
++
++      /* more colors can be added after 255 to use with DefaultXX */
++      "#cccccc",
++      "#555555",
++};
++
++
++/*
++ * Default colors (colorname index)
++ * foreground, background, cursor, reverse cursor
++ */
++unsigned int defaultfg = 7;
++unsigned int defaultbg = 0;
++static unsigned int defaultcs = 256;
++static unsigned int defaultrcs = 257;
++
++/*
++ * Default shape of cursor
++ * 2: Block ("█")
++ * 4: Underline ("_")
++ * 6: Bar ("|")
++ * 7: Snowman ("☃")
++ */
++static unsigned int cursorshape = 2;
++
++/*
++ * Default columns and rows numbers
++ */
++
++static unsigned int cols = 80;
++static unsigned int rows = 24;
++
++/*
++ * Default colour and shape of the mouse cursor
++ */
++static unsigned int mouseshape = XC_xterm;
++static unsigned int mousefg = 7;
++static unsigned int mousebg = 0;
++
++/*
++ * Color used to display font attributes when fontconfig selected a font which
++ * doesn't match the ones requested.
++ */
++static unsigned int defaultattr = 11;
++/// Colors for the entities that are 'highlighted' in normal mode (search
++/// results currently on screen) [Vim Browse].
++static unsigned int highlightBg = 160;
++static unsigned int highlightFg = 15;
++/// Colors for highlighting the current cursor position (row + col) in normal
++/// mode [Vim Browse].
++static unsigned int currentBg = 8;
++static unsigned int currentFg = 15;
++
++/*
++ * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set).
++ * Note that if you want to use ShiftMask with selmasks, set this to an other
++ * modifier, set to 0 to not use it.
++ */
++static uint forcemousemod = ShiftMask;
++
++/*
++ * Internal mouse shortcuts.
++ * Beware that overloading Button1 will disable the selection.
++ */
++static MouseShortcut mshortcuts[] = {
++      /* mask                 button   function        argument       release 
*/
++      { XK_ANY_MOD,           Button2, selpaste,       {.i = 0},      1 },
++      { XK_ANY_MOD,           Button4, ttysend,        {.s = ""} },
++      { XK_ANY_MOD,           Button5, ttysend,        {.s = ""} },
++};
++
++/* Internal keyboard shortcuts. */
++#define MODKEY Mod1Mask
++#define AltMask Mod1Mask
++#define TERMMOD (ControlMask|ShiftMask)
++
++static Shortcut shortcuts[] = {
++      /* mask                 keysym          function        argument */
++      { AltMask,              XK_c,           normalMode,     {.i =  0} },
++      { XK_ANY_MOD,           XK_Break,       sendbreak,      {.i =  0} },
++      { ControlMask,          XK_Print,       toggleprinter,  {.i =  0} },
++      { ShiftMask,            XK_Print,       printscreen,    {.i =  0} },
++      { XK_ANY_MOD,           XK_Print,       printsel,       {.i =  0} },
++      { TERMMOD,              XK_Prior,       zoom,           {.f = +1} },
++      { TERMMOD,              XK_Next,        zoom,           {.f = -1} },
++      { TERMMOD,              XK_Home,        zoomreset,      {.f =  0} },
++      { TERMMOD,              XK_C,           clipcopy,       {.i =  0} },
++      { TERMMOD,              XK_V,           clippaste,      {.i =  0} },
++      { TERMMOD,              XK_Y,           selpaste,       {.i =  0} },
++      { ShiftMask,            XK_Insert,      selpaste,       {.i =  0} },
++      { TERMMOD,              XK_Num_Lock,    numlock,        {.i =  0} },
++      { ShiftMask,            XK_Page_Up,     kscrollup,      {.i = -1} },
++      { ShiftMask,            XK_Page_Down,   kscrolldown,    {.i = -1} },
++};
++
++/*
++ * Special keys (change & recompile st.info accordingly)
++ *
++ * Mask value:
++ * * Use XK_ANY_MOD to match the key no matter modifiers state
++ * * Use XK_NO_MOD to match the key alone (no modifiers)
++ * appkey value:
++ * * 0: no value
++ * * > 0: keypad application mode enabled
++ * *   = 2: term.numlock = 1
++ * * < 0: keypad application mode disabled
++ * appcursor value:
++ * * 0: no value
++ * * > 0: cursor application mode enabled
++ * * < 0: cursor application mode disabled
++ *
++ * Be careful with the order of the definitions because st searches in
++ * this table sequentially, so any XK_ANY_MOD must be in the last
++ * position for a key.
++ */
++
++/*
++ * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF)
++ * to be mapped below, add them to this array.
++ */
++static KeySym mappedkeys[] = { -1 };
++
++/*
++ * State bits to ignore when matching key or button events.  By default,
++ * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored.
++ */
++static uint ignoremod = Mod2Mask|XK_SWITCH_MOD;
++
++/*
++ * This is the huge key array which defines all compatibility to the Linux
++ * world. Please decide about changes wisely.
++ */
++static Key key[] = {
++      /* keysym           mask            string      appkey appcursor */
++      { XK_KP_Home,       ShiftMask,      "",       0,   -1},
++      { XK_KP_Home,       ShiftMask,      "",     0,   +1},
++      { XK_KP_Home,       XK_ANY_MOD,     "",        0,   -1},
++      { XK_KP_Home,       XK_ANY_MOD,     "[1~",       0,   +1},
++      { XK_KP_Up,         XK_ANY_MOD,     "Ox",       +1,    0},
++      { XK_KP_Up,         XK_ANY_MOD,     "",        0,   -1},
++      { XK_KP_Up,         XK_ANY_MOD,     "OA",        0,   +1},
++      { XK_KP_Down,       XK_ANY_MOD,     "Or",       +1,    0},
++      { XK_KP_Down,       XK_ANY_MOD,     "",        0,   -1},
++      { XK_KP_Down,       XK_ANY_MOD,     "OB",        0,   +1},
++      { XK_KP_Left,       XK_ANY_MOD,     "Ot",       +1,    0},
++      { XK_KP_Left,       XK_ANY_MOD,     "",        0,   -1},
++      { XK_KP_Left,       XK_ANY_MOD,     "OD",        0,   +1},
++      { XK_KP_Right,      XK_ANY_MOD,     "Ov",       +1,    0},
++      { XK_KP_Right,      XK_ANY_MOD,     "",        0,   -1},
++      { XK_KP_Right,      XK_ANY_MOD,     "OC",        0,   +1},
++      { XK_KP_Prior,      ShiftMask,      "[5;2~",     0,    0},
++      { XK_KP_Prior,      XK_ANY_MOD,     "[5~",       0,    0},
++      { XK_KP_Begin,      XK_ANY_MOD,     "",        0,    0},
++      { XK_KP_End,        ControlMask,    "",       -1,    0},
++      { XK_KP_End,        ControlMask,    "",    +1,    0},
++      { XK_KP_End,        ShiftMask,      "",       -1,    0},
++      { XK_KP_End,        ShiftMask,      "",    +1,    0},
++      { XK_KP_End,        XK_ANY_MOD,     "[4~",       0,    0},
++      { XK_KP_Next,       ShiftMask,      "[6;2~",     0,    0},
++      { XK_KP_Next,       XK_ANY_MOD,     "[6~",       0,    0},
++      { XK_KP_Insert,     ShiftMask,      "[2;2~",    +1,    0},
++      { XK_KP_Insert,     ShiftMask,      "",      -1,    0},
++      { XK_KP_Insert,     ControlMask,    "",       -1,    0},
++      { XK_KP_Insert,     ControlMask,    "[2;5~",    +1,    0},
++      { XK_KP_Insert,     XK_ANY_MOD,     "",      -1,    0},
++      { XK_KP_Insert,     XK_ANY_MOD,     "[2~",      +1,    0},
++      { XK_KP_Delete,     ControlMask,    "",       -1,    0},
++      { XK_KP_Delete,     ControlMask,    "[3;5~",    +1,    0},
++      { XK_KP_Delete,     ShiftMask,      "",      -1,    0},
++      { XK_KP_Delete,     ShiftMask,      "[3;2~",    +1,    0},
++      { XK_KP_Delete,     XK_ANY_MOD,     "",       -1,    0},
++      { XK_KP_Delete,     XK_ANY_MOD,     "[3~",      +1,    0},
++      { XK_KP_Multiply,   XK_ANY_MOD,     "Oj",       +2,    0},
++      { XK_KP_Add,        XK_ANY_MOD,     "Ok",       +2,    0},
++      { XK_KP_Enter,      XK_ANY_MOD,     "OM",       +2,    0},
++      { XK_KP_Enter,      XK_ANY_MOD,     "
",           -1,    0},
++      { XK_KP_Subtract,   XK_ANY_MOD,     "Om",       +2,    0},
++      { XK_KP_Decimal,    XK_ANY_MOD,     "On",       +2,    0},
++      { XK_KP_Divide,     XK_ANY_MOD,     "Oo",       +2,    0},
++      { XK_KP_0,          XK_ANY_MOD,     "Op",       +2,    0},
++      { XK_KP_1,          XK_ANY_MOD,     "Oq",       +2,    0},
++      { XK_KP_2,          XK_ANY_MOD,     "Or",       +2,    0},
++      { XK_KP_3,          XK_ANY_MOD,     "Os",       +2,    0},
++      { XK_KP_4,          XK_ANY_MOD,     "Ot",       +2,    0},
++      { XK_KP_5,          XK_ANY_MOD,     "Ou",       +2,    0},
++      { XK_KP_6,          XK_ANY_MOD,     "Ov",       +2,    0},
++      { XK_KP_7,          XK_ANY_MOD,     "Ow",       +2,    0},
++      { XK_KP_8,          XK_ANY_MOD,     "Ox",       +2,    0},
++      { XK_KP_9,          XK_ANY_MOD,     "Oy",       +2,    0},
++      { XK_Up,            ShiftMask,      "",     0,    0},
++      { XK_Up,            Mod1Mask,       "",     0,    0},
++      { XK_Up,         ShiftMask|Mod1Mask,"",     0,    0},
++      { XK_Up,            ControlMask,    "",     0,    0},
++      { XK_Up,      ShiftMask|ControlMask,"",     0,    0},
++      { XK_Up,       ControlMask|Mod1Mask,"",     0,    0},
++      { XK_Up,ShiftMask|ControlMask|Mod1Mask,"",  0,    0},
++      { XK_Up,            XK_ANY_MOD,     "",        0,   -1},
++      { XK_Up,            XK_ANY_MOD,     "OA",        0,   +1},
++      { XK_Down,          ShiftMask,      "",     0,    0},
++      { XK_Down,          Mod1Mask,       "",     0,    0},
++      { XK_Down,       ShiftMask|Mod1Mask,"",     0,    0},
++      { XK_Down,          ControlMask,    "",     0,    0},
++      { XK_Down,    ShiftMask|ControlMask,"",     0,    0},
++      { XK_Down,     ControlMask|Mod1Mask,"",     0,    0},
++      { XK_Down,ShiftMask|ControlMask|Mod1Mask,"",0,    0},
++      { XK_Down,          XK_ANY_MOD,     "",        0,   -1},
++      { XK_Down,          XK_ANY_MOD,     "OB",        0,   +1},
++      { XK_Left,          ShiftMask,      "",     0,    0},
++      { XK_Left,          Mod1Mask,       "",     0,    0},
++      { XK_Left,       ShiftMask|Mod1Mask,"",     0,    0},
++      { XK_Left,          ControlMask,    "",     0,    0},
++      { XK_Left,    ShiftMask|ControlMask,"",     0,    0},
++      { XK_Left,     ControlMask|Mod1Mask,"",     0,    0},
++      { XK_Left,ShiftMask|ControlMask|Mod1Mask,"",0,    0},
++      { XK_Left,          XK_ANY_MOD,     "",        0,   -1},
++      { XK_Left,          XK_ANY_MOD,     "OD",        0,   +1},
++      { XK_Right,         ShiftMask,      "",     0,    0},
++      { XK_Right,         Mod1Mask,       "",     0,    0},
++      { XK_Right,      ShiftMask|Mod1Mask,"",     0,    0},
++      { XK_Right,         ControlMask,    "",     0,    0},
++      { XK_Right,   ShiftMask|ControlMask,"",     0,    0},
++      { XK_Right,    ControlMask|Mod1Mask,"",     0,    0},
++      { XK_Right,ShiftMask|ControlMask|Mod1Mask,"",0,   0},
++      { XK_Right,         XK_ANY_MOD,     "",        0,   -1},
++      { XK_Right,         XK_ANY_MOD,     "OC",        0,   +1},
++      { XK_ISO_Left_Tab,  ShiftMask,      "",        0,    0},
++      { XK_Return,        Mod1Mask,       "
",        0,    0},
++      { XK_Return,        XK_ANY_MOD,     "
",            0,    0},
++      { XK_Insert,        ShiftMask,      "",      -1,    0},
++      { XK_Insert,        ShiftMask,      "[2;2~",    +1,    0},
++      { XK_Insert,        ControlMask,    "",       -1,    0},
++      { XK_Insert,        ControlMask,    "[2;5~",    +1,    0},
++      { XK_Insert,        XK_ANY_MOD,     "",      -1,    0},
++      { XK_Insert,        XK_ANY_MOD,     "[2~",      +1,    0},
++      { XK_Delete,        ControlMask,    "",       -1,    0},
++      { XK_Delete,        ControlMask,    "[3;5~",    +1,    0},
++      { XK_Delete,        ShiftMask,      "",      -1,    0},
++      { XK_Delete,        ShiftMask,      "[3;2~",    +1,    0},
++      { XK_Delete,        XK_ANY_MOD,     "",       -1,    0},
++      { XK_Delete,        XK_ANY_MOD,     "[3~",      +1,    0},
++      { XK_BackSpace,     XK_NO_MOD,      "\177",          0,    0},
++      { XK_BackSpace,     Mod1Mask,       "\177",      0,    0},
++      { XK_Home,          ShiftMask,      "",       0,   -1},
++      { XK_Home,          ShiftMask,      "",     0,   +1},
++      { XK_Home,          XK_ANY_MOD,     "",        0,   -1},
++      { XK_Home,          XK_ANY_MOD,     "[1~",       0,   +1},
++      { XK_End,           ControlMask,    "",       -1,    0},
++      { XK_End,           ControlMask,    "",    +1,    0},
++      { XK_End,           ShiftMask,      "",       -1,    0},
++      { XK_End,           ShiftMask,      "",    +1,    0},
++      { XK_End,           XK_ANY_MOD,     "[4~",       0,    0},
++      { XK_Prior,         ControlMask,    "[5;5~",     0,    0},
++      { XK_Prior,         ShiftMask,      "[5;2~",     0,    0},
++      { XK_Prior,         XK_ANY_MOD,     "[5~",       0,    0},
++      { XK_Next,          ControlMask,    "[6;5~",     0,    0},
++      { XK_Next,          ShiftMask,      "[6;2~",     0,    0},
++      { XK_Next,          XK_ANY_MOD,     "[6~",       0,    0},
++      { XK_F1,            XK_NO_MOD,      "OP" ,       0,    0},
++      { XK_F1, /* F13 */  ShiftMask,      "",     0,    0},
++      { XK_F1, /* F25 */  ControlMask,    "",     0,    0},
++      { XK_F1, /* F37 */  Mod4Mask,       "",     0,    0},
++      { XK_F1, /* F49 */  Mod1Mask,       "",     0,    0},
++      { XK_F1, /* F61 */  Mod3Mask,       "",     0,    0},
++      { XK_F2,            XK_NO_MOD,      "OQ" ,       0,    0},
++      { XK_F2, /* F14 */  ShiftMask,      "",     0,    0},
++      { XK_F2, /* F26 */  ControlMask,    "",     0,    0},
++      { XK_F2, /* F38 */  Mod4Mask,       "",     0,    0},
++      { XK_F2, /* F50 */  Mod1Mask,       "",     0,    0},
++      { XK_F2, /* F62 */  Mod3Mask,       "",     0,    0},
++      { XK_F3,            XK_NO_MOD,      "OR" ,       0,    0},
++      { XK_F3, /* F15 */  ShiftMask,      "",     0,    0},
++      { XK_F3, /* F27 */  ControlMask,    "",     0,    0},
++      { XK_F3, /* F39 */  Mod4Mask,       "",     0,    0},
++      { XK_F3, /* F51 */  Mod1Mask,       "",     0,    0},
++      { XK_F3, /* F63 */  Mod3Mask,       "",     0,    0},
++      { XK_F4,            XK_NO_MOD,      "OS" ,       0,    0},
++      { XK_F4, /* F16 */  ShiftMask,      "",     0,    0},
++      { XK_F4, /* F28 */  ControlMask,    "",     0,    0},
++      { XK_F4, /* F40 */  Mod4Mask,       "",     0,    0},
++      { XK_F4, /* F52 */  Mod1Mask,       "",     0,    0},
++      { XK_F5,            XK_NO_MOD,      "[15~",      0,    0},
++      { XK_F5, /* F17 */  ShiftMask,      "[15;2~",    0,    0},
++      { XK_F5, /* F29 */  ControlMask,    "[15;5~",    0,    0},
++      { XK_F5, /* F41 */  Mod4Mask,       "[15;6~",    0,    0},
++      { XK_F5, /* F53 */  Mod1Mask,       "[15;3~",    0,    0},
++      { XK_F6,            XK_NO_MOD,      "[17~",      0,    0},
++      { XK_F6, /* F18 */  ShiftMask,      "[17;2~",    0,    0},
++      { XK_F6, /* F30 */  ControlMask,    "[17;5~",    0,    0},
++      { XK_F6, /* F42 */  Mod4Mask,       "[17;6~",    0,    0},
++      { XK_F6, /* F54 */  Mod1Mask,       "[17;3~",    0,    0},
++      { XK_F7,            XK_NO_MOD,      "[18~",      0,    0},
++      { XK_F7, /* F19 */  ShiftMask,      "[18;2~",    0,    0},
++      { XK_F7, /* F31 */  ControlMask,    "[18;5~",    0,    0},
++      { XK_F7, /* F43 */  Mod4Mask,       "[18;6~",    0,    0},
++      { XK_F7, /* F55 */  Mod1Mask,       "[18;3~",    0,    0},
++      { XK_F8,            XK_NO_MOD,      "[19~",      0,    0},
++      { XK_F8, /* F20 */  ShiftMask,      "[19;2~",    0,    0},
++      { XK_F8, /* F32 */  ControlMask,    "[19;5~",    0,    0},
++      { XK_F8, /* F44 */  Mod4Mask,       "[19;6~",    0,    0},
++      { XK_F8, /* F56 */  Mod1Mask,       "[19;3~",    0,    0},
++      { XK_F9,            XK_NO_MOD,      "[20~",      0,    0},
++      { XK_F9, /* F21 */  ShiftMask,      "[20;2~",    0,    0},
++      { XK_F9, /* F33 */  ControlMask,    "[20;5~",    0,    0},
++      { XK_F9, /* F45 */  Mod4Mask,       "[20;6~",    0,    0},
++      { XK_F9, /* F57 */  Mod1Mask,       "[20;3~",    0,    0},
++      { XK_F10,           XK_NO_MOD,      "[21~",      0,    0},
++      { XK_F10, /* F22 */ ShiftMask,      "[21;2~",    0,    0},
++      { XK_F10, /* F34 */ ControlMask,    "[21;5~",    0,    0},
++      { XK_F10, /* F46 */ Mod4Mask,       "[21;6~",    0,    0},
++      { XK_F10, /* F58 */ Mod1Mask,       "[21;3~",    0,    0},
++      { XK_F11,           XK_NO_MOD,      "[23~",      0,    0},
++      { XK_F11, /* F23 */ ShiftMask,      "[23;2~",    0,    0},
++      { XK_F11, /* F35 */ ControlMask,    "[23;5~",    0,    0},
++      { XK_F11, /* F47 */ Mod4Mask,       "[23;6~",    0,    0},
++      { XK_F11, /* F59 */ Mod1Mask,       "[23;3~",    0,    0},
++      { XK_F12,           XK_NO_MOD,      "[24~",      0,    0},
++      { XK_F12, /* F24 */ ShiftMask,      "[24;2~",    0,    0},
++      { XK_F12, /* F36 */ ControlMask,    "[24;5~",    0,    0},
++      { XK_F12, /* F48 */ Mod4Mask,       "[24;6~",    0,    0},
++      { XK_F12, /* F60 */ Mod1Mask,       "[24;3~",    0,    0},
++      { XK_F13,           XK_NO_MOD,      "",     0,    0},
++      { XK_F14,           XK_NO_MOD,      "",     0,    0},
++      { XK_F15,           XK_NO_MOD,      "",     0,    0},
++      { XK_F16,           XK_NO_MOD,      "",     0,    0},
++      { XK_F17,           XK_NO_MOD,      "[15;2~",    0,    0},
++      { XK_F18,           XK_NO_MOD,      "[17;2~",    0,    0},
++      { XK_F19,           XK_NO_MOD,      "[18;2~",    0,    0},
++      { XK_F20,           XK_NO_MOD,      "[19;2~",    0,    0},
++      { XK_F21,           XK_NO_MOD,      "[20;2~",    0,    0},
++      { XK_F22,           XK_NO_MOD,      "[21;2~",    0,    0},
++      { XK_F23,           XK_NO_MOD,      "[23;2~",    0,    0},
++      { XK_F24,           XK_NO_MOD,      "[24;2~",    0,    0},
++      { XK_F25,           XK_NO_MOD,      "",     0,    0},
++      { XK_F26,           XK_NO_MOD,      "",     0,    0},
++      { XK_F27,           XK_NO_MOD,      "",     0,    0},
++      { XK_F28,           XK_NO_MOD,      "",     0,    0},
++      { XK_F29,           XK_NO_MOD,      "[15;5~",    0,    0},
++      { XK_F30,           XK_NO_MOD,      "[17;5~",    0,    0},
++      { XK_F31,           XK_NO_MOD,      "[18;5~",    0,    0},
++      { XK_F32,           XK_NO_MOD,      "[19;5~",    0,    0},
++      { XK_F33,           XK_NO_MOD,      "[20;5~",    0,    0},
++      { XK_F34,           XK_NO_MOD,      "[21;5~",    0,    0},
++      { XK_F35,           XK_NO_MOD,      "[23;5~",    0,    0},
++};
++
++/*
++ * Selection types' masks.
++ * Use the same masks as usual.
++ * Button1Mask is always unset, to make masks match between ButtonPress.
++ * ButtonRelease and MotionNotify.
++ * If no match is found, regular selection is used.
++ */
++static uint selmasks[] = {
++      [SEL_RECTANGULAR] = Mod1Mask,
++};
++
++/*
++ * Printable characters in ASCII, used to estimate the advance width
++ * of single wide characters.
++ */
++static char ascii_printable[] =
++      " !\"#$%&'()*+,-./0123456789:;<=>?"
++      "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_"
++      "`abcdefghijklmnopqrstuvwxyz{|}~";
++
++
++/// word sepearors normal mode
++/// [Vim Browse].
++char wordDelimSmall[] = "     !\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~";
++char wordDelimLarge[] = "     "; /// <Word sepearors normal mode (capital W)
++
++/// Shortcusts executed in normal mode (which should not already be in use)
++/// [Vim Browse].
++struct NormalModeShortcuts normalModeShortcuts [] = {
++      { 'R', "?Building
" },
++      { 'r', "/Building
" },
++      { 'F', "?: error:
" },
++      { 'f', "/: error:
" },
++      { 'Q', "?[Leaving vim, starting execution]
" },
++      { 'S', "Qf" },
++      { 'X', "?juli@machine
" },
++      { 'x', "/juli@machine
" },
++};
++
++size_t const amountNormalModeShortcuts = sizeof(normalModeShortcuts) / 
sizeof(*normalModeShortcuts);
++
++/// Style of the command string visualized in normal mode in the right corner
++/// [Vim Browse].
++Glyph const styleCommand = {' ', ATTR_ITALIC | ATTR_FAINT, 7, 16};
++/// Style of the search string visualized in normal mode in the right corner.
++/// [Vim Browse].
++Glyph const styleSearch = {' ', ATTR_ITALIC | ATTR_BOLD_FAINT, 7, 16};
++
++/// Colors used in normal mode in order to highlight different operations and
++/// empathise the current position on screen  in  the status area [Vim 
Browse].
++unsigned int bgCommandYank = 11;
++unsigned int bgCommandVisual = 4;
++unsigned int bgCommandVisualLine = 12;
++
++unsigned int fgCommandYank = 232;
++unsigned int fgCommandVisual = 232;
++unsigned int fgCommandVisualLine = 232;
++
++unsigned int bgPos = 15;
++unsigned int fgPos = 16;
++
+diff --git a/dynamicArray.h b/dynamicArray.h
+new file mode 100644
+index 0000000..8af5d9c
+--- /dev/null
++++ b/dynamicArray.h
+@@ -0,0 +1,175 @@
++#ifndef DYNAMIC_ARRAY_H
++#define DYNAMIC_ARRAY_H
++
++#include "error.h"
++
++#include <stdint.h>
++#include <stdlib.h>
++#include <string.h>
++#include <stdbool.h>
++
++/// Struct for which this file offers functionality in order to expand the 
array
++/// and set / get its content.
++typedef struct DynamicArray {
++      /// Size of the datatype contained in the array.
++      uint8_t itemSize;
++      /// Amount of bytes currently initialized
++      uint32_t index;
++      /// Amount of bytes currently reserved (not necessarily initialized)
++      uint32_t allocated;
++      /// Actual content.
++      char* content;
++} DynamicArray;
++
++#define EXPAND_STEP 15
++
++/// Default initializers for the dynamic array.
++#define CHAR_ARRAY  {1, 0, 0, NULL}
++#define WORD_ARRAY  {2, 0, 0, NULL}
++#define DWORD_ARRAY {4, 0, 0, NULL}
++#define QWORD_ARRAY {8, 0, 0, NULL}
++/// (Wasteful) utf-8 array, that always used 4 bytes in order to display a
++/// character, even if the space is not required.
++#define UTF8_ARRAY  DWORD_ARRAY
++
++/// Check that at least \p bytes are allocated, if true implying that
++/// \p s->content[ytes - 1] is allocated.
++static inline bool
++isAllocated(DynamicArray const *s, uint32_t bytes) {
++      return s != NULL && s->allocated >= bytes;
++}
++
++/// @see #isAllocated
++static inline bool
++isInitialized(DynamicArray const *s, uint32_t bytes) {
++      return s != NULL && s->index >= bytes;
++}
++
++/// Return the next element in \p s and increment index without checking 
bounds.
++static inline char*
++gnext(DynamicArray *s) {
++      ENSURE(s!=NULL, return NULL);
++      ENSURE(s->index % s->itemSize == 0 && "(index not aligned)",
++                      s->index += s->itemSize - (s->index % s->itemSize));
++      ENSURE(isAllocated(s, s->index + 2 * s->itemSize), return NULL);
++      return s->content + (s->index += s->itemSize);
++}
++
++/// View element \p i in \p s.
++static inline char*
++view(DynamicArray const * s, uint32_t i) {
++      ENSURE((s != NULL) && isAllocated(s, (i+1) * s->itemSize), return NULL);
++      return s->content + i*s->itemSize;
++}
++
++/// Inspect element content[size() - 1 - i].
++static inline char *
++viewEnd(DynamicArray const *s, uint32_t i) {
++      ENSURE((s != NULL) && isInitialized(s, i * s->itemSize), return NULL);
++      ENSURE(s->index%s->itemSize == 0 && "(index not aligned)", return NULL);
++      return s->content + s->index - (i + 1) * s->itemSize;
++}
++
++/// Set conent without applying
++static inline bool
++setValues(DynamicArray* s, char const *vals, uint32_t amount) {
++      ENSURE(vals != NULL, return false);
++      ENSURE((s != NULL) && isAllocated(s, s->index + amount), return false);
++      memcpy(s->content + s->index, vals, amount);
++      return true;
++}
++
++static inline bool
++snext(DynamicArray* s, char const *vals, uint32_t amount) {
++      bool const success = setValues(s, vals, amount);
++      ENSURE(success, return false);
++      uint8_t const rest = amount % s->itemSize;
++      uint32_t const newSize = s->index + amount + (rest ? s->itemSize : 0);
++      ENSURE(isAllocated(s, newSize), return false);
++      s->index = newSize;
++      return true;
++}
++
++/// Empty \p s.
++static inline void
++empty(DynamicArray* s) {
++      ENSURE((s != NULL), return);
++      s->index = 0;
++}
++
++/// Check if \p s has initialized content (which can be the case even if 
memory
++/// is allocated).
++static inline bool
++isEmpty(DynamicArray const * s) {
++      ENSURE((s != NULL), return true);
++      return s->index == 0;
++}
++
++static inline int
++size(DynamicArray const * s) {
++      ENSURE(s != NULL, return 0);
++      ENSURE(s->itemSize != 0, return 0);
++      return s->index / s->itemSize;
++}
++
++static inline void
++pop(DynamicArray* s) {
++      ENSURE((s != NULL), return);
++      ENSURE(s->index % s->itemSize == 0 && "(index not aligned)",
++                      s->index += s->itemSize - (s->index % s->itemSize));
++      ENSURE(isInitialized(s, s->itemSize), return);
++      s->index -= s->itemSize;
++}
++
++static inline bool
++checkSetNext(DynamicArray *s, char const *c, uint32_t amount) {
++      ENSURE(s != NULL && c != NULL, return false);
++      if (s->allocated < s->index + s->itemSize * amount) {
++              uint32_t const diff = s->index+s->itemSize*amount-s->allocated;
++              uint32_t const newAlloSize = s->allocated + (diff > EXPAND_STEP
++                              ? diff : EXPAND_STEP) * s->itemSize;
++              char* tmp = realloc(s->content, newAlloSize);
++              if (tmp == NULL) { return false; }
++              s->allocated = newAlloSize;
++              s->content = tmp;
++              assert(s->allocated >= s->index + s->itemSize * amount);
++      }
++      if (amount) { snext(s, c, amount); }
++      return true;
++}
++
++static inline bool
++checkSetNextV(DynamicArray *s, char const c) {
++      return checkSetNext(s, &c, 1);
++}
++
++static inline bool
++checkSetNextP(DynamicArray *s, char const *c) {
++      ENSURE(c != NULL, return false);
++      return checkSetNext(s, c, strlen(c));
++}
++
++/// Expand the currently initialized content in \p s and the allocated chunk 
of
++/// memory if required.
++static char *
++expand(DynamicArray *s) {
++      ENSURE(s != NULL, return NULL);
++      if (s->allocated < s->index + s->itemSize) {
++              uint32_t const diff = s->index + s->itemSize - s->allocated;
++              uint32_t const newAlloSize = s->allocated + (diff > EXPAND_STEP
++                              ? diff : EXPAND_STEP) * s->itemSize;
++              char* tmp = realloc(s->content, newAlloSize);
++              if (tmp == NULL) { return NULL; }
++              s->allocated = newAlloSize;
++              s->content = tmp;
++              assert(s->allocated >= s->index + s->itemSize);
++      }
++      s->index+=s->itemSize;
++      return viewEnd(s, 0);
++}
++
++#define append(s, c) checkSetNext((s), (char const *) (c), (s)->itemSize)
++#define appendPartial(s, c, i) checkSetNext((s), (char const *) (c), (i))
++
++
++#endif // DYNAMIC_ARRAY_H
+diff --git a/error.h b/error.h
+new file mode 100644
+index 0000000..91c621f
+--- /dev/null
++++ b/error.h
+@@ -0,0 +1,47 @@
++#ifndef ERROR_H
++#define ERROR_H
++
++#include <assert.h>
++
++// Flag which determines whether to fail if a required condition is not met, 
or
++// to adapt the condition in order to work properly.
++// Attention: Be sure to perform a clean build after you alter preprocessor
++//            directives / definitions.
++//#define FAIL_ON_ERROR
++
++#include <stdio.h>
++
++///
++/// Function used in case the fail-on-error mode is disabled (via definition)
++/// to report errors. In debug production mode, alias st to st 2> error.log.
++static void reportError(char const * cond, char const * stt, char const * 
file,
++              unsigned int line ) {
++      unsigned int const maxErrorCount = 100;
++      static unsigned int errorCount = 0;
++      if (++errorCount == 1) {
++              printf("Report the following bug to "
++                              "https://github.com/juliusHuelsmann/st.
");
++      }
++      if (errorCount < maxErrorCount) {
++              printf("Bug:    Condition '%s' evaluates to false.
        Performing"
++                              " '%s' to counteract.
        File:%s:%u
",
++                              cond, stt, file, line);
++      } else if (errorCount == maxErrorCount) {
++              printf("Max amount of reported errors %u is reached. From here"
++                              "on, no additional errors will be reported.
",
++                              maxErrorCount);
++      }
++}
++
++/// Note that everyting condition checked / endforced with #ENSURE is
++/// considered an error, and behaves like an error depending on the flag.
++#ifdef FAIL_ON_ERROR
++#define ENSURE(cond, stt) assert(cond);
++#else // FAIL_ON_ERROR
++#define ENSURE(cond, stt) if (!(cond)) {                                      
 \
++                              reportError(#cond, #stt, __FILE__, __LINE__);  \
++                              stt;                                           \
++                          }
++#endif // FAIL_ON_ERROR
++
++#endif // ERROR_H
+diff --git a/glyph.h b/glyph.h
+new file mode 100644
+index 0000000..84aa252
+--- /dev/null
++++ b/glyph.h
+@@ -0,0 +1,30 @@
++#ifndef LINE_H
++#define LINE_H
++
++//
++// Contains the representation of the entities in the buffer (Line, Gylph), 
that
++// is used by every part of the software implmeneting terminal logic.
++//
++
++#include <stdint.h>
++
++enum selection_type {
++      SEL_REGULAR = 1,
++      SEL_RECTANGULAR = 2
++};
++
++typedef uint_least32_t Rune;
++
++#define Glyph Glyph_
++
++typedef struct {
++      Rune u;           /* character code */
++      unsigned short mode;      /* attribute flags */
++      uint32_t fg;      /* foreground  */
++      uint32_t bg;      /* background  */
++} Glyph;
++
++
++typedef Glyph *Line;
++
++#endif // LINE_H
+diff --git a/normalMode.c b/normalMode.c
+new file mode 100644
+index 0000000..59a5a89
+--- /dev/null
++++ b/normalMode.c
+@@ -0,0 +1,750 @@
++/* See LICENSE for license details. */
++#include "normalMode.h"
++#include "dynamicArray.h"
++#include "term.h"
++#include "win.h"
++#include "error.h"
++
++#include <X11/keysym.h>
++#include <X11/XKBlib.h>
++
++#include <ctype.h>
++#include <stdio.h>
++#include <limits.h>
++#include <math.h>
++
++#define LEN(a)                 (sizeof(a) / sizeof(a)[0])
++#define BETWEEN(x, a, b)       ((a) <= (x) && (x) <= (b))
++#define FALLTHROUGH            __attribute__((fallthrough));
++#define SEC(var,ini,h,r)       var = ini; if (!var) { h; return r; }
++#define EXPAND(v1,v2,r)        char *SEC(v1, expand(v2), empty(v2), true)
++#define currentCommand         (toggle ? &commandHist0 : &commandHist1)
++#define lastCommand            (toggle ? &commandHist1 : &commandHist0)
++
++//
++// Interface to the terminal
++extern Glyph const styleCommand, styleSearch;
++extern NormalModeShortcuts normalModeShortcuts[];
++extern size_t const amountNormalModeShortcuts;
++extern char wordDelimSmall[];
++extern char wordDelimLarge[];
++extern unsigned int fgCommandYank, fgCommandVisual, fgCommandVisualLine,
++       bgCommandYank, bgCommandVisual, bgCommandVisualLine, bgPos, fgPos;
++
++extern void selclear(void);
++extern void tsetdirt(int, int);
++extern size_t utf8encode(Rune, char *);
++extern size_t utf8decode(const char *, Rune *, size_t);
++extern size_t utf8decodebyte(char c, size_t *i);
++
++extern void selextend(int, int, int, int, int);
++extern void selstart(int, int, int, int);
++extern char *getsel(void);
++extern void tfulldirt(void);
++
++//
++// `Private` structs
++typedef struct { uint32_t x; uint32_t y; uint32_t yScr; } Position;
++
++/// Entire normal mode state, consisting of an operation and a motion.
++typedef struct {
++      Position initialPosition;
++      struct OperationState {
++              enum Operation {
++                      noop = ' ', visual='v', visualLine='V', yank = 'y' } op;
++              Position startPosition;
++              enum Infix { infix_none = 0, infix_i = 1, infix_a = 2, } infix;
++      } command;
++      struct MotionState {
++              uint32_t amount;
++              enum Search {none, forward, backward} search;
++              Position searchPosition;
++              bool finished;
++      } motion;
++} NormalModeState;
++
++/// Default state if no operation is performed.
++NormalModeState defaultNormalMode = {
++      {0,0,0},    {noop, {0, 0, 0}, false},   {0, none, {0, 0, 0}, true}
++};
++NormalModeState stateVB = {
++      {0,0,0},    {noop, {0, 0, 0}, false},   {0, none, {0, 0, 0}, true}
++};
++
++DynamicArray searchString =  UTF8_ARRAY;
++DynamicArray commandHist0 =  UTF8_ARRAY;
++DynamicArray commandHist1 =  UTF8_ARRAY;
++DynamicArray highlights   = DWORD_ARRAY;
++
++/// History command toggle
++static bool toggle = false;
++
++//
++// Utility functions
++static inline int intervalDiff(int v, int a, int b) {
++      return (v < a) ? (v - a) : ((v > b) ? (v - b) : 0);
++}
++static inline void swap(DynamicArray *const a, DynamicArray *const b) {
++      DynamicArray tmp = *a; *a = *b; *b = tmp;
++}
++static inline int max(int a, int b) { return a > b ? a : b; }
++static inline int min(int a, int b) { return a < b ? a : b; }
++static inline int mod(int a, int b) { for (; a < 0; a += b); return a % b; }
++static inline bool contains (char c, char const * values, uint32_t memSize) {
++      ENSURE(values != NULL, return false);
++      for (uint32_t i = 0; i < memSize; ++i) if (c == values[i]) return true;
++      return false;
++}
++static inline void applyPosition(Position const *pos) {
++      ENSURE(pos != NULL, return);
++      term.c.x = pos->x;
++      term.c.y = pos->y;
++      term.scr = pos->yScr;
++}
++static inline int getSearchDirection(void) {
++      return stateVB.motion.search == forward ? 1 : -1;
++}
++
++//  Utilities for working with the current version of the scrollback patch.
++static bool moveLine(int32_t const amount) {
++      int32_t const reqShift = intervalDiff(term.c.y+=amount, 0, term.row-1);
++      term.c.y -= reqShift;
++      int32_t const sDiff = intervalDiff(term.scr-=reqShift, 0, HISTSIZE-1);
++      term.scr -= sDiff;
++      return sDiff == 0;
++}
++
++static void moveLetter(int32_t const amount) {
++      int32_t value = (term.c.x += amount) / term.col;
++      if (value -= (term.c.x < 0)) {
++              term.c.x = moveLine(value) ? mod(term.c.x, term.col)
++                      : max(min(term.c.x,term.col - 1), 0);
++      }
++      assert(BETWEEN(term.c.x,0,term.col-1)&&BETWEEN(term.c.y,0,term.row-1));
++}
++
++//
++// `Private` functions:
++
++// Functions: Temporarily display string on screen.
++
++/// Display string at end of a specified line without writing it into the 
buffer
++/// @param str  string that is to be displayed
++/// @param g    glyph
++/// @param yPos
++static void
++displayString(DynamicArray const *str, Glyph const *g, int yPos, bool prePos) 
{
++      ENSURE((str != NULL) && (g != NULL) && (term.row > 0), return);
++      ENSURE(yPos >= 0, yPos = 0);
++      ENSURE(yPos < term.row, yPos = term.row - 1);
++      // Arbritary limit to avoid withhelding too much info from user.
++      int const maxFractionOverridden = 3;
++      // Threshold: if there is no space to print, do not print, but transfer
++      //            repsonsibility for printing back to [st].
++      if (term.col < maxFractionOverridden) {                        // (0)
++              term.dirty[yPos] = 1;
++              return;
++      }
++      int32_t const botSz = prePos * 6; //< sz for position indication
++      // Determine the dimensions of used chunk of screen.
++      int32_t const overrideSize = min(size(str) + botSz,
++                      term.col / maxFractionOverridden);             // (1)
++      int32_t const overrideEnd = term.col - 2;
++      // Has to follow trivially hence th assert:
++      // overrideSize <(1)= term.col/3  <(0)= term.col = overrideEnd + 1.
++      assert(overrideSize <= overrideEnd + 1);
++      int32_t const overrideStart = 1 + overrideEnd - overrideSize;
++      // display history[history.size() - (overrideSize - botSz)::-1]
++      Glyph *SEC(line, malloc(sizeof(Glyph) * (overrideSize)),,)
++      int32_t offset = (size(str) - overrideSize - 1 + botSz) * str->itemSize;
++      for (uint32_t chr = 0; chr < overrideSize - botSz; ++chr) {
++              line[chr] = *g;
++              line[chr].u = *((Rune*) (str->content+(offset+=str->itemSize)));
++      }
++      if (prePos) {
++              ENSURE(term.scr < HISTSIZE, term.scr = HISTSIZE - 1);
++              int32_t const p=round((HISTSIZE-1-term.scr)*100./(HISTSIZE-1));
++              char prc [8];
++              switch (term.scr) {
++                      case HISTSIZE - 1: strcpy(prc, " [TOP]"); break;
++                      case 0:            strcpy(prc, " [BOT]"); break;
++                      default:           sprintf(prc, " % 3d%c  ", p, '%');
++              }
++              for (uint32_t chr = 0; chr < botSz; ++chr) {
++                      line[chr + overrideSize - botSz] =*g;
++                      line[chr + overrideSize - botSz].fg = fgPos;
++                      line[chr + overrideSize - botSz].bg = bgPos;
++                      utf8decode(&prc[chr],&line[chr+overrideSize-botSz].u,1);
++              }
++              line[overrideSize - botSz] =*g;
++      }
++      xdrawline(TLINE(yPos), 0, yPos, overrideStart);
++      term.c.y -= term.row; term.c.x -= term.col; // not highlight hack
++      xdrawline(line-overrideStart, overrideStart, yPos, overrideEnd + 1);
++      term.c.y += term.row; term.c.x += term.col;
++      free(line);
++}
++
++static inline void printCommandString(void) {
++      Glyph g = styleCommand;
++      switch(stateVB.command.op) {
++              case yank: g.fg = fgCommandYank; g.bg = bgCommandYank; break;
++              case visual: g.fg=fgCommandVisual; g.bg=bgCommandVisual; break;
++              case visualLine: g.fg=fgCommandVisualLine;
++                               g.bg=bgCommandVisualLine;
++      }
++      displayString(isEmpty(currentCommand) ? lastCommand : currentCommand,
++                      &g, term.row - 1, true);
++}
++
++static inline void printSearchString(void) {
++      displayString(&searchString, &styleSearch, term.row - 2, false);
++}
++
++// NormalMode Operation / Motion utilies.
++
++static inline bool isMotionFinished(void) { return stateVB.motion.finished; }
++
++static inline void finishMotion(void) { stateVB.motion.finished = true; }
++
++static inline bool isOperationFinished(void) {
++      return stateVB.command.op==noop && stateVB.command.infix==infix_none;
++}
++
++/// Register that the current comamnd is finished and a new command is lgoged
++static inline  void startNewCommand(bool abort) {
++      if (!abort) { toggle = !toggle; }
++      empty(currentCommand);
++}
++
++static inline void finishOperation(void) {
++      stateVB.command = defaultNormalMode.command;
++      assert(isOperationFinished());
++      // After an operation is finished, the selection has to be released and
++      // no highlights are to be released.
++      selclear();
++      empty(&highlights);
++      // THe command string is reset for a new command.
++      startNewCommand(true);
++}
++
++static inline void enableOperation(enum Operation o) {
++      finishOperation();
++      stateVB.command.op = o;
++      stateVB.command.infix = infix_none;
++      stateVB.command.startPosition.x = term.c.x;
++      stateVB.command.startPosition.y = term.c.y;
++      stateVB.command.startPosition.yScr = term.scr;
++}
++
++/// @param abort: If enabled, the command exits without registering
++/// @return       Whether the the application is ready to yield control back 
to
++//the normal command flow
++static bool terminateCommand(bool abort) {
++      bool const exitOperation = isMotionFinished();
++      bool exitNormalMode = false;
++      finishMotion();
++
++      if (exitOperation) {
++              exitNormalMode = isOperationFinished();
++              finishOperation();
++      }
++      printCommandString();
++      printSearchString();
++      return exitNormalMode;
++}
++
++static inline void exitCommand(void) { terminateCommand(false); }
++
++static inline void abortCommand(void) { terminateCommand(true); }
++
++/// Go to next occurrence of string relative to the current location
++/// conduct search, starting at start pos
++static bool gotoString(int8_t sign) {
++      moveLetter(sign);
++      uint32_t const searchStrSize = size(&searchString);
++      uint32_t const maxIter = (HISTSIZE+term.row) * term.col + searchStrSize;
++      uint32_t findIdx = 0;
++      for (uint32_t cIteration = 0; findIdx < searchStrSize
++                      && ++cIteration <= maxIter; moveLetter(sign)) {
++              char const * const SEC(next, sign==1
++                              ? view(&searchString, findIdx)
++                              : viewEnd(&searchString, findIdx), , false)
++              uint32_t const searchChar = *((uint32_t*) next);
++
++              if (TLINE(term.c.y)[term.c.x].u == searchChar) { ++findIdx; }
++              else { findIdx = 0; }
++      }
++      bool const found = findIdx == searchStrSize;
++      for (uint32_t i = 0; found && i < searchStrSize; ++i) moveLetter(-sign);
++      return found;
++}
++
++/// Highlight all found strings on the current screen.
++static void highlightStringOnScreen(void) {
++      if (isEmpty(&searchString)) { return; }
++      empty(&highlights);
++      uint32_t const searchStringSize = size(&searchString);
++      uint32_t findIdx = 0;
++      uint32_t xStart, yStart;
++      bool success = true;
++      for (int y = 0; y < term.row && success; y++) {
++              for (int x = 0; x < term.col && success; x++) {
++                      char const* const SEC(next,
++                                      view(&searchString,findIdx),,)
++                      if (TLINE(y)[x].u == (Rune) *((uint32_t*)(next))) {
++                              if (++findIdx == 1) {
++                                      xStart = x;
++                                      yStart = y;
++                              }
++                              if (findIdx == searchStringSize) {
++                                      success = success
++                                              && append(&highlights, &xStart)
++                                              && append(&highlights, &yStart);
++                                      findIdx = 0; //term.dirty[yStart] = 1;
++                              }
++                      } else { findIdx = 0; }
++              }
++      }
++      if (!success) { empty(&highlights); }
++}
++
++static bool gotoStringAndHighlight(int8_t sign) {
++              // Find hte next occurrence of the #searchString in direction 
#sign
++      bool const found = gotoString(sign);
++      if (!found) {  applyPosition(&stateVB.motion.searchPosition); }
++      highlightStringOnScreen();
++      //tsetdirt(0, term.row-3); //< everything except for the 'status bar'
++      return found;
++}
++
++static bool pressKeys(char const* nullTerminatedString, size_t end) {
++        bool sc = true;
++      for (size_t i = 0; i < end && sc; ++i) {
++              sc = kpressNormalMode(&nullTerminatedString[i], 1, false, NULL);
++      }
++      return sc;
++}
++
++static bool executeCommand(DynamicArray const *command) {
++      size_t end=size(command);
++      char decoded [32];
++      bool succ = true;
++      size_t len;
++      for (size_t i = 0; i < end && succ; ++i) {
++              char const *const SEC(nextRune, view(command, i),,false)
++              len = utf8encode(*((Rune *) nextRune), decoded);
++              succ = kpressNormalMode(decoded, len, false, NULL);
++      }
++      return succ;
++}
++
++struct { char const first; char const second; } const Brackets [] =
++{ {'(', ')'}, {'<', '>'}, {'{', '}'}, {'[', ']'}, };
++
++
++/// Emits Command prefix and suffix when i motion is performed (e.g. yiw).
++///
++/// @param c:             motion character
++/// @param expandMode:    1 for 'i', 2 for 'a'
++/// @param first, second: Dynamic arrays in which the prefix and postfix
++///                       commands will be returned
++/// @return               whether the command could be extracted successfully.
++static bool expandExpression(char const c, enum Infix expandMode,
++              char operation, DynamicArray *cmd) {
++      empty(cmd);
++      bool s = true; //< used in order to detect memory allocation errors.
++      char const lower = tolower(c);
++      // Motions
++      if (lower == 'w') {
++              // translated into wb[command]e resp. WB[command]E, which works
++              // file even when at the fist letter. Does not work for single
++              // letter words though.
++              int const diff = c - lower;
++              s = s && checkSetNextV(cmd, c);
++              s = s && checkSetNextV(cmd, (signed char)(((int)'b') + diff));
++              s = s && checkSetNextV(cmd, operation);
++              s = s && checkSetNextV(cmd, (signed char)(((int)'e')+ diff));
++              return s;
++      }
++      // Symmetrical brackets (quotation marks)
++      if (c == '\'' || c == '"') {
++              // Local ambiguity -> do nothing. It cannot be determined if
++              // the current char is the 1st or last char of the selection.
++              //  <---- search here? -- ['] -- or search here? --->
++              if (TLINE(term.c.y)[term.c.x].u == c) {
++                      return false;
++              }
++              // Prefix
++              char res [] = {'?', c, '
'};
++              s = s && checkSetNextP(cmd, res);
++              // infix
++              bool const iffy = expandMode == infix_i;
++              if (iffy) { s = s && checkSetNextV(cmd, 'l'); }
++              s = s && checkSetNextV(cmd, operation);
++              if (!iffy) { s = s && checkSetNextV(cmd, 'l'); }
++              // suffix
++              res[0] = '/';
++              s = s && checkSetNextP(cmd, res);
++              if (iffy) { s = s && checkSetNextV(cmd, 'h'); }
++              return s;
++      }
++      // Brackets: Does not if in range / if the brackets belong togehter.
++      for (size_t pid = 0; pid < sizeof(Brackets); ++pid) {
++              if(Brackets[pid].first == c || Brackets[pid].second == c) {
++                      if (TLINE(term.c.y)[term.c.x].u!=Brackets[pid].first) {
++                              s = s && checkSetNextV(cmd, '?');
++                              s = s && checkSetNextV(cmd, 
Brackets[pid].first);
++                              s = s && checkSetNextV(cmd, '
');
++                      }
++                      bool const iffy = expandMode == infix_i;
++                      if (iffy) { s = s && checkSetNextV(cmd, 'l'); }
++                      s = s && checkSetNextV(cmd, operation);
++                      if (!iffy) { s = s && checkSetNextV(cmd, 'l'); }
++                      s = s && checkSetNextV(cmd, '/');
++                      s = s && checkSetNextV(cmd, Brackets[pid].second);
++                      s = s && checkSetNextV(cmd, '
');
++                      if (iffy) { s = s && checkSetNextV(cmd, 'h'); }
++                      return s;
++              }
++      }
++      /**/
++      // search string
++      // complicated search operation: <tag>
++      if (c == 't') {
++              // XXX: (Bug in vim: @vit )
++              // <tag_name attr="hier" a2="\<sch\>"> [current pos] </tag_name>
++
++              // 1. Copy history ( tag := hist[?<
:/ 
] )
++              // 2. Copy history ( first_find := hist[?<
: next place in
++              //                   history where count '>' > count '<'
++              //                   (can be behind current pos) )
++              // 3. first := [?first_find][#first_ind]l
++              //    second:= [/tag">"]h
++              //return true; // XXX: not implmented yet.
++      }
++      return false;
++}
++
++//
++// Public API
++//
++
++void onMove(void) {
++      stateVB.initialPosition.x = term.c.x;
++      stateVB.initialPosition.y = term.c.y;
++      stateVB.initialPosition.yScr = term.scr;
++}
++
++int highlighted(int x, int y) {
++      // Compute the legal bounds for a hit:
++      int32_t const stringSize = size(&searchString);
++      int32_t xMin = x - stringSize;
++      int32_t yMin = y;
++      while (xMin < 0 && yMin > 0) {
++              xMin += term.col;
++              --yMin;
++      }
++      if (xMin < 0) { xMin = 0; }
++
++      uint32_t highSize = size(&highlights);
++      ENSURE(highSize % 2 == 0, empty(&highlights); return false;);
++      highSize /= 2;
++      uint32_t *ptr = (uint32_t*) highlights.content;
++      for (uint32_t i = 0; i < highSize; ++i) {
++              int32_t const sx = (int32_t) *(ptr++);
++              int32_t const sy = (int32_t) *(ptr++);
++              if (BETWEEN(sy, yMin, y) && (sy != yMin || sx > xMin)
++                              && (sy != y || sx <= x)) {
++                      return true;
++              }
++      }
++      return false;
++}
++
++ExitState kpressNormalMode(char const * cs, int len, bool ctrl, void const 
*v) {
++      KeySym const * const ksym = (KeySym*) v;
++      bool const esc = ksym &&  *ksym == XK_Escape;
++      bool const enter = (ksym && *ksym==XK_Return) || (len==1 &&cs[0]=='
');
++      bool const quantifier = len == 1 && (BETWEEN(cs[0], 49, 57)
++                      || (cs[0] == 48 && stateVB.motion.amount));
++      int const previousScroll = term.scr;
++      // [ESC] or [ENTER] abort resp. finish the current level of operation.
++      // Typing 'i' if no operation is currently performed behaves like ESC.
++      if (esc || enter || (len == 1 && cs[0] == 'i' && isMotionFinished()
++                              && isOperationFinished())) {
++              if (terminateCommand(!enter)) {
++                      applyPosition(&stateVB.initialPosition);
++                      Position const pc = stateVB.initialPosition;
++                      stateVB = defaultNormalMode;
++                      stateVB.initialPosition = pc;
++                      tfulldirt();
++                      return finished;
++              }
++              len = 0;
++              goto motionFinish;
++      }
++      // Backspace
++      if (ksym && *ksym == XK_BackSpace) {
++              bool s = stateVB.motion.search!=none&&!stateVB.motion.finished;
++              bool q = stateVB.motion.amount != 0;
++              if (!(s || q)) { return failed; }
++              len = 0;
++
++              if (!isEmpty(currentCommand)) { pop(currentCommand); }
++              if (s) {
++                      if (!isEmpty(&searchString)) { pop(&searchString); }
++                      else if (isEmpty(&searchString)) {
++                              exitCommand();
++                              return success;
++                      }
++              } else if (q) {
++                      stateVB.motion.amount /= 10;
++                      goto finishNoAppend;
++              }
++      }
++
++      // Search: append to search string, then search & highlight
++      if (stateVB.motion.search != none && !stateVB.motion.finished) {
++              if (len >= 1) {
++                      EXPAND(kSearch, &searchString, true)
++                      utf8decode(cs, (Rune*)(kSearch), len);
++              }
++              applyPosition(&stateVB.motion.searchPosition);
++              gotoStringAndHighlight(getSearchDirection());
++              goto finish;
++      }
++      if (len == 0) { return failed; }
++      // Quantifiers
++      if (quantifier) {
++              stateVB.motion.amount = min(SHRT_MAX,
++                              stateVB.motion.amount * 10 + cs[0] - 48);
++              goto finish;
++      }
++      // 'i' mode enabled, hence the expression is to be expanded:
++      // [start_expression(cs[0])] [operation] [stop_expression(cs[0])]
++      if (stateVB.command.infix != infix_none && stateVB.command.op != noop) {
++              DynamicArray cmd = CHAR_ARRAY;
++              char const operation = stateVB.command.op;
++              bool succ = expandExpression(cs[0],
++                              stateVB.command.infix, visual, &cmd);
++              if (operation == yank) {
++                      succ = succ && checkSetNextV(&cmd, operation);
++              }
++              NormalModeState const st = stateVB;
++              TCursor         const tc = term.c;
++              stateVB.command.infix    = infix_none;
++              if (succ) {
++                      stateVB.command.op = noop;
++                      for (int i = 0; i < size(&cmd) && succ; ++i) {
++                              succ = pressKeys(&cmd.content[i], 1);
++                      }
++                      if (!succ) { // go back to the old position, apply op
++                              stateVB = st;
++                              term.c = tc;
++                      }
++                      empty(currentCommand);
++                      for (uint32_t i = 0; i < size(&cmd); ++i) {
++                              EXPAND(kCommand, currentCommand, true)
++                              utf8decode(cmd.content+i, (Rune*)(kCommand),1);
++                      }
++              }
++              free(cmd.content);
++              goto finishNoAppend;
++      }
++      // Commands (V / v or y)
++      switch(cs[0]) {
++              case '.':
++              {
++                      if (isEmpty(currentCommand)) { toggle = !toggle; }
++                      DynamicArray cmd = UTF8_ARRAY;
++                      swap(&cmd, currentCommand);
++                      executeCommand(&cmd) ? success : failed;
++                      swap(&cmd, currentCommand);
++                      free(cmd.content);
++                      goto finishNoAppend;
++              }
++              case 'i': stateVB.command.infix = infix_i; goto finish;
++              case 'a': stateVB.command.infix = infix_a; goto finish;
++              case 'y':
++                      switch(stateVB.command.op) {
++                              case noop: //< Start yank mode & set #op
++                                      enableOperation(yank);
++                                      selstart(term.c.x, term.c.y,term.scr,0);
++                                      goto finish;
++                              case yank: //< Complete yank [y#amount j]
++                                      selstart(0, term.c.y, term.scr, 0);
++                                      int const origY = term.c.y;
++                                      moveLine(max(stateVB.motion.amount, 1));
++                                      selextend(term.col-1,term.c.y,term.scr,
++                                                      SEL_RECTANGULAR, 0);
++                                      term.c.y = origY;
++                                      FALLTHROUGH
++                              case visualLine: // Yank visual selection
++                              case visual:
++                                      xsetsel(getsel());
++                                      xclipcopy();
++                                      exitCommand();
++                                      goto finish;
++                              default:
++                                      return failed;
++                      }
++              case visual:
++              case visualLine:
++                      if (stateVB.command.op == cs[0]) {
++                              finishOperation();
++                              return true;
++                      } else {
++                              enableOperation(cs[0]);
++                              selstart(cs[0] == visualLine ? 0 : term.c.x,
++                                              term.c.y, term.scr, 0);
++                              goto finish;
++                      }
++      }
++      // CTRL Motions
++      int32_t sign = -1;    //< if command goes 'forward'(1) or 'backward'(-1)
++      if (ctrl) {
++              if (ksym == NULL) { return false; }
++              switch(*ksym) {
++                      case XK_f:
++                              term.scr = max(term.scr - max(term.row-2,1), 0);
++                              term.c.y = 0;
++                              goto finish;
++                      case XK_b:
++                              term.scr = min(term.scr + max(term.row - 2, 1),
++                                              HISTSIZE - 1);
++                              term.c.y = term.bot;
++                              goto finish;
++                      case XK_u:
++                              term.scr = min(term.scr+term.row/2, HISTSIZE-1);
++                              goto finish;
++                      case XK_d:
++                              term.scr = max(term.scr - term.row / 2, 0);
++                              goto finish;
++                      default: return false;
++              }
++      }
++      // Motions
++      switch(cs[0]) {
++              case 'c': empty(&commandHist0); empty(&commandHist1);
++                        goto finishNoAppend;
++              case 'j': sign = 1; FALLTHROUGH
++              case 'k': moveLine(max(stateVB.motion.amount,1) * sign);
++                        goto motionFinish;
++              case 'H': term.c.y = 0;
++                        goto motionFinish;
++              case 'M': term.c.y = term.bot / 2;
++                        goto motionFinish;
++              case 'L': term.c.y = term.bot;
++                        goto motionFinish;
++              case 'G': applyPosition(&stateVB.initialPosition);
++                        goto motionFinish;
++              case 'l': sign = 1; FALLTHROUGH
++              case 'h': moveLetter(sign * max(stateVB.motion.amount,1));
++                        goto motionFinish;
++              case '0': term.c.x = 0;
++                        goto motionFinish;
++              case '$': term.c.x = term.col-1;
++                        goto motionFinish;
++              case 'w': FALLTHROUGH
++              case 'W': FALLTHROUGH
++              case 'e': FALLTHROUGH
++              case 'E': sign = 1; FALLTHROUGH
++              case 'B': FALLTHROUGH
++              case 'b': {
++                      char const * const wDelim =
++                              cs[0] <= 90 ? wordDelimLarge : wordDelimSmall;
++                      uint32_t const wDelimLen = strlen(wDelim);
++
++                      bool const startSpaceIsSeparator =
++                              !(cs[0] == 'w' || cs[0] == 'W');
++                      // Whether to start & end with offset:
++                      bool const performOffset = startSpaceIsSeparator;
++                      // Max iteration := One complete hist traversal.
++                      uint32_t const maxIter = (HISTSIZE+term.row) * term.col;
++                      // Doesn't work exactly as in vim: Linebreak is
++                      // counted as 'normal' separator, hence a jump can
++                      // span multiple lines here.
++                      stateVB.motion.amount = max(stateVB.motion.amount, 1);
++                      for (;stateVB.motion.amount>0;--stateVB.motion.amount) {
++                              uint8_t state = 0;
++                              if (performOffset) { moveLetter(sign); }
++                              for (uint32_t cIt = 0; cIt ++ < maxIter; 
moveLetter(sign)) {
++                                      if (startSpaceIsSeparator == 
contains(TLINE(term.c.y)[term.c.x].u, wDelim, wDelimLen)) {
++                                              if (state == 1) {
++                                                      if (performOffset) {
++                                                              
moveLetter(-sign);
++                                                      }
++                                                      break;
++                                              }
++                                      } else if (state == 0) { state = 1; }
++                              }
++                      }
++                      goto motionFinish;
++              }
++              case '/': sign = 1; FALLTHROUGH
++              case '?':
++                        empty(&searchString);
++                        stateVB.motion.search = sign == 1 ? forward : 
backward;
++                        stateVB.motion.searchPosition.x = term.c.x;
++                        stateVB.motion.searchPosition.y = term.c.y;
++                        stateVB.motion.searchPosition.yScr = term.scr;
++                        stateVB.motion.finished = false;
++                        goto finish;
++              case 'n': sign = 1; FALLTHROUGH
++              case 'N': {
++                      if (stateVB.motion.search == none) return failed;
++                      if (stateVB.motion.search == backward) { sign *= -1; }
++                      bool b = true; int ox = term.c.x;
++                      int oy = term.c.y ; int scr = term.scr;
++                      int32_t i = max(stateVB.motion.amount, 1);
++                      for (;i>0 && (b=gotoString(sign)); --i) {
++                          oy = term.c.y; scr = term.scr;
++                      }
++                      if (!b) { term.c.x = ox; term.c.y = oy; term.scr = scr;}
++                      goto motionFinish;
++              }
++              case 't': // Toggle selection mode and set dirt.
++                        sel.type = sel.type == SEL_REGULAR
++                                ? SEL_RECTANGULAR : SEL_REGULAR;
++                        //tsetdirt(sel.nb.y, sel.ne.y);
++                        goto motionFinish;
++      }
++      // Custom commands
++      for (size_t i = 0; i < amountNormalModeShortcuts; ++i) {
++              if (cs[0] == normalModeShortcuts[i].key) {
++                      return pressKeys(normalModeShortcuts[i].value,
++                                      strlen(normalModeShortcuts[i].value))
++                                      ? success : failed;
++              }
++      }
++      return failed;
++motionFinish:
++      stateVB.motion.amount = 0;
++      //if (isMotionFinished() && stateVB.command.op == yank) {
++      if (stateVB.command.op == yank) {
++              selextend(term.c.x, term.c.y, term.scr, sel.type, 0);
++              xsetsel(getsel());
++              xclipcopy();
++              exitCommand();
++      }
++finish:
++      if (len == 1 && !ctrl) { // XXX: for now.
++              EXPAND(kCommand, currentCommand, true)
++              utf8decode(cs, (Rune*)(kCommand), len);
++      }
++finishNoAppend:
++      if (stateVB.command.op == visual) {
++              selextend(term.c.x, term.c.y, term.scr, sel.type, 0);
++      } else if (stateVB.command.op == visualLine) {
++              selextend(term.col-1, term.c.y, term.scr, sel.type, 0);
++      }
++
++      if (previousScroll != term.scr && !isEmpty(&searchString)) {
++              highlightStringOnScreen();
++      }
++      tsetdirt(0, term.row-3); //< Required because of the cursor cross.
++      printCommandString();
++      printSearchString();
++      return success;
++}
+diff --git a/normalMode.h b/normalMode.h
+new file mode 100644
+index 0000000..7d88259
+--- /dev/null
++++ b/normalMode.h
+@@ -0,0 +1,36 @@
++/* See LICENSE for license details. */
++#ifndef NORMAL_MODE_H
++#define NORMAL_MODE_H
++
++#include <stdbool.h>
++#include <stddef.h>
++#include <stdint.h>
++
++/// Used in the configuration file to define custom shortcuts.
++typedef struct NormalModeShortcuts {
++      char key;
++      char *value;
++} NormalModeShortcuts;
++
++/// Holds the exit status of the #kpressNormalMode function, which informs the
++/// caller when to exit normal mode.
++typedef enum ExitState {
++      failed = 0,
++      success = 1,
++      finished = 2,
++} ExitState;
++
++/// Called when curr position is altered.
++void onMove(void);
++
++/// Function which returns whether the value at position provided as arguments
++/// is to be highlighted.
++int highlighted(int, int);
++
++/// Handles keys in normal mode.
++ExitState kpressNormalMode(char const * decoded, int len, bool ctrlPressed,
++              void const * ksym);
++              //bool esc, bool enter, bool backspace, void* keysym);
++
++
++#endif // NORMAL_MODE_H
+diff --git a/st.c b/st.c
+index 3e48410..d8bf7ab 100644
+--- a/st.c
++++ b/st.c
+@@ -1,8 +1,10 @@
+ /* See LICENSE for license details. */
++#include <assert.h>
+ #include <ctype.h>
+ #include <errno.h>
+ #include <fcntl.h>
+ #include <limits.h>
++#include <math.h>
+ #include <pwd.h>
+ #include <stdarg.h>
+ #include <stdio.h>
+@@ -17,6 +19,8 @@
+ #include <unistd.h>
+ #include <wchar.h>
+ 
++
++#include "term.h"
+ #include "st.h"
+ #include "win.h"
+ 
+@@ -42,6 +46,7 @@
+ #define ISCONTROLC1(c)                (BETWEEN(c, 0x80, 0x9f))
+ #define ISCONTROL(c)          (ISCONTROLC0(c) || ISCONTROLC1(c))
+ #define ISDELIM(u)            (u && wcschr(worddelimiters, u))
++#define INTERVAL(x, a, b)     (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
+ 
+ enum term_mode {
+       MODE_WRAP        = 1 << 0,
+@@ -86,51 +91,6 @@ enum escape_state {
+       ESC_DCS        =128,
+ };
+ 
+-typedef struct {
+-      Glyph attr; /* current char attributes */
+-      int x;
+-      int y;
+-      char state;
+-} TCursor;
+-
+-typedef struct {
+-      int mode;
+-      int type;
+-      int snap;
+-      /*
+-       * Selection variables:
+-       * nb – normalized coordinates of the beginning of the selection
+-       * ne – normalized coordinates of the end of the selection
+-       * ob – original coordinates of the beginning of the selection
+-       * oe – original coordinates of the end of the selection
+-       */
+-      struct {
+-              int x, y;
+-      } nb, ne, ob, oe;
+-
+-      int alt;
+-} Selection;
+-
+-/* Internal representation of the screen */
+-typedef struct {
+-      int row;      /* nb row */
+-      int col;      /* nb col */
+-      Line *line;   /* screen */
+-      Line *alt;    /* alternate screen */
+-      int *dirty;   /* dirtyness of lines */
+-      TCursor c;    /* cursor */
+-      int ocx;      /* old cursor col */
+-      int ocy;      /* old cursor row */
+-      int top;      /* top    scroll limit */
+-      int bot;      /* bottom scroll limit */
+-      int mode;     /* terminal mode flags */
+-      int esc;      /* escape state flags */
+-      char trantbl[4]; /* charset table translation */
+-      int charset;  /* current charset */
+-      int icharset; /* selected charset for sequence */
+-      int *tabs;
+-} Term;
+-
+ /* CSI Escape sequence structs */
+ /* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */
+ typedef struct {
+@@ -153,6 +113,8 @@ typedef struct {
+       int narg;              /* nb of args */
+ } STREscape;
+ 
++void tfulldirt(void);
++
+ static void execsh(char *, char **);
+ static void stty(char **);
+ static void sigchld(int);
+@@ -185,16 +147,14 @@ static void tnewline(int);
+ static void tputtab(int);
+ static void tputc(Rune);
+ static void treset(void);
+-static void tscrollup(int, int);
+-static void tscrolldown(int, int);
++static void tscrollup(int, int, int);
++static void tscrolldown(int, int, int);
+ static void tsetattr(int *, int);
+ static void tsetchar(Rune, Glyph *, int, int);
+-static void tsetdirt(int, int);
+ static void tsetscroll(int, int);
+ static void tswapscreen(void);
+ static void tsetmode(int, int, int *, int);
+ static int twrite(const char *, int, int);
+-static void tfulldirt(void);
+ static void tcontrolcode(uchar );
+ static void tdectest(char );
+ static void tdefutf8(char);
+@@ -208,8 +168,6 @@ static void selnormalize(void);
+ static void selscroll(int, int);
+ static void selsnap(int *, int *, int);
+ 
+-static size_t utf8decode(const char *, Rune *, size_t);
+-static Rune utf8decodebyte(char, size_t *);
+ static char utf8encodebyte(Rune, size_t);
+ static size_t utf8validate(Rune *, size_t);
+ 
+@@ -219,8 +177,8 @@ static char base64dec_getc(const char **);
+ static ssize_t xwrite(int, const char *, size_t);
+ 
+ /* Globals */
+-static Term term;
+-static Selection sel;
++Term term;
++Selection sel;
+ static CSIEscape csiescseq;
+ static STREscape strescseq;
+ static int iofd = 1;
+@@ -414,17 +372,22 @@ tlinelen(int y)
+ {
+       int i = term.col;
+ 
+-      if (term.line[y][i - 1].mode & ATTR_WRAP)
++      if (TLINE(y)[i - 1].mode & ATTR_WRAP)
+               return i;
+ 
+-      while (i > 0 && term.line[y][i - 1].u == ' ')
++      while (i > 0 && TLINE(y)[i - 1].u == ' ')
+               --i;
+ 
+       return i;
+ }
+ 
+ void
+-selstart(int col, int row, int snap)
++xselstart(int col, int row, int snap) {
++      selstart(col, row, term.scr, snap);
++}
++
++void
++selstart(int col, int row, int scroll, int snap)
+ {
+       selclear();
+       sel.mode = SEL_EMPTY;
+@@ -433,6 +396,7 @@ selstart(int col, int row, int snap)
+       sel.snap = snap;
+       sel.oe.x = sel.ob.x = col;
+       sel.oe.y = sel.ob.y = row;
++      sel.oe.scroll = sel.ob.scroll = scroll;
+       selnormalize();
+ 
+       if (sel.snap != 0)
+@@ -441,10 +405,13 @@ selstart(int col, int row, int snap)
+ }
+ 
+ void
+-selextend(int col, int row, int type, int done)
+-{
+-      int oldey, oldex, oldsby, oldsey, oldtype;
++xselextend(int col, int row, int type, int done) {
++      selextend(col, row, term.scr, type, done);
++}
+ 
++void
++selextend(int col, int row, int scroll, int type, int done)
++{
+       if (sel.mode == SEL_IDLE)
+               return;
+       if (done && sel.mode == SEL_EMPTY) {
+@@ -452,18 +419,22 @@ selextend(int col, int row, int type, int done)
+               return;
+       }
+ 
+-      oldey = sel.oe.y;
+-      oldex = sel.oe.x;
+-      oldsby = sel.nb.y;
+-      oldsey = sel.ne.y;
+-      oldtype = sel.type;
++      int const oldey = sel.oe.y;
++      int const oldex = sel.oe.x;
++      int const oldscroll = sel.oe.scroll;
++      int const oldsby = sel.nb.y;
++      int const oldsey = sel.ne.y;
++      int const oldtype = sel.type;
+ 
+       sel.oe.x = col;
+       sel.oe.y = row;
++      sel.oe.scroll = scroll;
++
+       selnormalize();
+       sel.type = type;
+ 
+-      if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || 
sel.mode == SEL_EMPTY)
++      if (oldey != sel.oe.y || oldex != sel.oe.x || oldscroll != sel.oe.scroll
++                      || oldtype != sel.type || sel.mode == SEL_EMPTY)
+               tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
+ 
+       sel.mode = done ? SEL_IDLE : SEL_READY;
+@@ -472,17 +443,21 @@ selextend(int col, int row, int type, int done)
+ void
+ selnormalize(void)
+ {
+-      int i;
+-
+-      if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {
+-              sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
+-              sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
++      sel.nb.y = INTERVAL(sel.ob.y + term.scr - sel.ob.scroll, 0, term.bot);
++      sel.ne.y = INTERVAL(sel.oe.y + term.scr - sel.oe.scroll, 0, term.bot);
++      if (sel.type == SEL_REGULAR && sel.nb.y != sel.ne.y) {
++              sel.nb.x = sel.nb.y < sel.ne.y ? sel.ob.x : sel.oe.x;
++              sel.ne.x = sel.nb.y < sel.ne.y ? sel.oe.x : sel.ob.x;
+       } else {
+               sel.nb.x = MIN(sel.ob.x, sel.oe.x);
+               sel.ne.x = MAX(sel.ob.x, sel.oe.x);
+       }
+-      sel.nb.y = MIN(sel.ob.y, sel.oe.y);
+-      sel.ne.y = MAX(sel.ob.y, sel.oe.y);
++
++      if (sel.nb.y > sel.ne.y) {
++              int32_t const tmp = sel.nb.y;
++              sel.nb.y = sel.ne.y;
++              sel.ne.y = tmp;
++      }
+ 
+       selsnap(&sel.nb.x, &sel.nb.y, -1);
+       selsnap(&sel.ne.x, &sel.ne.y, +1);
+@@ -490,7 +465,7 @@ selnormalize(void)
+       /* expand selection over line breaks */
+       if (sel.type == SEL_RECTANGULAR)
+               return;
+-      i = tlinelen(sel.nb.y);
++      int i = tlinelen(sel.nb.y);
+       if (i < sel.nb.x)
+               sel.nb.x = i;
+       if (tlinelen(sel.ne.y) <= sel.ne.x)
+@@ -526,7 +501,7 @@ selsnap(int *x, int *y, int direction)
+                * Snap around if the word wraps around at the end or
+                * beginning of a line.
+                */
+-              prevgp = &term.line[*y][*x];
++              prevgp = &TLINE(*y)[*x];
+               prevdelim = ISDELIM(prevgp->u);
+               for (;;) {
+                       newx = *x + direction;
+@@ -541,14 +516,14 @@ selsnap(int *x, int *y, int direction)
+                                       yt = *y, xt = *x;
+                               else
+                                       yt = newy, xt = newx;
+-                              if (!(term.line[yt][xt].mode & ATTR_WRAP))
++                              if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
+                                       break;
+                       }
+ 
+                       if (newx >= tlinelen(newy))
+                               break;
+ 
+-                      gp = &term.line[newy][newx];
++                      gp = &TLINE(newy)[newx];
+                       delim = ISDELIM(gp->u);
+                       if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
+                                       || (delim && gp->u != prevgp->u)))
+@@ -569,14 +544,14 @@ selsnap(int *x, int *y, int direction)
+               *x = (direction < 0) ? 0 : term.col - 1;
+               if (direction < 0) {
+                       for (; *y > 0; *y += direction) {
+-                              if (!(term.line[*y-1][term.col-1].mode
++                              if (!(TLINE(*y-1)[term.col-1].mode
+                                               & ATTR_WRAP)) {
+                                       break;
+                               }
+                       }
+               } else if (direction > 0) {
+                       for (; *y < term.row-1; *y += direction) {
+-                              if (!(term.line[*y][term.col-1].mode
++                              if (!(TLINE(*y)[term.col-1].mode
+                                               & ATTR_WRAP)) {
+                                       break;
+                               }
+@@ -596,24 +571,32 @@ getsel(void)
+       if (sel.ob.x == -1)
+               return NULL;
+ 
+-      bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ;
++      int32_t syb = sel.ob.y - sel.ob.scroll + term.scr;
++      int32_t sye = sel.oe.y - sel.oe.scroll + term.scr;
++      if (syb > sye) {
++              int32_t tmp = sye;
++              sye = syb;
++              syb = tmp;
++      }
++
++      bufsize = (term.col+1) * (sye - syb + 1) * UTF_SIZ;
+       ptr = str = xmalloc(bufsize);
+ 
+       /* append every set & selected glyph to the selection */
+-      for (y = sel.nb.y; y <= sel.ne.y; y++) {
++      for (y = syb; y <= sye; y++) {
+               if ((linelen = tlinelen(y)) == 0) {
+                       *ptr++ = '
';
+                       continue;
+               }
+ 
+               if (sel.type == SEL_RECTANGULAR) {
+-                      gp = &term.line[y][sel.nb.x];
++                      gp = &TLINE(y)[sel.nb.x];
+                       lastx = sel.ne.x;
+               } else {
+-                      gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
+-                      lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
++                      gp = &TLINE(y)[syb == y ? sel.nb.x : 0];
++                      lastx = (sye == y) ? sel.ne.x : term.col-1;
+               }
+-              last = &term.line[y][MIN(lastx, linelen-1)];
++              last = &TLINE(y)[MIN(lastx, linelen-1)];
+               while (last >= gp && last->u == ' ')
+                       --last;
+ 
+@@ -836,6 +819,9 @@ void
+ ttywrite(const char *s, size_t n, int may_echo)
+ {
+       const char *next;
++      Arg arg = (Arg) { .i = term.scr };
++
++      kscrolldown(&arg);
+ 
+       if (may_echo && IS_SET(MODE_ECHO))
+               twrite(s, n, 1);
+@@ -1047,13 +1033,53 @@ tswapscreen(void)
+ }
+ 
+ void
+-tscrolldown(int orig, int n)
++kscrolldown(const Arg* a)
++{
++      int n = a->i;
++
++      if (n < 0)
++              n = term.row + n;
++
++      if (n > term.scr)
++              n = term.scr;
++
++      if (term.scr > 0) {
++              term.scr -= n;
++              selscroll(0, -n);
++              tfulldirt();
++      }
++}
++
++void
++kscrollup(const Arg* a)
++{
++      int n = a->i;
++
++      if (n < 0)
++              n = term.row + n;
++
++      if (term.scr <= HISTSIZE-n) {
++              term.scr += n;
++              selscroll(0, n);
++              tfulldirt();
++      }
++}
++
++void
++tscrolldown(int orig, int n, int copyhist)
+ {
+       int i;
+       Line temp;
+ 
+       LIMIT(n, 0, term.bot-orig+1);
+ 
++      if (copyhist) {
++              term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE;
++              temp = term.hist[term.histi];
++              term.hist[term.histi] = term.line[term.bot];
++              term.line[term.bot] = temp;
++      }
++
+       tsetdirt(orig, term.bot-n);
+       tclearregion(0, term.bot-n+1, term.col-1, term.bot);
+ 
+@@ -1067,13 +1093,23 @@ tscrolldown(int orig, int n)
+ }
+ 
+ void
+-tscrollup(int orig, int n)
++tscrollup(int orig, int n, int copyhist)
+ {
+       int i;
+       Line temp;
+ 
+       LIMIT(n, 0, term.bot-orig+1);
+ 
++      if (copyhist) {
++              term.histi = (term.histi + 1) % HISTSIZE;
++              temp = term.hist[term.histi];
++              term.hist[term.histi] = term.line[orig];
++              term.line[orig] = temp;
++      }
++
++      if (term.scr > 0 && term.scr < HISTSIZE)
++              term.scr = MIN(term.scr + n, HISTSIZE-1);
++
+       tclearregion(0, orig, term.col-1, orig+n-1);
+       tsetdirt(orig+n, term.bot);
+ 
+@@ -1093,6 +1129,7 @@ selscroll(int orig, int n)
+               return;
+ 
+       if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, 
term.bot)) {
++              sel.oe.scroll = sel.ob.scroll = term.scr;
+               if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) {
+                       selclear();
+                       return;
+@@ -1122,13 +1159,19 @@ tnewline(int first_col)
+       int y = term.c.y;
+ 
+       if (y == term.bot) {
+-              tscrollup(term.top, 1);
++              tscrollup(term.top, 1, 1);
+       } else {
+               y++;
+       }
+       tmoveto(first_col ? 0 : term.c.x, y);
+ }
+ 
++int
++currentLine(int x, int y)
++{
++      return (x == term.c.x || y == term.c.y);
++}
++
+ void
+ csiparse(void)
+ {
+@@ -1181,6 +1224,8 @@ tmoveto(int x, int y)
+       term.c.state &= ~CURSOR_WRAPNEXT;
+       term.c.x = LIMIT(x, 0, term.col-1);
+       term.c.y = LIMIT(y, miny, maxy);
++      // Set the last position in order to restore after normal mode exits.
++      onMove();
+ }
+ 
+ void
+@@ -1287,14 +1332,14 @@ void
+ tinsertblankline(int n)
+ {
+       if (BETWEEN(term.c.y, term.top, term.bot))
+-              tscrolldown(term.c.y, n);
++              tscrolldown(term.c.y, n, 0);
+ }
+ 
+ void
+ tdeleteline(int n)
+ {
+       if (BETWEEN(term.c.y, term.top, term.bot))
+-              tscrollup(term.c.y, n);
++              tscrollup(term.c.y, n, 0);
+ }
+ 
+ int32_t
+@@ -1725,11 +1770,11 @@ csihandle(void)
+               break;
+       case 'S': /* SU -- Scroll <n> line up */
+               DEFAULT(csiescseq.arg[0], 1);
+-              tscrollup(term.top, csiescseq.arg[0]);
++              tscrollup(term.top, csiescseq.arg[0], 0);
+               break;
+       case 'T': /* SD -- Scroll <n> line down */
+               DEFAULT(csiescseq.arg[0], 1);
+-              tscrolldown(term.top, csiescseq.arg[0]);
++              tscrolldown(term.top, csiescseq.arg[0], 0);
+               break;
+       case 'L': /* IL -- Insert <n> blank lines */
+               DEFAULT(csiescseq.arg[0], 1);
+@@ -2235,7 +2280,7 @@ eschandle(uchar ascii)
+               return 0;
+       case 'D': /* IND -- Linefeed */
+               if (term.c.y == term.bot) {
+-                      tscrollup(term.top, 1);
++                      tscrollup(term.top, 1, 1);
+               } else {
+                       tmoveto(term.c.x, term.c.y+1);
+               }
+@@ -2248,7 +2293,7 @@ eschandle(uchar ascii)
+               break;
+       case 'M': /* RI -- Reverse index */
+               if (term.c.y == term.top) {
+-                      tscrolldown(term.top, 1);
++                      tscrolldown(term.top, 1, 1);
+               } else {
+                       tmoveto(term.c.x, term.c.y-1);
+               }
+@@ -2290,7 +2335,7 @@ tputc(Rune u)
+ {
+       char c[UTF_SIZ];
+       int control;
+-      int width, len;
++      int width = 0, len;
+       Glyph *gp;
+ 
+       control = ISCONTROL(u);
+@@ -2469,7 +2514,7 @@ twrite(const char *buf, int buflen, int show_ctrl)
+ void
+ tresize(int col, int row)
+ {
+-      int i;
++      int i, j;
+       int minrow = MIN(row, term.row);
+       int mincol = MIN(col, term.col);
+       int *bp;
+@@ -2506,6 +2551,14 @@ tresize(int col, int row)
+       term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
+       term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
+ 
++      for (i = 0; i < HISTSIZE; i++) {
++              term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph));
++              for (j = mincol; j < col; j++) {
++                      term.hist[i][j] = term.c.attr;
++                      term.hist[i][j].u = ' ';
++              }
++      }
++
+       /* resize each row to new width, zero-pad if needed */
+       for (i = 0; i < minrow; i++) {
+               term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
+@@ -2563,7 +2616,7 @@ drawregion(int x1, int y1, int x2, int y2)
+                       continue;
+ 
+               term.dirty[y] = 0;
+-              xdrawline(term.line[y], x1, y, x2);
++              xdrawline(TLINE(y), x1, y, x2);
+       }
+ }
+ 
+@@ -2584,8 +2637,8 @@ draw(void)
+               cx--;
+ 
+       drawregion(0, 0, term.col, term.row);
+-      xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
+-                      term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
++      xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
++                      term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
+       term.ocx = cx, term.ocy = term.c.y;
+       xfinishdraw();
+       xximspot(term.ocx, term.ocy);
+diff --git a/st.h b/st.h
+index a1928ca..3aad9f3 100644
+--- a/st.h
++++ b/st.h
+@@ -1,5 +1,8 @@
+ /* See LICENSE for license details. */
+ 
++#include "glyph.h"
++#include "normalMode.h"
++
+ #include <stdint.h>
+ #include <sys/types.h>
+ 
+@@ -33,6 +36,8 @@ enum glyph_attribute {
+       ATTR_WRAP       = 1 << 8,
+       ATTR_WIDE       = 1 << 9,
+       ATTR_WDUMMY     = 1 << 10,
++      ATTR_HIGHLIGHT  = 1 << 12,
++      ATTR_CURRENT    = 1 << 13,
+       ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
+ };
+ 
+@@ -42,11 +47,6 @@ enum selection_mode {
+       SEL_READY = 2
+ };
+ 
+-enum selection_type {
+-      SEL_REGULAR = 1,
+-      SEL_RECTANGULAR = 2
+-};
+-
+ enum selection_snap {
+       SNAP_WORD = 1,
+       SNAP_LINE = 2
+@@ -57,18 +57,6 @@ typedef unsigned int uint;
+ typedef unsigned long ulong;
+ typedef unsigned short ushort;
+ 
+-typedef uint_least32_t Rune;
+-
+-#define Glyph Glyph_
+-typedef struct {
+-      Rune u;           /* character code */
+-      ushort mode;      /* attribute flags */
+-      uint32_t fg;      /* foreground  */
+-      uint32_t bg;      /* background  */
+-} Glyph;
+-
+-typedef Glyph *Line;
+-
+ typedef union {
+       int i;
+       uint ui;
+@@ -81,6 +69,11 @@ void die(const char *, ...);
+ void redraw(void);
+ void draw(void);
+ 
++int currentLine(int, int);
++void kscrolldown(const Arg *);
++void kscrollup(const Arg *);
++void normalMode(Arg const *);
++
+ void printscreen(const Arg *);
+ void printsel(const Arg *);
+ void sendbreak(const Arg *);
+@@ -90,6 +83,9 @@ int tattrset(int);
+ void tnew(int, int);
+ void tresize(int, int);
+ void tsetdirtattr(int);
++size_t utf8decode(const char *, Rune *, size_t);
++Rune utf8decodebyte(char, size_t *);
++void tsetdirt(int, int);
+ void ttyhangup(void);
+ int ttynew(char *, char *, char *, char **);
+ size_t ttyread(void);
+@@ -100,8 +96,10 @@ void resettitle(void);
+ 
+ void selclear(void);
+ void selinit(void);
+-void selstart(int, int, int);
+-void selextend(int, int, int, int);
++void selstart(int, int, int, int);
++void xselstart(int, int, int);
++void selextend(int, int, int, int, int);
++void xselextend(int, int, int, int);
+ int selected(int, int);
+ char *getsel(void);
+ 
+diff --git a/term.h b/term.h
+new file mode 100644
+index 0000000..23adf0e
+--- /dev/null
++++ b/term.h
+@@ -0,0 +1,73 @@
++#ifndef TERM_H
++#define TERM_H
++
++//
++// Internal terminal structs.
++//
++
++#include "glyph.h"
++
++#include <stdint.h>
++
++#define HISTSIZE      2500
++
++typedef struct {
++      Glyph attr; /* current char attributes */
++      int x;
++      int y;
++      char state;
++} TCursor;
++
++typedef struct {
++      int mode;
++      int type;
++      int snap;
++      /// Selection variables:
++      /// ob – original coordinates of the beginning of the selection
++      /// oe – original coordinates of the end of the selection
++      struct {
++              int x, y, scroll;
++      } ob, oe;
++      /// Selection variables; currently displayed chunk.
++      /// nb – normalized coordinates of the beginning of the selection
++      /// ne – normalized coordinates of the end of the selection
++      struct {
++              int x, y;
++      } nb, ne;
++
++      int alt;
++} Selection;
++
++/* Internal representation of the screen */
++typedef struct {
++      int row;      /* nb row */
++      int col;      /* nb col */
++      Line *line;   /* screen */
++      Line *alt;    /* alternate screen */
++      Line hist[HISTSIZE]; /* history buffer */
++      int histi;    /* history index */
++      int scr;      /* scroll back */
++      int *dirty;   /* dirtyness of lines */
++      TCursor c;    /* cursor */
++      int ocx;      /* old cursor col */
++      int ocy;      /* old cursor row */
++      int top;      /* top    scroll limit */
++      int bot;      /* bottom scroll limit */
++      int mode;     /* terminal mode flags */
++      int esc;      /* escape state flags */
++      char trantbl[4]; /* charset table translation */
++      int charset;  /* current charset */
++      int icharset; /* selected charset for sequence */
++      int *tabs;
++} Term;
++
++extern Term term;
++
++#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \
++               term.scr + HISTSIZE + 1) % HISTSIZE] : \
++               term.line[(y) - term.scr])
++
++extern Selection sel;
++
++
++#endif // TERM_H
+diff --git a/win.h b/win.h
+index a6ef1b9..1a6fefe 100644
+--- a/win.h
++++ b/win.h
+@@ -19,6 +19,7 @@ enum win_mode {
+       MODE_MOUSEMANY   = 1 << 15,
+       MODE_BRCKTPASTE  = 1 << 16,
+       MODE_NUMLOCK     = 1 << 17,
++      MODE_NORMAL      = 1 << 18,
+       MODE_MOUSE       = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
+                         |MODE_MOUSEMANY,
+ };
+@@ -27,6 +28,7 @@ void xbell(void);
+ void xclipcopy(void);
+ void xdrawcursor(int, int, Glyph, int, int, Glyph);
+ void xdrawline(Line, int, int, int);
++void xdrawglyph(Glyph, int, int);
+ void xfinishdraw(void);
+ void xloadcols(void);
+ int xsetcolorname(int, const char *);
+diff --git a/x.c b/x.c
+index 1f62129..e297946 100644
+--- a/x.c
++++ b/x.c
+@@ -143,7 +143,6 @@ typedef struct {
+ static inline ushort sixd_to_16bit(int);
+ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, 
int);
+ static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, 
int);
+-static void xdrawglyph(Glyph, int, int);
+ static void xclear(int, int, int, int);
+ static int xgeommasktogravity(int);
+ static int ximopen(Display *);
+@@ -355,7 +354,7 @@ mousesel(XEvent *e, int done)
+                       break;
+               }
+       }
+-      selextend(evcol(e), evrow(e), seltype, done);
++      xselextend(evcol(e), evrow(e), seltype, done);
+       if (done)
+               setsel(getsel(), e->xbutton.time);
+ }
+@@ -471,7 +470,7 @@ bpress(XEvent *e)
+               xsel.tclick2 = xsel.tclick1;
+               xsel.tclick1 = now;
+ 
+-              selstart(evcol(e), evrow(e), snap);
++              xselstart(evcol(e), evrow(e), snap);
+       }
+ }
+ 
+@@ -757,6 +756,13 @@ xloadcolor(int i, const char *name, Color *ncolor)
+       return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor);
+ }
+ 
++void
++normalMode(Arg const *_) {
++      (void) _;
++      win.mode ^= MODE_NORMAL;
++}
++
++
+ void
+ xloadcols(void)
+ {
+@@ -1344,6 +1350,14 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, 
Glyph base, int len, int x, i
+               base.fg = defaultattr;
+       }
+ 
++      if (base.mode & ATTR_HIGHLIGHT) {
++              base.bg = highlightBg;
++              base.fg = highlightFg;
++      } else if ((base.mode & ATTR_CURRENT) && (win.mode & MODE_NORMAL)) {
++              base.bg = currentBg;
++              base.fg = currentFg;
++      }
++
+       if (IS_TRUECOL(base.fg)) {
+               colfg.alpha = 0xffff;
+               colfg.red = TRUERED(base.fg);
+@@ -1433,7 +1447,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph 
base, int len, int x, i
+               xclear(winx, winy + win.ch, winx + width, win.h);
+ 
+       /* Clean up the region we want to draw to. */
+-      XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
++      XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
+ 
+       /* Set the clip region because Xft is sometimes dirty. */
+       r.x = 0;
+@@ -1476,8 +1490,9 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, 
Glyph og)
+       Color drawcol;
+ 
+       /* remove the old cursor */
+-      if (selected(ox, oy))
+-              og.mode ^= ATTR_REVERSE;
++      if (selected(ox, oy)) og.mode ^= ATTR_REVERSE;
++      if (highlighted(ox, oy)) { og.mode ^= ATTR_HIGHLIGHT; }
++      if (currentLine(ox, oy)) { og.mode ^= ATTR_CURRENT; }
+       xdrawglyph(og, ox, oy);
+ 
+       if (IS_SET(MODE_HIDE))
+@@ -1509,6 +1524,11 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, 
Glyph og)
+               drawcol = dc.col[g.bg];
+       }
+ 
++      if ((g.mode & ATTR_CURRENT) && (win.mode & MODE_NORMAL)) {
++              g.bg = currentBg;
++              g.fg = currentFg;
++      }
++
+       /* draw the new one */
+       if (IS_SET(MODE_FOCUSED)) {
+               switch (win.cursor) {
+@@ -1592,12 +1612,18 @@ xdrawline(Line line, int x1, int y1, int x2)
+ 
+       numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
+       i = ox = 0;
+-      for (x = x1; x < x2 && i < numspecs; x++) {
++      for (x = x1; x < x2 && i < numspecs; ++x) {
+               new = line[x];
+               if (new.mode == ATTR_WDUMMY)
+                       continue;
+               if (selected(x, y1))
+                       new.mode ^= ATTR_REVERSE;
++              if (highlighted(x, y1)) {
++                      new.mode ^= ATTR_HIGHLIGHT;
++              }
++              if (currentLine(x, y1)) {
++                      new.mode ^= ATTR_CURRENT;
++              }
+               if (i > 0 && ATTRCMP(base, new)) {
+                       xdrawglyphfontspecs(specs, base, i, ox, y1);
+                       specs += i;
+@@ -1786,6 +1812,14 @@ kpress(XEvent *ev)
+               len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, 
&status);
+       else
+               len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
++
++      if (IS_SET(MODE_NORMAL)) {
++              ExitState const es = kpressNormalMode(buf, len, // strlen(buf),
++                              match(ControlMask, e->state),
++                              &ksym);
++              if (es == finished) { normalMode(NULL); }
++              return;
++      }
+       /* 1. shortcuts */
+       for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
+               if (ksym == bp->keysym && match(bp->mod, e->state)) {
+-- 
+2.25.0
+


Reply via email to