commit 42f9d623d25bdb930495e74084bc46c04bd15a14
Author: Julius Huelsmann <[email protected]>
Date:   Tue Nov 12 15:24:02 2019 +0100

    [st][patch][vimBrowse+alphaFocusHighlight] Upload the patches which were 
previously only hosted at a remote server and could thus not be checked.

diff --git a/st.suckless.org/patches/alphaFocusHighlight/index.md 
b/st.suckless.org/patches/alphaFocusHighlight/index.md
index 36311c0e..1941788d 100644
--- a/st.suckless.org/patches/alphaFocusHighlight/index.md
+++ b/st.suckless.org/patches/alphaFocusHighlight/index.md
@@ -32,7 +32,12 @@ Notes
 
 Download
 --------
-* 
[st-alphaFocusHighlight-20191107-2b8333f.diff](https://github.com/juliusHuelsmann/st/releases/download/patchesV1/st-alphaFocusHighlight-20191107-2b8333f.diff)
+**Most Recent**:
+* [st-alphaFocusHighlight-20191107-2b8333f.diff 
(Github)](https://github.com/juliusHuelsmann/st/releases/download/patchesV1/st-alphaFocusHighlight-20191107-2b8333f.diff)
+
+**All Versions**:
+* [st-alphaFocusHighlight-20191107-2b8333f.diff 
(Github)](https://github.com/juliusHuelsmann/st/releases/download/patchesV1/st-alphaFocusHighlight-20191107-2b8333f.diff)
+* [st-alphaFocusHighlight-20191107-2b8333f.diff 
(attached)](st-alphaFocusHighlight-20191107-2b8333f.diff)
 
 Authors of the Alpha Patch
 --------------------------
diff --git 
a/st.suckless.org/patches/alphaFocusHighlight/st-alphaFocusHighlight-20191107-2b8333f.diff
 
b/st.suckless.org/patches/alphaFocusHighlight/st-alphaFocusHighlight-20191107-2b8333f.diff
new file mode 100644
index 00000000..1f74cf9b
--- /dev/null
+++ 
b/st.suckless.org/patches/alphaFocusHighlight/st-alphaFocusHighlight-20191107-2b8333f.diff
@@ -0,0 +1,223 @@
+From 49fbc3f0a3d3524fa37ba4c0129425394b84feed Mon Sep 17 00:00:00 2001
+From: Julius Huelsmann <[email protected]>
+Date: Wed, 6 Nov 2019 21:59:28 +0100
+Subject: [PATCH] [PATCH:FOCUS]: first version
+
+---
+ config.def.h |  7 ++++++-
+ config.mk    |  2 +-
+ st.h         |  2 ++
+ x.c          | 59 +++++++++++++++++++++++++++++++++++++++-------------
+ 4 files changed, 54 insertions(+), 16 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 6ebea98..16f1ebd 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -82,6 +82,10 @@ char *termname = "st-256color";
+  */
+ unsigned int tabspaces = 8;
+ 
++/* bg opacity */
++float alpha = 0.8;           //< alpha value used when the window is focused.
++float alphaUnfocussed = 0.6; //< alpha value used when the focus is lost
++
+ /* Terminal colors (16 first used in escape sequence) */
+ static const char *colorname[] = {
+       /* 8 normal colors */
+@@ -109,6 +113,7 @@ static const char *colorname[] = {
+       /* more colors can be added after 255 to use with DefaultXX */
+       "#cccccc",
+       "#555555",
++      "black",
+ };
+ 
+ 
+@@ -117,7 +122,7 @@ static const char *colorname[] = {
+  * foreground, background, cursor, reverse cursor
+  */
+ unsigned int defaultfg = 7;
+-unsigned int defaultbg = 0;
++unsigned int defaultbg = 258;
+ static unsigned int defaultcs = 256;
+ static unsigned int defaultrcs = 257;
+ 
+diff --git a/config.mk b/config.mk
+index 0cbb002..1d2f0e2 100644
+--- a/config.mk
++++ b/config.mk
+@@ -16,7 +16,7 @@ PKG_CONFIG = pkg-config
+ INCS = -I$(X11INC) \
+        `$(PKG_CONFIG) --cflags fontconfig` \
+        `$(PKG_CONFIG) --cflags freetype2`
+-LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
++LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
+        `$(PKG_CONFIG) --libs fontconfig` \
+        `$(PKG_CONFIG) --libs freetype2`
+ 
+diff --git a/st.h b/st.h
+index 4da3051..0bc69f8 100644
+--- a/st.h
++++ b/st.h
+@@ -120,3 +120,5 @@ extern char *termname;
+ extern unsigned int tabspaces;
+ extern unsigned int defaultfg;
+ extern unsigned int defaultbg;
++extern float alpha;
++extern float alphaUnfocussed;
+diff --git a/x.c b/x.c
+index 5828a3b..45bc960 100644
+--- a/x.c
++++ b/x.c
+@@ -4,6 +4,7 @@
+ #include <limits.h>
+ #include <locale.h>
+ #include <signal.h>
++#include <stdbool.h>
+ #include <sys/select.h>
+ #include <time.h>
+ #include <unistd.h>
+@@ -98,6 +99,7 @@ typedef struct {
+       XSetWindowAttributes attrs;
+       int scr;
+       int isfixed; /* is fixed geometry? */
++      int depth; /* bit depth */
+       int l, t; /* left and top offset */
+       int gm; /* geometry mask */
+ } XWindow;
+@@ -233,6 +235,7 @@ static char *usedfont = NULL;
+ static double usedfontsize = 0;
+ static double defaultfontsize = 0;
+ 
++static char *opt_alpha = NULL;
+ static char *opt_class = NULL;
+ static char **opt_cmd  = NULL;
+ static char *opt_embed = NULL;
+@@ -241,6 +244,7 @@ static char *opt_io    = NULL;
+ static char *opt_line  = NULL;
+ static char *opt_name  = NULL;
+ static char *opt_title = NULL;
++static bool focused = true;
+ 
+ static int oldbutton = 3; /* button event on startup: 3 = release */
+ 
+@@ -692,7 +696,7 @@ xresize(int col, int row)
+ 
+       XFreePixmap(xw.dpy, xw.buf);
+       xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
+-                      DefaultDepth(xw.dpy, xw.scr));
++                      xw.depth);
+       XftDrawChange(xw.draw, xw.buf);
+       xclear(0, 0, win.w, win.h);
+ 
+@@ -752,6 +756,14 @@ xloadcols(void)
+                       else
+                               die("could not allocate color %d
", i);
+               }
++
++      /* set alpha value of bg color */
++      if (opt_alpha)
++              alpha = strtof(opt_alpha, NULL);
++      float const usedAlpha = focused ? alpha : alphaUnfocussed;
++      dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * usedAlpha);
++      dc.col[defaultbg].pixel &= 0x00FFFFFF;
++      dc.col[defaultbg].pixel |= (unsigned char)(0xff * usedAlpha) << 24;
+       loaded = 1;
+ }
+ 
+@@ -1044,11 +1056,23 @@ xinit(int cols, int rows)
+       Window parent;
+       pid_t thispid = getpid();
+       XColor xmousefg, xmousebg;
++      XWindowAttributes attr;
++      XVisualInfo vis;
+ 
+       if (!(xw.dpy = XOpenDisplay(NULL)))
+               die("can't open display
");
+       xw.scr = XDefaultScreen(xw.dpy);
+-      xw.vis = XDefaultVisual(xw.dpy, xw.scr);
++
++      if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) {
++              parent = XRootWindow(xw.dpy, xw.scr);
++              xw.depth = 32;
++      } else {
++              XGetWindowAttributes(xw.dpy, parent, &attr);
++              xw.depth = attr.depth;
++      }
++
++      XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis);
++      xw.vis = vis.visual;
+ 
+       /* font */
+       if (!FcInit())
+@@ -1058,7 +1082,7 @@ xinit(int cols, int rows)
+       xloadfonts(usedfont, 0);
+ 
+       /* colors */
+-      xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
++      xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None);
+       xloadcols();
+ 
+       /* adjust fixed window geometry */
+@@ -1078,19 +1102,15 @@ xinit(int cols, int rows)
+               | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
+       xw.attrs.colormap = xw.cmap;
+ 
+-      if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0))))
+-              parent = XRootWindow(xw.dpy, xw.scr);
+       xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t,
+-                      win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), 
InputOutput,
++                      win.w, win.h, 0, xw.depth, InputOutput,
+                       xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
+                       | CWEventMask | CWColormap, &xw.attrs);
+ 
+       memset(&gcvalues, 0, sizeof(gcvalues));
+       gcvalues.graphics_exposures = False;
+-      dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures,
+-                      &gcvalues);
+-      xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
+-                      DefaultDepth(xw.dpy, xw.scr));
++      xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth);
++      dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues);
+       XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
+       XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
+ 
+@@ -1663,13 +1683,21 @@ focus(XEvent *ev)
+               XSetICFocus(xw.xic);
+               win.mode |= MODE_FOCUSED;
+               xseturgency(0);
+-              if (IS_SET(MODE_FOCUS))
+-                      ttywrite("", 3, 0);
++              if (IS_SET(MODE_FOCUS)) { ttywrite("", 3, 0); }
++              if (!focused) {
++                      focused = true;
++                      xloadcols();
++                      redraw();
++              }
+       } else {
+               XUnsetICFocus(xw.xic);
+               win.mode &= ~MODE_FOCUSED;
+-              if (IS_SET(MODE_FOCUS))
+-                      ttywrite("", 3, 0);
++              if (IS_SET(MODE_FOCUS)) { ttywrite("", 3, 0); }
++              if (focused) {
++                      focused = false;
++                      xloadcols();
++                      redraw();
++              }
+       }
+ }
+ 
+@@ -1925,6 +1953,9 @@ main(int argc, char *argv[])
+       case 'a':
+               allowaltscreen = 0;
+               break;
++      case 'A':
++              opt_alpha = EARGF(usage());
++              break;
+       case 'c':
+               opt_class = EARGF(usage());
+               break;
+-- 
+2.24.0
+
diff --git a/st.suckless.org/patches/vimBrowse/index.md 
b/st.suckless.org/patches/vimBrowse/index.md
index 2bf2b33e..7dc02dc1 100644
--- a/st.suckless.org/patches/vimBrowse/index.md
+++ b/st.suckless.org/patches/vimBrowse/index.md
@@ -85,7 +85,12 @@ Bugs
 
 Download
 --------
-* 
[st-vimBrowse-20191107-2b8333f.diff](https://github.com/juliusHuelsmann/st/releases/download/patchesV1/st-vimBrowse-20191107-2b8333f.diff)
+**Most Recent**:
+* [st-vimBrowse-20191107-2b8333f.diff 
(Github)](https://github.com/juliusHuelsmann/st/releases/download/patchesV1/st-vimBrowse-20191107-2b8333f.diff)
+
+**All versions**:
+* [st-vimBrowse-20191107-2b8333f.diff 
(Github)](https://github.com/juliusHuelsmann/st/releases/download/patchesV1/st-vimBrowse-20191107-2b8333f.diff)
+* [st-vimBrowse-20191107-2b8333f.diff 
(attached)](st-vimBrowse-20191107-2b8333f.diff )
 
 Authors of the [Scrollback patch](https://st.suckless.org/patches/scrollback/)
 ------------------------------------------------------------------------------
diff --git 
a/st.suckless.org/patches/vimBrowse/st-vimBrowse-20191107-2b8333f.diff 
b/st.suckless.org/patches/vimBrowse/st-vimBrowse-20191107-2b8333f.diff
new file mode 100644
index 00000000..3e523a8d
--- /dev/null
+++ b/st.suckless.org/patches/vimBrowse/st-vimBrowse-20191107-2b8333f.diff
@@ -0,0 +1,1530 @@
+From de020f0c06440fd19a36e1b001ef9e0058f73369 Mon Sep 17 00:00:00 2001
+From: Julius Huelsmann <[email protected]>
+Date: Thu, 7 Nov 2019 09:08:49 +0100
+Subject: [PATCH] [PATCH:VIM]: first version
+
+---
+ Makefile       |   6 +-
+ config.def.h   |  27 ++
+ dynamicArray.h |  90 ++++++
+ st.c           | 794 +++++++++++++++++++++++++++++++++++++++++++++----
+ st.h           |  31 +-
+ win.h          |   2 +
+ x.c            |  51 +++-
+ 7 files changed, 936 insertions(+), 65 deletions(-)
+ create mode 100644 dynamicArray.h
+
+diff --git a/Makefile b/Makefile
+index 470ac86..7d93347 100644
+--- a/Makefile
++++ b/Makefile
+@@ -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
++x.o: arg.h config.h st.h win.h dynamicArray.h
+ 
+ $(OBJ): config.h config.mk
+ 
+@@ -35,7 +35,7 @@ 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 
$(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 6ebea98..1b0e501 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -149,6 +149,12 @@ 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.
++static unsigned int highlightBg = 160;
++static unsigned int highlightFg = 15;
++/// Colors for the line and column that is marked 'current' in normal mode.
++static unsigned int currentBg = 0;
++static unsigned int currentFg = 15;
+ 
+ /*
+  * Internal mouse shortcuts.
+@@ -162,10 +168,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} },
+@@ -178,6 +186,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} },
+ };
+ 
+ /*
+@@ -456,3 +466,20 @@ static char ascii_printable[] =
+       " !\"#$%&'()*+,-./0123456789:;<=>?"
+       "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_"
+       "`abcdefghijklmnopqrstuvwxyz{|}~";
++
++
++/// word sepearors normal mode
++char wordDelimSmall[] = "     !\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~";
++char wordDelimLarge[] = "     "; /// <Word sepearors normal mode (capital W)
++
++/// Shortcusts executed in normal mode (which should not already be in use)
++struct NormalModeShortcuts normalModeShortcuts [] = {
++      { 'C', "?Building
" },
++      { 'c', "/Building
" },
++      { 'F', "?: error:
" },
++      { 'f', "/: error:
" },
++      { 'X', "?juli@machine
" },
++      { 'x', "/juli@machine
" },
++};
++
++size_t const amountNormalModeShortcuts = sizeof(normalModeShortcuts) / 
sizeof(*normalModeShortcuts);
+diff --git a/dynamicArray.h b/dynamicArray.h
+new file mode 100644
+index 0000000..c65fbef
+--- /dev/null
++++ b/dynamicArray.h
+@@ -0,0 +1,90 @@
++#include <stdint.h>
++#include <assert.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 {
++      uint8_t itemSize;
++      uint32_t index;
++      uint32_t allocated;
++      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
++
++
++inline char*
++gnext(DynamicArray *s) { return &s->content[s->index+=s->itemSize]; }
++
++inline char*
++get(DynamicArray const * s) { return &s->content[s->index]; }
++
++inline char*
++view(DynamicArray const * s, uint32_t i) {
++      return s->content + i*s->itemSize;
++}
++
++inline char *
++viewEnd(DynamicArray const *s, uint32_t i) {
++      return s->content + s->index - (i + 1) * s->itemSize;
++}
++
++inline void
++set(DynamicArray* s, char const *vals, uint8_t amount) {
++      assert(amount <= s->itemSize);
++      memcpy(s->content + s->index, vals, amount);
++}
++
++inline void
++snext(DynamicArray* s, char const *vals, uint8_t amount) {
++      set(s, vals, amount);
++      s->index+=s->itemSize;
++}
++
++inline void
++empty(DynamicArray* s) { s->index = 0; }
++
++inline bool
++isEmpty(DynamicArray* s) { return s->index == 0; }
++
++inline uint32_t
++size(DynamicArray const * s) { return s->index / s->itemSize; }
++
++inline void
++pop(DynamicArray* s) { s->index -= s->itemSize; }
++
++inline void checkSetNext(DynamicArray *s, char const *c, uint8_t amount) {
++      if (s->index + s->itemSize >= s->allocated) {
++              if ((s->content = (char *)realloc(
++                                              s->content, s->allocated += 
EXPAND_STEP * s->itemSize)) == NULL) {
++                      exit(1);
++              };
++      }
++      if (amount) { snext(s, c, amount); }
++}
++
++char *checkGetNext(DynamicArray *s) {
++      if (s->index + s->itemSize >= s->allocated) {
++              if ((s->content = (char *)realloc(
++                                              s->content, s->allocated += 
EXPAND_STEP * s->itemSize)) == NULL) {
++                      exit(1);
++              };
++      }
++      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))
+diff --git a/st.c b/st.c
+index ede7ae6..27bfca8 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,8 +19,10 @@
+ #include <unistd.h>
+ #include <wchar.h>
+ 
++
+ #include "st.h"
+ #include "win.h"
++#include "dynamicArray.h"
+ 
+ #if   defined(__linux)
+  #include <pty.h>
+@@ -35,6 +39,8 @@
+ #define ESC_ARG_SIZ   16
+ #define STR_BUF_SIZ   ESC_BUF_SIZ
+ #define STR_ARG_SIZ   ESC_ARG_SIZ
++//#define HISTSIZE      100
++#define HISTSIZE      2000
+ 
+ /* macros */
+ #define IS_SET(flag)          ((term.mode & (flag)) != 0)
+@@ -42,6 +48,9 @@
+ #define ISCONTROLC1(c)                (BETWEEN(c, 0x80, 0x9f))
+ #define ISCONTROL(c)          (ISCONTROLC0(c) || ISCONTROLC1(c))
+ #define ISDELIM(u)            (u && wcschr(worddelimiters, u))
++#define TLINE(y)              ((y) < term.scr ? term.hist[((y) + term.histi - 
\
++                              term.scr + HISTSIZE + 1) % HISTSIZE] : \
++                              term.line[(y) - term.scr])
+ 
+ enum term_mode {
+       MODE_WRAP        = 1 << 0,
+@@ -97,16 +106,18 @@ 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
+-       */
++      /// 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, ob, oe;
++      } nb, ne;
+ 
+       int alt;
+ } Selection;
+@@ -117,6 +128,9 @@ typedef struct {
+       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 */
+@@ -152,6 +166,50 @@ typedef struct {
+       int narg;              /* nb of args */
+ } STREscape;
+ 
++/// Position (x, y , and current scroll in the y dimension).
++typedef struct Position {
++      uint32_t x;
++      uint32_t y;
++      uint32_t yScr;
++} Position;
++
++/// The entire normal mode state, consisting of an operation
++/// and a motion.
++struct NormalModeState {
++      Position initialPosition;
++      // Operation:
++      struct OperationState {
++              enum Operation {
++                      noop,
++                      visual,
++                      visualLine,
++                      yank
++              } op;
++              Position startPosition;
++      } command;
++      // Motions:
++      struct MotionState {
++              uint32_t amount;
++              enum Search {
++                      none,
++                      forward,
++                      backward,
++              } search;
++              Position searchPosition;
++              bool finished;
++      } motion;
++} stateNormalMode;
++
++
++DynamicArray searchString =  UTF8_ARRAY;
++DynamicArray commandHist0 =  UTF8_ARRAY;
++DynamicArray commandHist1 =  UTF8_ARRAY;
++DynamicArray highlights   = QWORD_ARRAY;
++/// History command toggle
++bool toggle = false;
++#define currentCommand toggle ? &commandHist0 : &commandHist1
++#define lastCommand    toggle ? &commandHist1 : &commandHist0
++
+ static void execsh(char *, char **);
+ static void stty(char **);
+ static void sigchld(int);
+@@ -184,8 +242,8 @@ 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);
+@@ -231,6 +289,12 @@ static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 
0xF0, 0xF8};
+ static Rune utfmin[UTF_SIZ + 1] = {       0,    0,  0x80,  0x800,  0x10000};
+ static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
+ 
++void applyPosition(Position const *pos) {
++      term.c.x = pos->x;
++      term.c.y = pos->y;
++      term.scr = pos->yScr;
++}
++
+ ssize_t
+ xwrite(int fd, const char *s, size_t len)
+ {
+@@ -409,17 +473,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;
+@@ -428,6 +497,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)
+@@ -436,10 +506,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) {
+@@ -447,18 +520,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;
+@@ -467,17 +544,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);
+@@ -485,7 +566,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)
+@@ -521,7 +602,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;
+@@ -536,14 +617,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)))
+@@ -564,14 +645,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;
+                               }
+@@ -591,24 +672,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;
+ 
+@@ -831,6 +920,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);
+@@ -1042,13 +1134,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);
+ 
+@@ -1062,13 +1194,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);
+ 
+@@ -1088,6 +1230,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;
+@@ -1117,13 +1260,544 @@ 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);
++}
++
++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) { //< I think this temds to be more 
efficient than
++              xMin += term.col;            //  division + modulo.
++              --yMin;
++      }
++      if (xMin < 0) { xMin = 0; }
++
++      uint32_t highSize = size(&highlights);
++      uint32_t *ptr = (uint32_t*) highlights.content;
++      for (uint32_t i = 0; i < highSize; ++i) {
++              int32_t const sx = *(ptr++);
++              int32_t const sy = *(ptr++);
++              if (BETWEEN(sy, yMin, y) && (sy != yMin || sx > xMin) && (sy != 
y || sx <= x)) {
++                      return true;
++              }
++      }
++      return false;
++}
++
++int mod(int a, int b) {
++      while (a < 0) {
++              a+= b;
++      }
++      return a % b;
++}
++
++void displayString(DynamicArray const *str, Glyph *g, int yPos) {
++      // Threshold: if there is nothing or no space to print, do not print.
++      if (term.col == 0 || str->index == 0) {
++              term.dirty[yPos] = 1; //< mark this line as 'dirty', because 
the line is not
++              //  marked dirty when scrolling due to string display.
++              return;
++      }
++
++      uint32_t lineSize = MIN(size(str), term.col / 3);
++      uint32_t xEnd = term.col - 1;
++      assert(lineSize <= 1 + xEnd); //< as lineSize <= term.col/3 <= term.col 
- 1 + 1 = xEnd + 1
++      uint32_t xStart = 1 + xEnd - lineSize;
++
++      Line line = malloc(sizeof(Glyph) * lineSize);
++      assert(str->index - 1 >=  lineSize - 1); //< lineSize <= str->index -1 
direct premise.
++
++      for (uint32_t lineIdx = 0; lineIdx < lineSize; lineIdx++) {
++              line[lineIdx] = *g;
++              char* end = viewEnd(str, lineSize - lineIdx - 1);
++              memcpy(&line[lineIdx].u, end, str->itemSize);
++      }
++      xdrawline(TLINE(yPos), 0, yPos, xStart);
++      xdrawline(line -xStart, xStart, yPos, xEnd+1);
++      free(line); // that sucks.
++}
++
++/// Print either the current command or the last comman din case the current 
command is empty.
++void printCommandString() {
++      Glyph g = {'c', ATTR_ITALIC | ATTR_FAINT , defaultfg, defaultbg};
++      if (term.c.y == term.row-1) { g.mode ^= ATTR_CURRENT; } //< dont 
highlight
++      DynamicArray * cc = currentCommand;
++      displayString(isEmpty(cc) ? lastCommand : cc, &g, term.row - 1);
++      //displayString(lastCommand, &g, term.row - 2);
++}
++
++void printSearchString() {
++      Glyph g = {'c', ATTR_ITALIC | ATTR_BOLD_FAINT, defaultfg, defaultbg};
++      if (term.c.y == term.row-2) { g.mode ^= ATTR_CURRENT; } //< dont 
highlight
++      displayString(&searchString, &g, term.row - 2);
++}
++
++/// Default state if no operation is performed.
++struct NormalModeState defaultNormalMode = {{0,0,0}, {noop, {0, 0, 0}}, {0, 
none, {0, 0, 0}, false}};
++
++void enableMode(enum Operation o) {
++      stateNormalMode.command.op = o;
++      stateNormalMode.command.startPosition.x = term.c.x;
++      stateNormalMode.command.startPosition.y = term.c.y;
++      stateNormalMode.command.startPosition.yScr = term.scr;
++}
++
++bool normalModeEnabled = false;
++
++void onNormalModeStart() {
++      normalModeEnabled = true;
++}
++
++void onNormalModeStop() { //XXX breaks if resized
++      normalModeEnabled = false;
++      applyPosition(&stateNormalMode.initialPosition);
++}
++
++void moveLine(int8_t sign) {
++      if (sign == -1) {
++              if (term.c.y-- == 0) {
++                      if (++term.scr == HISTSIZE) {
++                              term.c.y = term.row - 1;
++                              term.scr = 0;
++                      } else {
++                              term.c.y = 0;
++                      }
++              }
++      } else {
++              term.c.x = 0;
++              if (++term.c.y == term.row) {
++                      if (term.scr-- == 0) {
++                              term.c.y = 0;
++                              term.scr = HISTSIZE - 1;
++                      } else {
++                              term.c.y = term.row - 1;
++                      }
++              }
++      }
++}
++
++void moveLetter(int8_t sign) {
++      term.c.x += sign;
++      if (!BETWEEN(term.c.x, 0, term.col-1)) {
++              if (term.c.x < 0) {
++                      term.c.x = term.col - 1;
++                      moveLine(sign);
++              } else {
++                      term.c.x = 0;
++                      moveLine(sign);
++              }
++      }
++}
++
++bool contains (char ksym, char const * values, uint32_t amount) {
++      for (uint32_t i = 0; i < amount; i++) { if (ksym == values[i]) { return 
true; } }
++      return false;
++}
++
++
++void terminateCommand(bool abort) {
++      stateNormalMode.command = defaultNormalMode.command; //< clear command 
+ motion
++      stateNormalMode.motion  = defaultNormalMode.motion;
++      selclear();                                          //< clear 
selection if any
++
++      if (!abort) { toggle = !toggle; }
++      empty(currentCommand);
++
++      printCommandString();
++      printSearchString();
++      //tsetdirt(0, term.row-3);
++}
++inline void exitCommand() { terminateCommand(false); }
++inline void abortCommand() { terminateCommand(true); }
++
++/// Go to next occurrence of string relative to the current location
++/// conduct search, starting at start pos
++bool
++gotoString(int8_t sign) {
++      uint32_t findIndex = 0;
++      uint32_t searchStringSize = size(&searchString);
++      uint32_t const maxIteration = (HISTSIZE + term.row) * term.col + 
searchStringSize;  //< one complete traversal.
++      for (uint32_t cIteration = 0; findIndex < searchStringSize
++                      && cIteration ++ < maxIteration; moveLetter(sign)) {
++              uint32_t const searchChar = *((uint32_t*)(sign == 1 ? 
view(&searchString, findIndex)
++                                      : viewEnd(&searchString, findIndex)));
++
++              uint32_t const fu = TLINE(term.c.y)[term.c.x].u;
++
++              if (fu == searchChar) findIndex++;
++              else findIndex = 0;
++      }
++      bool const found = findIndex == searchStringSize;
++      if (found) { for (uint32_t i = 0; i < searchStringSize; i++) { 
moveLetter(-sign); } }
++      return found;
++}
++
++/// Find the next occurrence of a word
++bool
++gotoNextString(int8_t sign) {
++      moveLetter(sign);
++      return gotoString(sign);
++}
++
++/// Highlight all found strings on the current screen.
++void
++highlightStringOnScreen() {
++      if (isEmpty(&searchString)) { return; }
++      uint32_t const searchStringSize = size(&searchString);
++      uint32_t findIndex = 0;
++      uint32_t xStart, yStart;
++      for (uint32_t y = 0; y < term.row; y++) {
++              for (uint32_t x = 0; x < term.col; x++) {
++                      if (TLINE(y)[x].u == *((uint32_t*)(view(&searchString, 
findIndex)))) {
++                              if (findIndex++ == 0) {
++                                      xStart = x;
++                                      yStart = y;
++                              }
++                              if (findIndex == searchStringSize) {
++                                      // mark selected
++                                      append(&highlights, &xStart);
++                                      append(&highlights, &yStart);
++
++                                      findIndex = 0;
++                                      term.dirty[yStart] = 1;
++                              }
++                      } else {
++                              findIndex = 0;
++                      }
++              }
++      }
++}
++
++void gotoStringAndHighlight(int8_t sign) {
++      bool const found = gotoString(sign);  //< find the next string to the 
current position
++      empty(&highlights);             //< remove previous highlights
++      if (found) {                          //< apply new highlights if found
++              //if (sign == -1) { moveLetter(-1); }
++              highlightStringOnScreen(sign);
++      } else {                              //< go to the position where the 
search started.
++              applyPosition(&stateNormalMode.motion.searchPosition);
++      }
++      tsetdirt(0, term.row-3);              //< repaint everything except for 
the status bar, which
++                                            //  is painted separately.
++}
++
++void pressKeys(char const* nullTerminatedString) {
++      size_t end;
++      for (size_t i = 0, end=strlen(nullTerminatedString); i < end; ++i) {
++              if (nullTerminatedString[i] == '
') {
++                      kpressNormalMode(&nullTerminatedString[i], 0, false, 
true, false);
++              } else {
++                      kpressNormalMode(&nullTerminatedString[i], 1, false, 
false, false);
++              }
++      }
++}
++
++void executeCommand(DynamicArray const *command) {
++      size_t end;
++      char decoded [32];
++      for (size_t i = 0, end=size(command); i < end; ++i) {
++              size_t len = utf8encode(*((Rune*)view(command, i)) , decoded);
++              kpressNormalMode(decoded, len, false, false, false);
++      }
++      //kpressNormalMode(NULL, 0, false, true, false);
++}
++
++void kpressNormalMode(char const * ksym, uint32_t len, bool esc, bool enter, 
bool backspace) {
++      // [ESC] or [ENTER] abort resp. finish the current operation or
++      // the Normal Mode if no operation is currently executed.
++      if (esc || enter) {
++              if (stateNormalMode.command.op == noop
++                              && stateNormalMode.motion.search == none
++                              && stateNormalMode.motion.amount == 0) {
++                      terminateCommand(!enter);
++                      empty(&highlights);
++                      tfulldirt(); // < this also removes the search string 
and the last command.
++                      normalMode(NULL);
++              } else {
++                      if (enter && stateNormalMode.motion.search != none && 
!isEmpty(&searchString)) {
++                              exitCommand(); 
//stateNormalMode.motion.finished = true;
++                              return;
++                      } else {
++                              abortCommand();
++                      }
++              }
++              return;
++      } //< ! (esc || enter)
++      // Search: append to search string & conduct search for best hit, 
starting at start pos,
++      //         highlighting all other occurrences on the current page if 
one is found.
++      if (stateNormalMode.motion.search != none && 
!stateNormalMode.motion.finished) {
++              int8_t const sign = stateNormalMode.motion.search == forward ? 
1 : -1;
++              // Apply start position.
++              if (backspace) { // XXX: if a quantifier is subject to removal, 
it is currently only removed
++                                     //      from the  command string.
++                      if (!isEmpty(currentCommand) && 
!isEmpty(&searchString)) {
++                              pop(currentCommand);
++                              pop(&searchString);
++                      } else if (isEmpty(currentCommand) || 
isEmpty(&searchString)) {
++                              empty(&highlights);
++                              stateNormalMode.motion = defaultNormalMode 
.motion; //< if typed once more than there are
++                              selclear();                                     
    //  letters, the search motion is
++                              return;                                         
    //  terminated
++                      }
++                      applyPosition(&stateNormalMode.motion.searchPosition);
++              } else {
++                      if (len > 0) {
++                              char* kSearch = checkGetNext(&searchString);
++                              utf8decode(ksym, (Rune*)(kSearch), len);
++
++                              char* kCommand = checkGetNext(currentCommand);
++                              utf8decode(ksym, (Rune*)(kCommand), len);
++                      }
++              }
++              if (sign == -1) { moveLetter(1); }
++              gotoStringAndHighlight(sign); //< go to the next occurrence of 
the string and highlight
++                                            //  all occurrences currently on 
screen
++
++              if (stateNormalMode.command.op == visual) {
++                      selextend(term.c.x, term.c.y, term.scr, sel.type, 0);
++              } else if  (stateNormalMode.command.op == visualLine) {
++                      selextend(term.col-1, term.c.y, term.scr, sel.type, 0);
++              }
++              printCommandString();
++              printSearchString();
++              return;
++      }
++
++      if (len == 0) { return; }
++      // V / v or y take precedence over movement commands.
++      switch(ksym[0]) {
++              case '.':
++                      {
++
++                              if (!isEmpty(currentCommand)) { toggle = 
!toggle; empty(currentCommand); }
++                              executeCommand(lastCommand);
++                      }
++                      return;
++              case 'y': //< Yank mode
++                      {
++                              char* kCommand = checkGetNext(currentCommand);
++                              utf8decode(ksym, (Rune*)(kCommand), len);
++                              switch(stateNormalMode.command.op) {
++                                      case noop:           //< Start yank 
mode & set #op
++                                              enableMode(yank);
++                                              selstart(term.c.x, term.c.y, 
term.scr, 0);
++                                              empty(currentCommand);
++                                              break;
++                                      case visualLine:     //< Complete yank 
operation
++                                      case visual:
++                                              xsetsel(getsel());     //< yank
++                                              xclipcopy();
++                                              exitCommand();         //< 
reset command
++                                              break;
++                                      case yank:           //< Complete yank 
operation as in y#amount j
++                                              selstart(0, term.c.y, term.scr, 
0);
++                                              uint32_t const origY = term.c.y;
++                                              for (int32_t i = 0; i < 
MAX(stateNormalMode.motion.amount, 1) - 1; i ++) moveLine(1);
++                                              selextend(term.col-1, term.c.y, 
term.scr, SEL_RECTANGULAR, 0);
++                                              xsetsel(getsel());
++                                              xclipcopy();
++                                              term.c.y = origY;
++                                              exitCommand();
++                              }
++                      }
++                      printCommandString();
++                      printSearchString();
++                      return;
++              case 'v':                //< Visual Mode: Toggle mode.
++              case 'V':
++                      {
++                              enum Operation mode = ksym[0] == 'v' ? visual : 
visualLine;
++                              bool assign = stateNormalMode.command.op != 
mode;
++                              abortCommand();
++                              if (assign) {
++                                      enableMode(mode);
++                                      char* kCommand = 
checkGetNext(currentCommand);
++                                      utf8decode(ksym, (Rune*)(kCommand), 
len);
++                                      if (mode == visualLine) {
++                                              selstart(0, term.c.y, term.scr, 
0);
++                                              selextend(term.col-1, term.c.y, 
term.scr, SEL_RECTANGULAR, 0);
++                                      } else {
++                                              selstart(term.c.x, term.c.y, 
term.scr, 0);
++                                      }
++                              }
++                      }
++                      return;
++      }
++      // Perform the movement.
++      int32_t sign = -1;    //< whehter a command goes 'forward' (1) or 
'backward' (-1)
++      bool discard = false; //< discard input, as it does not have a meaning.
++      switch(ksym[0]) {
++              case 'j': sign = 1;
++              case 'k':
++                                                      term.c.y += sign * 
MAX(stateNormalMode.motion.amount, 1);
++                                                      break;
++              case 'H': term.c.y = 0;            break; //< [numer]H ~ 
L[number]j is not supported.
++              case 'M': term.c.y = term.bot / 2; break;
++              case 'L': term.c.y = term.bot;     break; //< [numer]L ~ 
L[number]k is not supported.
++              case 'G':  //< a little different from vim, but in this use 
case the most useful translation.
++                                                      
applyPosition(&stateNormalMode.initialPosition);
++              case 'l': sign = 1;
++              case 'h':
++                                                      {
++                                                              int32_t const 
amount = term.c.x + sign * MAX(stateNormalMode.motion.amount, 1);
++                                                              term.c.x = 
amount % term.col;
++                                                              while (term.c.x 
< 0) { term.c.x += term.col; }
++                                                              term.c.y += 
floor(1.0 * amount / term.col);
++                                                              break;
++                                                      }
++              case '0':
++                                                      if 
(stateNormalMode.motion.amount == 0) { term.c.x = 0; }
++                                                      else { discard = true; }
++                                                      break;
++              case '$': term.c.x = term.col-1; break;
++              case 'w':
++              case 'W':
++              case 'e':
++              case 'E': sign = 1;
++              case 'B':
++              case 'b':
++                                                      {
++                                                              bool const 
startSpaceIsSeparator = !(ksym[0] == 'w' || ksym[0] == 'W');
++                                                              bool const 
capital = ksym[0] <= 90; //< defines the word separators to use
++                                                              char const * 
const wDelim = capital ? wordDelimLarge : wordDelimSmall;
++                                                              uint32_t const 
wDelimLen =  strlen(wDelim);
++                                                              bool const 
performOffset = startSpaceIsSeparator; //< start & end with offset.
++                                                              uint32_t const 
maxIteration = (HISTSIZE + term.row) * term.col;  //< one complete traversal.
++
++                                                              // doesn't work 
exactly as in vim, but I think this version is better;
++                                                              // Linebreak is 
counted as 'normal' separator; hence a jump can span multiple lines here.
++                                                              
stateNormalMode.motion.amount = MAX(stateNormalMode.motion.amount, 1);
++                                                              for (; 
stateNormalMode.motion.amount > 0; stateNormalMode.motion.amount--) {
++                                                                      uint8_t 
state = 0;
++                                                                      if 
(performOffset) { moveLetter(sign); }
++                                                                      for 
(uint32_t cIteration = 0; cIteration ++ < maxIteration; 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; }
++                                                                      }
++                                                              }
++                                                              break;
++                                                      }
++              case '/': sign = 1;
++              case '?':
++                                                      empty(&searchString);
++                                                      
stateNormalMode.motion.search = sign == 1 ? forward : backward;
++                                                      
stateNormalMode.motion.searchPosition.x = term.c.x;
++                                                      
stateNormalMode.motion.searchPosition.y = term.c.y;
++                                                      
stateNormalMode.motion.searchPosition.yScr = term.scr;
++                                                      
stateNormalMode.motion.finished = false;
++                                                      break;
++              case 'n': sign = 1;
++              case 'N':
++                                                      toggle = !toggle;
++                                                      empty(currentCommand);
++                                                      if 
(stateNormalMode.motion.search == none) {
++                                                              
stateNormalMode.motion.search = forward;
++                                                              
stateNormalMode.motion.finished = true;
++                                                      }
++                                                      for (int32_t amount = 
MAX(stateNormalMode.motion.amount, 1); amount > 0; amount--) {
++                                                              if 
(stateNormalMode.motion.search == backward) { sign *= -1; }
++                                                              
moveLetter(sign);
++                                                              
gotoStringAndHighlight(sign);
++                                                      }
++                                                      break;
++              case 't':
++                                                      if (sel.type == 
SEL_REGULAR) {
++                                                              sel.type = 
SEL_RECTANGULAR;
++                                                      } else {
++                                                              sel.type = 
SEL_REGULAR;
++                                                      }
++                                                      tsetdirt(sel.nb.y, 
sel.ne.y);
++                                                      discard = true;
++              default:
++                                                      discard = true;
++      }
++      bool const isNumber = len == 1 && BETWEEN(ksym[0], 48, 57);
++      if (isNumber) { //< record numbers
++              discard = false;
++              stateNormalMode.motion.amount =
++                      MIN(SHRT_MAX, stateNormalMode.motion.amount * 10 + 
ksym[0] - 48);
++      } else if (!discard) {
++              stateNormalMode.motion.amount = 0;
++      }
++
++      if (discard) {
++              for (size_t i = 0; i < amountNormalModeShortcuts; ++i) {
++                      if (ksym[0] == normalModeShortcuts[i].key) {
++                              pressKeys(normalModeShortcuts[i].value);
++                      }
++              }
++      } else {
++              char* kCommand = checkGetNext(currentCommand);
++              utf8decode(ksym, (Rune*)(kCommand), len);
++
++              int diff = 0;
++              if (term.c.y > 0) {
++                      if (term.c.y > term.bot) {
++                              diff = term.bot - term.c.y;
++                              term.c.y = term.bot;
++                      }
++              } else {
++                      if (term.c.y < 0) {
++                              diff = -term.c.y;
++                              term.c.y = 0;
++                      }
++              }
++
++              int const _newScr = term.scr + diff;
++              term.c.y = _newScr < 0 ? 0 : (_newScr >= HISTSIZE ? term.bot : 
term.c.y);
++              term.scr = mod(_newScr, HISTSIZE);
++
++              if (!isEmpty(&highlights)) {
++                      empty(&highlights);
++                      highlightStringOnScreen();
++              }
++
++              tsetdirt(0, term.row-3);
++              printCommandString();
++              printSearchString();
++
++              if (stateNormalMode.command.op == visual) {
++                      selextend(term.c.x, term.c.y, term.scr, sel.type, 0);
++              } else if  (stateNormalMode.command.op == visualLine) {
++                      selextend(term.col-1, term.c.y, term.scr, sel.type, 0);
++              } else {
++                      if (!isNumber && (stateNormalMode.motion.search == none
++                                      || stateNormalMode.motion.finished)) {
++                              toggle = !toggle;
++                              empty(currentCommand);
++                      }
++                      if (stateNormalMode.command.op == yank) {
++                              if (!isNumber && !discard) {
++                                      // copy
++                                      selextend(term.c.x, term.c.y, term.scr, 
sel.mode, 0);
++                                      xsetsel(getsel());
++                                      xclipcopy();
++                                      
applyPosition(&stateNormalMode.command.startPosition);
++                                      exitCommand();
++                              }
++                      }
++              }
++      }
++}
++
+ void
+ csiparse(void)
+ {
+@@ -1176,6 +1850,10 @@ 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.
++      stateNormalMode.initialPosition.x = term.c.x;
++      stateNormalMode.initialPosition.y = term.c.y;
++      stateNormalMode.initialPosition.yScr = term.scr;
+ }
+ 
+ void
+@@ -1282,14 +1960,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
+@@ -1720,11 +2398,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);
+@@ -2227,7 +2905,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);
+               }
+@@ -2240,7 +2918,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);
+               }
+@@ -2458,7 +3136,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;
+@@ -2495,6 +3173,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));
+@@ -2552,7 +3238,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);
+       }
+ }
+ 
+@@ -2573,8 +3259,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 4da3051..7bd8bba 100644
+--- a/st.h
++++ b/st.h
+@@ -1,5 +1,6 @@
+ /* See LICENSE for license details. */
+ 
++#include <stdbool.h>
+ #include <stdint.h>
+ #include <sys/types.h>
+ 
+@@ -10,6 +11,8 @@
+ #define BETWEEN(x, a, b)      ((a) <= (x) && (x) <= (b))
+ #define DIVCEIL(n, d)         (((n) + ((d) - 1)) / (d))
+ #define DEFAULT(a, b)         (a) = (a) ? (a) : (b)
++#define INTERVAL(x, a, b)             (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
++#define INTERVAL_DIFF(x, a, b)                (x) < (a) ? (x) - (a) : (x) > 
(b) ? (x) - (b) : 0
+ #define LIMIT(x, a, b)                (x) = (x) < (a) ? (a) : (x) > (b) ? (b) 
: (x)
+ #define ATTRCMP(a, b)         ((a).mode != (b).mode || (a).fg != (b).fg || \
+                               (a).bg != (b).bg)
+@@ -33,6 +36,8 @@ enum glyph_attribute {
+       ATTR_WRAP       = 1 << 8,
+       ATTR_WIDE       = 1 << 9,
+       ATTR_WDUMMY     = 1 << 10,
++      ATTR_HIGHLIGHT  = 1 << 11 | ATTR_UNDERLINE,
++      ATTR_CURRENT    = 1 << 12,
+       ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
+ };
+ 
+@@ -80,6 +85,14 @@ void die(const char *, ...);
+ void redraw(void);
+ void draw(void);
+ 
++int highlighted(int, int);
++int currentLine(int, int);
++void kscrolldown(const Arg *);
++void kscrollup(const Arg *);
++void kpressNormalMode(char const * ksym, uint32_t len, bool esc, bool enter, 
bool backspace);
++void normalMode(Arg const *);
++void onNormalModeStart();
++void onNormalModeStop();
+ void printscreen(const Arg *);
+ void printsel(const Arg *);
+ void sendbreak(const Arg *);
+@@ -99,8 +112,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);
+ 
+@@ -110,6 +125,8 @@ void *xmalloc(size_t);
+ void *xrealloc(void *, size_t);
+ char *xstrdup(char *);
+ 
++
++
+ /* config.h globals */
+ extern char *utmp;
+ extern char *stty_args;
+@@ -120,3 +137,13 @@ extern char *termname;
+ extern unsigned int tabspaces;
+ extern unsigned int defaultfg;
+ extern unsigned int defaultbg;
++extern char wordDelimSmall[];
++extern char wordDelimLarge[];
++
++typedef struct NormalModeShortcuts {
++      char key;
++      char *value;
++} NormalModeShortcuts;
++
++extern NormalModeShortcuts normalModeShortcuts[];
++extern size_t const amountNormalModeShortcuts;
+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 5828a3b..ccf1751 100644
+--- a/x.c
++++ b/x.c
+@@ -136,7 +136,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 void ximopen(Display *);
+@@ -340,7 +339,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);
+ }
+@@ -444,7 +443,7 @@ bpress(XEvent *e)
+               xsel.tclick2 = xsel.tclick1;
+               xsel.tclick1 = now;
+ 
+-              selstart(evcol(e), evrow(e), snap);
++              xselstart(evcol(e), evrow(e), snap);
+       }
+ }
+ 
+@@ -730,6 +729,19 @@ xloadcolor(int i, const char *name, Color *ncolor)
+       return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor);
+ }
+ 
++void
++normalMode(Arg const *_)  //< the argument is just for the sake of
++                          //  adhering to the function format.
++{
++      win.mode ^= MODE_NORMAL; //< toggle normal mode via exclusive or.
++      if (win.mode & MODE_NORMAL) {
++              onNormalModeStart();
++      } else {
++              onNormalModeStop();
++      }
++}
++
++
+ void
+ xloadcols(void)
+ {
+@@ -1296,6 +1308,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);
+@@ -1428,8 +1448,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))
+@@ -1461,6 +1482,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) {
+@@ -1550,6 +1576,12 @@ xdrawline(Line line, int x1, int y1, int x2)
+                       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;
+@@ -1731,6 +1763,12 @@ kpress(XEvent *ev)
+               return;
+ 
+       len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status);
++      if (IS_SET(MODE_NORMAL)) {
++              kpressNormalMode(buf, strlen(buf),
++                              ksym == XK_Escape, ksym == XK_Return, ksym == 
XK_BackSpace);
++              return;
++      }
++
+       /* 1. shortcuts */
+       for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
+               if (ksym == bp->keysym && match(bp->mod, e->state)) {
+@@ -1870,8 +1908,9 @@ run(void)
+                               XNextEvent(xw.dpy, &ev);
+                               if (XFilterEvent(&ev, None))
+                                       continue;
+-                              if (handler[ev.type])
++                              if (handler[ev.type]) {
+                                       (handler[ev.type])(&ev);
++                              }
+                       }
+ 
+                       draw();
+-- 
+2.24.0
+


Reply via email to