st-vimBrowse-20200212-26cdfeb.diff - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       st-vimBrowse-20200212-26cdfeb.diff (85174B)
       ---
            1 From 22471790fcaf1505890bcb53752a6df87cfd3592 Mon Sep 17 00:00:00 2001
            2 From: Julius Huelsmann <juliusHuelsmann@gmail.com>
            3 Date: Wed, 12 Feb 2020 19:50:06 +0100
            4 Subject: [PATCH] port: vim patch 2020-02 to current HEAD of st
            5 
            6 ---
            7  Makefile       |   9 +-
            8  config.def.h   |  54 ++++
            9  config.h       | 513 +++++++++++++++++++++++++++++++++
           10  dynamicArray.h | 175 ++++++++++++
           11  error.h        |  47 ++++
           12  glyph.h        |  30 ++
           13  normalMode.c   | 750 +++++++++++++++++++++++++++++++++++++++++++++++++
           14  normalMode.h   |  36 +++
           15  st.c           | 249 +++++++++-------
           16  st.h           |  36 ++-
           17  term.h         |  73 +++++
           18  win.h          |   2 +
           19  x.c            |  48 +++-
           20  13 files changed, 1894 insertions(+), 128 deletions(-)
           21  create mode 100644 config.h
           22  create mode 100644 dynamicArray.h
           23  create mode 100644 error.h
           24  create mode 100644 glyph.h
           25  create mode 100644 normalMode.c
           26  create mode 100644 normalMode.h
           27  create mode 100644 term.h
           28 
           29 diff --git a/Makefile b/Makefile
           30 index 470ac86..af8f0be 100644
           31 --- a/Makefile
           32 +++ b/Makefile
           33 @@ -4,7 +4,7 @@
           34  
           35  include config.mk
           36  
           37 -SRC = st.c x.c
           38 +SRC = st.c x.c normalMode.c
           39  OBJ = $(SRC:.c=.o)
           40  
           41  all: options st
           42 @@ -21,8 +21,8 @@ config.h:
           43  .c.o:
           44          $(CC) $(STCFLAGS) -c $<
           45  
           46 -st.o: config.h st.h win.h
           47 -x.o: arg.h config.h st.h win.h
           48 +st.o: config.h st.h win.h dynamicArray.h normalMode.h term.h glyph.h error.h
           49 +x.o: arg.h config.h st.h win.h dynamicArray.h normalMode.h term.h glyph.h error.h
           50  
           51  $(OBJ): config.h config.mk
           52  
           53 @@ -35,7 +35,8 @@ clean:
           54  dist: clean
           55          mkdir -p st-$(VERSION)
           56          cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\
           57 -                config.def.h st.info st.1 arg.h st.h win.h $(SRC)\
           58 +                config.def.h st.info st.1 arg.h st.h win.h dynamicArray.h\
           59 +                normalMode.h term.h error.h $(SRC)\
           60                  st-$(VERSION)
           61          tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz
           62          rm -rf st-$(VERSION)
           63 diff --git a/config.def.h b/config.def.h
           64 index 546edda..92b541d 100644
           65 --- a/config.def.h
           66 +++ b/config.def.h
           67 @@ -149,6 +149,14 @@ static unsigned int mousebg = 0;
           68   * doesn't match the ones requested.
           69   */
           70  static unsigned int defaultattr = 11;
           71 +/// Colors for the entities that are 'highlighted' in normal mode (search
           72 +/// results currently on screen) [Vim Browse].
           73 +static unsigned int highlightBg = 160;
           74 +static unsigned int highlightFg = 15;
           75 +/// Colors for highlighting the current cursor position (row + col) in normal
           76 +/// mode [Vim Browse].
           77 +static unsigned int currentBg = 8;
           78 +static unsigned int currentFg = 15;
           79  
           80  /*
           81   * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set).
           82 @@ -170,10 +178,12 @@ static MouseShortcut mshortcuts[] = {
           83  
           84  /* Internal keyboard shortcuts. */
           85  #define MODKEY Mod1Mask
           86 +#define AltMask Mod1Mask
           87  #define TERMMOD (ControlMask|ShiftMask)
           88  
           89  static Shortcut shortcuts[] = {
           90          /* mask                 keysym          function        argument */
           91 +        { AltMask,              XK_c,           normalMode,     {.i =  0} },
           92          { XK_ANY_MOD,           XK_Break,       sendbreak,      {.i =  0} },
           93          { ControlMask,          XK_Print,       toggleprinter,  {.i =  0} },
           94          { ShiftMask,            XK_Print,       printscreen,    {.i =  0} },
           95 @@ -186,6 +196,8 @@ static Shortcut shortcuts[] = {
           96          { TERMMOD,              XK_Y,           selpaste,       {.i =  0} },
           97          { ShiftMask,            XK_Insert,      selpaste,       {.i =  0} },
           98          { TERMMOD,              XK_Num_Lock,    numlock,        {.i =  0} },
           99 +        { ShiftMask,            XK_Page_Up,     kscrollup,      {.i = -1} },
          100 +        { ShiftMask,            XK_Page_Down,   kscrolldown,    {.i = -1} },
          101  };
          102  
          103  /*
          104 @@ -457,3 +469,45 @@ static char ascii_printable[] =
          105          " !\"#$%&'()*+,-./0123456789:;<=>?"
          106          "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
          107          "`abcdefghijklmnopqrstuvwxyz{|}~";
          108 +
          109 +
          110 +/// word sepearors normal mode
          111 +/// [Vim Browse].
          112 +char wordDelimSmall[] = " \t!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
          113 +char wordDelimLarge[] = " \t"; /// <Word sepearors normal mode (capital W)
          114 +
          115 +/// Shortcusts executed in normal mode (which should not already be in use)
          116 +/// [Vim Browse].
          117 +struct NormalModeShortcuts normalModeShortcuts [] = {
          118 +        { 'R', "?Building\n" },
          119 +        { 'r', "/Building\n" },
          120 +        { 'F', "?: error:\n" },
          121 +        { 'f', "/: error:\n" },
          122 +        { 'Q', "?[Leaving vim, starting execution]\n" },
          123 +        { 'S', "Qf" },
          124 +        { 'X', "?juli@machine\n" },
          125 +        { 'x', "/juli@machine\n" },
          126 +};
          127 +
          128 +size_t const amountNormalModeShortcuts = sizeof(normalModeShortcuts) / sizeof(*normalModeShortcuts);
          129 +
          130 +/// Style of the command string visualized in normal mode in the right corner
          131 +/// [Vim Browse].
          132 +Glyph const styleCommand = {' ', ATTR_ITALIC | ATTR_FAINT, 7, 16};
          133 +/// Style of the search string visualized in normal mode in the right corner.
          134 +/// [Vim Browse].
          135 +Glyph const styleSearch = {' ', ATTR_ITALIC | ATTR_BOLD_FAINT, 7, 16};
          136 +
          137 +/// Colors used in normal mode in order to highlight different operations and
          138 +/// empathise the current position on screen  in  the status area [Vim Browse].
          139 +unsigned int bgCommandYank = 11;
          140 +unsigned int bgCommandVisual = 4;
          141 +unsigned int bgCommandVisualLine = 12;
          142 +
          143 +unsigned int fgCommandYank = 232;
          144 +unsigned int fgCommandVisual = 232;
          145 +unsigned int fgCommandVisualLine = 232;
          146 +
          147 +unsigned int bgPos = 15;
          148 +unsigned int fgPos = 16;
          149 +
          150 diff --git a/config.h b/config.h
          151 new file mode 100644
          152 index 0000000..92b541d
          153 --- /dev/null
          154 +++ b/config.h
          155 @@ -0,0 +1,513 @@
          156 +/* See LICENSE file for copyright and license details. */
          157 +
          158 +/*
          159 + * appearance
          160 + *
          161 + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
          162 + */
          163 +static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true";
          164 +static int borderpx = 2;
          165 +
          166 +/*
          167 + * What program is execed by st depends of these precedence rules:
          168 + * 1: program passed with -e
          169 + * 2: utmp option
          170 + * 3: SHELL environment variable
          171 + * 4: value of shell in /etc/passwd
          172 + * 5: value of shell in config.h
          173 + */
          174 +static char *shell = "/bin/sh";
          175 +char *utmp = NULL;
          176 +char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400";
          177 +
          178 +/* identification sequence returned in DA and DECID */
          179 +char *vtiden = "\033[?6c";
          180 +
          181 +/* Kerning / character bounding-box multipliers */
          182 +static float cwscale = 1.0;
          183 +static float chscale = 1.0;
          184 +
          185 +/*
          186 + * word delimiter string
          187 + *
          188 + * More advanced example: L" `'\"()[]{}"
          189 + */
          190 +wchar_t *worddelimiters = L" ";
          191 +
          192 +/* selection timeouts (in milliseconds) */
          193 +static unsigned int doubleclicktimeout = 300;
          194 +static unsigned int tripleclicktimeout = 600;
          195 +
          196 +/* alt screens */
          197 +int allowaltscreen = 1;
          198 +
          199 +/* frames per second st should at maximum draw to the screen */
          200 +static unsigned int xfps = 120;
          201 +static unsigned int actionfps = 30;
          202 +
          203 +/*
          204 + * blinking timeout (set to 0 to disable blinking) for the terminal blinking
          205 + * attribute.
          206 + */
          207 +static unsigned int blinktimeout = 800;
          208 +
          209 +/*
          210 + * thickness of underline and bar cursors
          211 + */
          212 +static unsigned int cursorthickness = 2;
          213 +
          214 +/*
          215 + * bell volume. It must be a value between -100 and 100. Use 0 for disabling
          216 + * it
          217 + */
          218 +static int bellvolume = 0;
          219 +
          220 +/* default TERM value */
          221 +char *termname = "st-256color";
          222 +
          223 +/*
          224 + * spaces per tab
          225 + *
          226 + * When you are changing this value, don't forget to adapt the »it« value in
          227 + * the st.info and appropriately install the st.info in the environment where
          228 + * you use this st version.
          229 + *
          230 + *        it#$tabspaces,
          231 + *
          232 + * Secondly make sure your kernel is not expanding tabs. When running `stty
          233 + * -a` »tab0« should appear. You can tell the terminal to not expand tabs by
          234 + *  running following command:
          235 + *
          236 + *        stty tabs
          237 + */
          238 +unsigned int tabspaces = 8;
          239 +
          240 +/* Terminal colors (16 first used in escape sequence) */
          241 +static const char *colorname[] = {
          242 +        /* 8 normal colors */
          243 +        "black",
          244 +        "red3",
          245 +        "green3",
          246 +        "yellow3",
          247 +        "blue2",
          248 +        "magenta3",
          249 +        "cyan3",
          250 +        "gray90",
          251 +
          252 +        /* 8 bright colors */
          253 +        "gray50",
          254 +        "red",
          255 +        "green",
          256 +        "yellow",
          257 +        "#5c5cff",
          258 +        "magenta",
          259 +        "cyan",
          260 +        "white",
          261 +
          262 +        [255] = 0,
          263 +
          264 +        /* more colors can be added after 255 to use with DefaultXX */
          265 +        "#cccccc",
          266 +        "#555555",
          267 +};
          268 +
          269 +
          270 +/*
          271 + * Default colors (colorname index)
          272 + * foreground, background, cursor, reverse cursor
          273 + */
          274 +unsigned int defaultfg = 7;
          275 +unsigned int defaultbg = 0;
          276 +static unsigned int defaultcs = 256;
          277 +static unsigned int defaultrcs = 257;
          278 +
          279 +/*
          280 + * Default shape of cursor
          281 + * 2: Block ("█")
          282 + * 4: Underline ("_")
          283 + * 6: Bar ("|")
          284 + * 7: Snowman ("☃")
          285 + */
          286 +static unsigned int cursorshape = 2;
          287 +
          288 +/*
          289 + * Default columns and rows numbers
          290 + */
          291 +
          292 +static unsigned int cols = 80;
          293 +static unsigned int rows = 24;
          294 +
          295 +/*
          296 + * Default colour and shape of the mouse cursor
          297 + */
          298 +static unsigned int mouseshape = XC_xterm;
          299 +static unsigned int mousefg = 7;
          300 +static unsigned int mousebg = 0;
          301 +
          302 +/*
          303 + * Color used to display font attributes when fontconfig selected a font which
          304 + * doesn't match the ones requested.
          305 + */
          306 +static unsigned int defaultattr = 11;
          307 +/// Colors for the entities that are 'highlighted' in normal mode (search
          308 +/// results currently on screen) [Vim Browse].
          309 +static unsigned int highlightBg = 160;
          310 +static unsigned int highlightFg = 15;
          311 +/// Colors for highlighting the current cursor position (row + col) in normal
          312 +/// mode [Vim Browse].
          313 +static unsigned int currentBg = 8;
          314 +static unsigned int currentFg = 15;
          315 +
          316 +/*
          317 + * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set).
          318 + * Note that if you want to use ShiftMask with selmasks, set this to an other
          319 + * modifier, set to 0 to not use it.
          320 + */
          321 +static uint forcemousemod = ShiftMask;
          322 +
          323 +/*
          324 + * Internal mouse shortcuts.
          325 + * Beware that overloading Button1 will disable the selection.
          326 + */
          327 +static MouseShortcut mshortcuts[] = {
          328 +        /* mask                 button   function        argument       release */
          329 +        { XK_ANY_MOD,           Button2, selpaste,       {.i = 0},      1 },
          330 +        { XK_ANY_MOD,           Button4, ttysend,        {.s = "\031"} },
          331 +        { XK_ANY_MOD,           Button5, ttysend,        {.s = "\005"} },
          332 +};
          333 +
          334 +/* Internal keyboard shortcuts. */
          335 +#define MODKEY Mod1Mask
          336 +#define AltMask Mod1Mask
          337 +#define TERMMOD (ControlMask|ShiftMask)
          338 +
          339 +static Shortcut shortcuts[] = {
          340 +        /* mask                 keysym          function        argument */
          341 +        { AltMask,              XK_c,           normalMode,     {.i =  0} },
          342 +        { XK_ANY_MOD,           XK_Break,       sendbreak,      {.i =  0} },
          343 +        { ControlMask,          XK_Print,       toggleprinter,  {.i =  0} },
          344 +        { ShiftMask,            XK_Print,       printscreen,    {.i =  0} },
          345 +        { XK_ANY_MOD,           XK_Print,       printsel,       {.i =  0} },
          346 +        { TERMMOD,              XK_Prior,       zoom,           {.f = +1} },
          347 +        { TERMMOD,              XK_Next,        zoom,           {.f = -1} },
          348 +        { TERMMOD,              XK_Home,        zoomreset,      {.f =  0} },
          349 +        { TERMMOD,              XK_C,           clipcopy,       {.i =  0} },
          350 +        { TERMMOD,              XK_V,           clippaste,      {.i =  0} },
          351 +        { TERMMOD,              XK_Y,           selpaste,       {.i =  0} },
          352 +        { ShiftMask,            XK_Insert,      selpaste,       {.i =  0} },
          353 +        { TERMMOD,              XK_Num_Lock,    numlock,        {.i =  0} },
          354 +        { ShiftMask,            XK_Page_Up,     kscrollup,      {.i = -1} },
          355 +        { ShiftMask,            XK_Page_Down,   kscrolldown,    {.i = -1} },
          356 +};
          357 +
          358 +/*
          359 + * Special keys (change & recompile st.info accordingly)
          360 + *
          361 + * Mask value:
          362 + * * Use XK_ANY_MOD to match the key no matter modifiers state
          363 + * * Use XK_NO_MOD to match the key alone (no modifiers)
          364 + * appkey value:
          365 + * * 0: no value
          366 + * * > 0: keypad application mode enabled
          367 + * *   = 2: term.numlock = 1
          368 + * * < 0: keypad application mode disabled
          369 + * appcursor value:
          370 + * * 0: no value
          371 + * * > 0: cursor application mode enabled
          372 + * * < 0: cursor application mode disabled
          373 + *
          374 + * Be careful with the order of the definitions because st searches in
          375 + * this table sequentially, so any XK_ANY_MOD must be in the last
          376 + * position for a key.
          377 + */
          378 +
          379 +/*
          380 + * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF)
          381 + * to be mapped below, add them to this array.
          382 + */
          383 +static KeySym mappedkeys[] = { -1 };
          384 +
          385 +/*
          386 + * State bits to ignore when matching key or button events.  By default,
          387 + * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored.
          388 + */
          389 +static uint ignoremod = Mod2Mask|XK_SWITCH_MOD;
          390 +
          391 +/*
          392 + * This is the huge key array which defines all compatibility to the Linux
          393 + * world. Please decide about changes wisely.
          394 + */
          395 +static Key key[] = {
          396 +        /* keysym           mask            string      appkey appcursor */
          397 +        { XK_KP_Home,       ShiftMask,      "\033[2J",       0,   -1},
          398 +        { XK_KP_Home,       ShiftMask,      "\033[1;2H",     0,   +1},
          399 +        { XK_KP_Home,       XK_ANY_MOD,     "\033[H",        0,   -1},
          400 +        { XK_KP_Home,       XK_ANY_MOD,     "\033[1~",       0,   +1},
          401 +        { XK_KP_Up,         XK_ANY_MOD,     "\033Ox",       +1,    0},
          402 +        { XK_KP_Up,         XK_ANY_MOD,     "\033[A",        0,   -1},
          403 +        { XK_KP_Up,         XK_ANY_MOD,     "\033OA",        0,   +1},
          404 +        { XK_KP_Down,       XK_ANY_MOD,     "\033Or",       +1,    0},
          405 +        { XK_KP_Down,       XK_ANY_MOD,     "\033[B",        0,   -1},
          406 +        { XK_KP_Down,       XK_ANY_MOD,     "\033OB",        0,   +1},
          407 +        { XK_KP_Left,       XK_ANY_MOD,     "\033Ot",       +1,    0},
          408 +        { XK_KP_Left,       XK_ANY_MOD,     "\033[D",        0,   -1},
          409 +        { XK_KP_Left,       XK_ANY_MOD,     "\033OD",        0,   +1},
          410 +        { XK_KP_Right,      XK_ANY_MOD,     "\033Ov",       +1,    0},
          411 +        { XK_KP_Right,      XK_ANY_MOD,     "\033[C",        0,   -1},
          412 +        { XK_KP_Right,      XK_ANY_MOD,     "\033OC",        0,   +1},
          413 +        { XK_KP_Prior,      ShiftMask,      "\033[5;2~",     0,    0},
          414 +        { XK_KP_Prior,      XK_ANY_MOD,     "\033[5~",       0,    0},
          415 +        { XK_KP_Begin,      XK_ANY_MOD,     "\033[E",        0,    0},
          416 +        { XK_KP_End,        ControlMask,    "\033[J",       -1,    0},
          417 +        { XK_KP_End,        ControlMask,    "\033[1;5F",    +1,    0},
          418 +        { XK_KP_End,        ShiftMask,      "\033[K",       -1,    0},
          419 +        { XK_KP_End,        ShiftMask,      "\033[1;2F",    +1,    0},
          420 +        { XK_KP_End,        XK_ANY_MOD,     "\033[4~",       0,    0},
          421 +        { XK_KP_Next,       ShiftMask,      "\033[6;2~",     0,    0},
          422 +        { XK_KP_Next,       XK_ANY_MOD,     "\033[6~",       0,    0},
          423 +        { XK_KP_Insert,     ShiftMask,      "\033[2;2~",    +1,    0},
          424 +        { XK_KP_Insert,     ShiftMask,      "\033[4l",      -1,    0},
          425 +        { XK_KP_Insert,     ControlMask,    "\033[L",       -1,    0},
          426 +        { XK_KP_Insert,     ControlMask,    "\033[2;5~",    +1,    0},
          427 +        { XK_KP_Insert,     XK_ANY_MOD,     "\033[4h",      -1,    0},
          428 +        { XK_KP_Insert,     XK_ANY_MOD,     "\033[2~",      +1,    0},
          429 +        { XK_KP_Delete,     ControlMask,    "\033[M",       -1,    0},
          430 +        { XK_KP_Delete,     ControlMask,    "\033[3;5~",    +1,    0},
          431 +        { XK_KP_Delete,     ShiftMask,      "\033[2K",      -1,    0},
          432 +        { XK_KP_Delete,     ShiftMask,      "\033[3;2~",    +1,    0},
          433 +        { XK_KP_Delete,     XK_ANY_MOD,     "\033[P",       -1,    0},
          434 +        { XK_KP_Delete,     XK_ANY_MOD,     "\033[3~",      +1,    0},
          435 +        { XK_KP_Multiply,   XK_ANY_MOD,     "\033Oj",       +2,    0},
          436 +        { XK_KP_Add,        XK_ANY_MOD,     "\033Ok",       +2,    0},
          437 +        { XK_KP_Enter,      XK_ANY_MOD,     "\033OM",       +2,    0},
          438 +        { XK_KP_Enter,      XK_ANY_MOD,     "\r",           -1,    0},
          439 +        { XK_KP_Subtract,   XK_ANY_MOD,     "\033Om",       +2,    0},
          440 +        { XK_KP_Decimal,    XK_ANY_MOD,     "\033On",       +2,    0},
          441 +        { XK_KP_Divide,     XK_ANY_MOD,     "\033Oo",       +2,    0},
          442 +        { XK_KP_0,          XK_ANY_MOD,     "\033Op",       +2,    0},
          443 +        { XK_KP_1,          XK_ANY_MOD,     "\033Oq",       +2,    0},
          444 +        { XK_KP_2,          XK_ANY_MOD,     "\033Or",       +2,    0},
          445 +        { XK_KP_3,          XK_ANY_MOD,     "\033Os",       +2,    0},
          446 +        { XK_KP_4,          XK_ANY_MOD,     "\033Ot",       +2,    0},
          447 +        { XK_KP_5,          XK_ANY_MOD,     "\033Ou",       +2,    0},
          448 +        { XK_KP_6,          XK_ANY_MOD,     "\033Ov",       +2,    0},
          449 +        { XK_KP_7,          XK_ANY_MOD,     "\033Ow",       +2,    0},
          450 +        { XK_KP_8,          XK_ANY_MOD,     "\033Ox",       +2,    0},
          451 +        { XK_KP_9,          XK_ANY_MOD,     "\033Oy",       +2,    0},
          452 +        { XK_Up,            ShiftMask,      "\033[1;2A",     0,    0},
          453 +        { XK_Up,            Mod1Mask,       "\033[1;3A",     0,    0},
          454 +        { XK_Up,         ShiftMask|Mod1Mask,"\033[1;4A",     0,    0},
          455 +        { XK_Up,            ControlMask,    "\033[1;5A",     0,    0},
          456 +        { XK_Up,      ShiftMask|ControlMask,"\033[1;6A",     0,    0},
          457 +        { XK_Up,       ControlMask|Mod1Mask,"\033[1;7A",     0,    0},
          458 +        { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A",  0,    0},
          459 +        { XK_Up,            XK_ANY_MOD,     "\033[A",        0,   -1},
          460 +        { XK_Up,            XK_ANY_MOD,     "\033OA",        0,   +1},
          461 +        { XK_Down,          ShiftMask,      "\033[1;2B",     0,    0},
          462 +        { XK_Down,          Mod1Mask,       "\033[1;3B",     0,    0},
          463 +        { XK_Down,       ShiftMask|Mod1Mask,"\033[1;4B",     0,    0},
          464 +        { XK_Down,          ControlMask,    "\033[1;5B",     0,    0},
          465 +        { XK_Down,    ShiftMask|ControlMask,"\033[1;6B",     0,    0},
          466 +        { XK_Down,     ControlMask|Mod1Mask,"\033[1;7B",     0,    0},
          467 +        { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0,    0},
          468 +        { XK_Down,          XK_ANY_MOD,     "\033[B",        0,   -1},
          469 +        { XK_Down,          XK_ANY_MOD,     "\033OB",        0,   +1},
          470 +        { XK_Left,          ShiftMask,      "\033[1;2D",     0,    0},
          471 +        { XK_Left,          Mod1Mask,       "\033[1;3D",     0,    0},
          472 +        { XK_Left,       ShiftMask|Mod1Mask,"\033[1;4D",     0,    0},
          473 +        { XK_Left,          ControlMask,    "\033[1;5D",     0,    0},
          474 +        { XK_Left,    ShiftMask|ControlMask,"\033[1;6D",     0,    0},
          475 +        { XK_Left,     ControlMask|Mod1Mask,"\033[1;7D",     0,    0},
          476 +        { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0,    0},
          477 +        { XK_Left,          XK_ANY_MOD,     "\033[D",        0,   -1},
          478 +        { XK_Left,          XK_ANY_MOD,     "\033OD",        0,   +1},
          479 +        { XK_Right,         ShiftMask,      "\033[1;2C",     0,    0},
          480 +        { XK_Right,         Mod1Mask,       "\033[1;3C",     0,    0},
          481 +        { XK_Right,      ShiftMask|Mod1Mask,"\033[1;4C",     0,    0},
          482 +        { XK_Right,         ControlMask,    "\033[1;5C",     0,    0},
          483 +        { XK_Right,   ShiftMask|ControlMask,"\033[1;6C",     0,    0},
          484 +        { XK_Right,    ControlMask|Mod1Mask,"\033[1;7C",     0,    0},
          485 +        { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0,   0},
          486 +        { XK_Right,         XK_ANY_MOD,     "\033[C",        0,   -1},
          487 +        { XK_Right,         XK_ANY_MOD,     "\033OC",        0,   +1},
          488 +        { XK_ISO_Left_Tab,  ShiftMask,      "\033[Z",        0,    0},
          489 +        { XK_Return,        Mod1Mask,       "\033\r",        0,    0},
          490 +        { XK_Return,        XK_ANY_MOD,     "\r",            0,    0},
          491 +        { XK_Insert,        ShiftMask,      "\033[4l",      -1,    0},
          492 +        { XK_Insert,        ShiftMask,      "\033[2;2~",    +1,    0},
          493 +        { XK_Insert,        ControlMask,    "\033[L",       -1,    0},
          494 +        { XK_Insert,        ControlMask,    "\033[2;5~",    +1,    0},
          495 +        { XK_Insert,        XK_ANY_MOD,     "\033[4h",      -1,    0},
          496 +        { XK_Insert,        XK_ANY_MOD,     "\033[2~",      +1,    0},
          497 +        { XK_Delete,        ControlMask,    "\033[M",       -1,    0},
          498 +        { XK_Delete,        ControlMask,    "\033[3;5~",    +1,    0},
          499 +        { XK_Delete,        ShiftMask,      "\033[2K",      -1,    0},
          500 +        { XK_Delete,        ShiftMask,      "\033[3;2~",    +1,    0},
          501 +        { XK_Delete,        XK_ANY_MOD,     "\033[P",       -1,    0},
          502 +        { XK_Delete,        XK_ANY_MOD,     "\033[3~",      +1,    0},
          503 +        { XK_BackSpace,     XK_NO_MOD,      "\177",          0,    0},
          504 +        { XK_BackSpace,     Mod1Mask,       "\033\177",      0,    0},
          505 +        { XK_Home,          ShiftMask,      "\033[2J",       0,   -1},
          506 +        { XK_Home,          ShiftMask,      "\033[1;2H",     0,   +1},
          507 +        { XK_Home,          XK_ANY_MOD,     "\033[H",        0,   -1},
          508 +        { XK_Home,          XK_ANY_MOD,     "\033[1~",       0,   +1},
          509 +        { XK_End,           ControlMask,    "\033[J",       -1,    0},
          510 +        { XK_End,           ControlMask,    "\033[1;5F",    +1,    0},
          511 +        { XK_End,           ShiftMask,      "\033[K",       -1,    0},
          512 +        { XK_End,           ShiftMask,      "\033[1;2F",    +1,    0},
          513 +        { XK_End,           XK_ANY_MOD,     "\033[4~",       0,    0},
          514 +        { XK_Prior,         ControlMask,    "\033[5;5~",     0,    0},
          515 +        { XK_Prior,         ShiftMask,      "\033[5;2~",     0,    0},
          516 +        { XK_Prior,         XK_ANY_MOD,     "\033[5~",       0,    0},
          517 +        { XK_Next,          ControlMask,    "\033[6;5~",     0,    0},
          518 +        { XK_Next,          ShiftMask,      "\033[6;2~",     0,    0},
          519 +        { XK_Next,          XK_ANY_MOD,     "\033[6~",       0,    0},
          520 +        { XK_F1,            XK_NO_MOD,      "\033OP" ,       0,    0},
          521 +        { XK_F1, /* F13 */  ShiftMask,      "\033[1;2P",     0,    0},
          522 +        { XK_F1, /* F25 */  ControlMask,    "\033[1;5P",     0,    0},
          523 +        { XK_F1, /* F37 */  Mod4Mask,       "\033[1;6P",     0,    0},
          524 +        { XK_F1, /* F49 */  Mod1Mask,       "\033[1;3P",     0,    0},
          525 +        { XK_F1, /* F61 */  Mod3Mask,       "\033[1;4P",     0,    0},
          526 +        { XK_F2,            XK_NO_MOD,      "\033OQ" ,       0,    0},
          527 +        { XK_F2, /* F14 */  ShiftMask,      "\033[1;2Q",     0,    0},
          528 +        { XK_F2, /* F26 */  ControlMask,    "\033[1;5Q",     0,    0},
          529 +        { XK_F2, /* F38 */  Mod4Mask,       "\033[1;6Q",     0,    0},
          530 +        { XK_F2, /* F50 */  Mod1Mask,       "\033[1;3Q",     0,    0},
          531 +        { XK_F2, /* F62 */  Mod3Mask,       "\033[1;4Q",     0,    0},
          532 +        { XK_F3,            XK_NO_MOD,      "\033OR" ,       0,    0},
          533 +        { XK_F3, /* F15 */  ShiftMask,      "\033[1;2R",     0,    0},
          534 +        { XK_F3, /* F27 */  ControlMask,    "\033[1;5R",     0,    0},
          535 +        { XK_F3, /* F39 */  Mod4Mask,       "\033[1;6R",     0,    0},
          536 +        { XK_F3, /* F51 */  Mod1Mask,       "\033[1;3R",     0,    0},
          537 +        { XK_F3, /* F63 */  Mod3Mask,       "\033[1;4R",     0,    0},
          538 +        { XK_F4,            XK_NO_MOD,      "\033OS" ,       0,    0},
          539 +        { XK_F4, /* F16 */  ShiftMask,      "\033[1;2S",     0,    0},
          540 +        { XK_F4, /* F28 */  ControlMask,    "\033[1;5S",     0,    0},
          541 +        { XK_F4, /* F40 */  Mod4Mask,       "\033[1;6S",     0,    0},
          542 +        { XK_F4, /* F52 */  Mod1Mask,       "\033[1;3S",     0,    0},
          543 +        { XK_F5,            XK_NO_MOD,      "\033[15~",      0,    0},
          544 +        { XK_F5, /* F17 */  ShiftMask,      "\033[15;2~",    0,    0},
          545 +        { XK_F5, /* F29 */  ControlMask,    "\033[15;5~",    0,    0},
          546 +        { XK_F5, /* F41 */  Mod4Mask,       "\033[15;6~",    0,    0},
          547 +        { XK_F5, /* F53 */  Mod1Mask,       "\033[15;3~",    0,    0},
          548 +        { XK_F6,            XK_NO_MOD,      "\033[17~",      0,    0},
          549 +        { XK_F6, /* F18 */  ShiftMask,      "\033[17;2~",    0,    0},
          550 +        { XK_F6, /* F30 */  ControlMask,    "\033[17;5~",    0,    0},
          551 +        { XK_F6, /* F42 */  Mod4Mask,       "\033[17;6~",    0,    0},
          552 +        { XK_F6, /* F54 */  Mod1Mask,       "\033[17;3~",    0,    0},
          553 +        { XK_F7,            XK_NO_MOD,      "\033[18~",      0,    0},
          554 +        { XK_F7, /* F19 */  ShiftMask,      "\033[18;2~",    0,    0},
          555 +        { XK_F7, /* F31 */  ControlMask,    "\033[18;5~",    0,    0},
          556 +        { XK_F7, /* F43 */  Mod4Mask,       "\033[18;6~",    0,    0},
          557 +        { XK_F7, /* F55 */  Mod1Mask,       "\033[18;3~",    0,    0},
          558 +        { XK_F8,            XK_NO_MOD,      "\033[19~",      0,    0},
          559 +        { XK_F8, /* F20 */  ShiftMask,      "\033[19;2~",    0,    0},
          560 +        { XK_F8, /* F32 */  ControlMask,    "\033[19;5~",    0,    0},
          561 +        { XK_F8, /* F44 */  Mod4Mask,       "\033[19;6~",    0,    0},
          562 +        { XK_F8, /* F56 */  Mod1Mask,       "\033[19;3~",    0,    0},
          563 +        { XK_F9,            XK_NO_MOD,      "\033[20~",      0,    0},
          564 +        { XK_F9, /* F21 */  ShiftMask,      "\033[20;2~",    0,    0},
          565 +        { XK_F9, /* F33 */  ControlMask,    "\033[20;5~",    0,    0},
          566 +        { XK_F9, /* F45 */  Mod4Mask,       "\033[20;6~",    0,    0},
          567 +        { XK_F9, /* F57 */  Mod1Mask,       "\033[20;3~",    0,    0},
          568 +        { XK_F10,           XK_NO_MOD,      "\033[21~",      0,    0},
          569 +        { XK_F10, /* F22 */ ShiftMask,      "\033[21;2~",    0,    0},
          570 +        { XK_F10, /* F34 */ ControlMask,    "\033[21;5~",    0,    0},
          571 +        { XK_F10, /* F46 */ Mod4Mask,       "\033[21;6~",    0,    0},
          572 +        { XK_F10, /* F58 */ Mod1Mask,       "\033[21;3~",    0,    0},
          573 +        { XK_F11,           XK_NO_MOD,      "\033[23~",      0,    0},
          574 +        { XK_F11, /* F23 */ ShiftMask,      "\033[23;2~",    0,    0},
          575 +        { XK_F11, /* F35 */ ControlMask,    "\033[23;5~",    0,    0},
          576 +        { XK_F11, /* F47 */ Mod4Mask,       "\033[23;6~",    0,    0},
          577 +        { XK_F11, /* F59 */ Mod1Mask,       "\033[23;3~",    0,    0},
          578 +        { XK_F12,           XK_NO_MOD,      "\033[24~",      0,    0},
          579 +        { XK_F12, /* F24 */ ShiftMask,      "\033[24;2~",    0,    0},
          580 +        { XK_F12, /* F36 */ ControlMask,    "\033[24;5~",    0,    0},
          581 +        { XK_F12, /* F48 */ Mod4Mask,       "\033[24;6~",    0,    0},
          582 +        { XK_F12, /* F60 */ Mod1Mask,       "\033[24;3~",    0,    0},
          583 +        { XK_F13,           XK_NO_MOD,      "\033[1;2P",     0,    0},
          584 +        { XK_F14,           XK_NO_MOD,      "\033[1;2Q",     0,    0},
          585 +        { XK_F15,           XK_NO_MOD,      "\033[1;2R",     0,    0},
          586 +        { XK_F16,           XK_NO_MOD,      "\033[1;2S",     0,    0},
          587 +        { XK_F17,           XK_NO_MOD,      "\033[15;2~",    0,    0},
          588 +        { XK_F18,           XK_NO_MOD,      "\033[17;2~",    0,    0},
          589 +        { XK_F19,           XK_NO_MOD,      "\033[18;2~",    0,    0},
          590 +        { XK_F20,           XK_NO_MOD,      "\033[19;2~",    0,    0},
          591 +        { XK_F21,           XK_NO_MOD,      "\033[20;2~",    0,    0},
          592 +        { XK_F22,           XK_NO_MOD,      "\033[21;2~",    0,    0},
          593 +        { XK_F23,           XK_NO_MOD,      "\033[23;2~",    0,    0},
          594 +        { XK_F24,           XK_NO_MOD,      "\033[24;2~",    0,    0},
          595 +        { XK_F25,           XK_NO_MOD,      "\033[1;5P",     0,    0},
          596 +        { XK_F26,           XK_NO_MOD,      "\033[1;5Q",     0,    0},
          597 +        { XK_F27,           XK_NO_MOD,      "\033[1;5R",     0,    0},
          598 +        { XK_F28,           XK_NO_MOD,      "\033[1;5S",     0,    0},
          599 +        { XK_F29,           XK_NO_MOD,      "\033[15;5~",    0,    0},
          600 +        { XK_F30,           XK_NO_MOD,      "\033[17;5~",    0,    0},
          601 +        { XK_F31,           XK_NO_MOD,      "\033[18;5~",    0,    0},
          602 +        { XK_F32,           XK_NO_MOD,      "\033[19;5~",    0,    0},
          603 +        { XK_F33,           XK_NO_MOD,      "\033[20;5~",    0,    0},
          604 +        { XK_F34,           XK_NO_MOD,      "\033[21;5~",    0,    0},
          605 +        { XK_F35,           XK_NO_MOD,      "\033[23;5~",    0,    0},
          606 +};
          607 +
          608 +/*
          609 + * Selection types' masks.
          610 + * Use the same masks as usual.
          611 + * Button1Mask is always unset, to make masks match between ButtonPress.
          612 + * ButtonRelease and MotionNotify.
          613 + * If no match is found, regular selection is used.
          614 + */
          615 +static uint selmasks[] = {
          616 +        [SEL_RECTANGULAR] = Mod1Mask,
          617 +};
          618 +
          619 +/*
          620 + * Printable characters in ASCII, used to estimate the advance width
          621 + * of single wide characters.
          622 + */
          623 +static char ascii_printable[] =
          624 +        " !\"#$%&'()*+,-./0123456789:;<=>?"
          625 +        "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
          626 +        "`abcdefghijklmnopqrstuvwxyz{|}~";
          627 +
          628 +
          629 +/// word sepearors normal mode
          630 +/// [Vim Browse].
          631 +char wordDelimSmall[] = " \t!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
          632 +char wordDelimLarge[] = " \t"; /// <Word sepearors normal mode (capital W)
          633 +
          634 +/// Shortcusts executed in normal mode (which should not already be in use)
          635 +/// [Vim Browse].
          636 +struct NormalModeShortcuts normalModeShortcuts [] = {
          637 +        { 'R', "?Building\n" },
          638 +        { 'r', "/Building\n" },
          639 +        { 'F', "?: error:\n" },
          640 +        { 'f', "/: error:\n" },
          641 +        { 'Q', "?[Leaving vim, starting execution]\n" },
          642 +        { 'S', "Qf" },
          643 +        { 'X', "?juli@machine\n" },
          644 +        { 'x', "/juli@machine\n" },
          645 +};
          646 +
          647 +size_t const amountNormalModeShortcuts = sizeof(normalModeShortcuts) / sizeof(*normalModeShortcuts);
          648 +
          649 +/// Style of the command string visualized in normal mode in the right corner
          650 +/// [Vim Browse].
          651 +Glyph const styleCommand = {' ', ATTR_ITALIC | ATTR_FAINT, 7, 16};
          652 +/// Style of the search string visualized in normal mode in the right corner.
          653 +/// [Vim Browse].
          654 +Glyph const styleSearch = {' ', ATTR_ITALIC | ATTR_BOLD_FAINT, 7, 16};
          655 +
          656 +/// Colors used in normal mode in order to highlight different operations and
          657 +/// empathise the current position on screen  in  the status area [Vim Browse].
          658 +unsigned int bgCommandYank = 11;
          659 +unsigned int bgCommandVisual = 4;
          660 +unsigned int bgCommandVisualLine = 12;
          661 +
          662 +unsigned int fgCommandYank = 232;
          663 +unsigned int fgCommandVisual = 232;
          664 +unsigned int fgCommandVisualLine = 232;
          665 +
          666 +unsigned int bgPos = 15;
          667 +unsigned int fgPos = 16;
          668 +
          669 diff --git a/dynamicArray.h b/dynamicArray.h
          670 new file mode 100644
          671 index 0000000..8af5d9c
          672 --- /dev/null
          673 +++ b/dynamicArray.h
          674 @@ -0,0 +1,175 @@
          675 +#ifndef DYNAMIC_ARRAY_H
          676 +#define DYNAMIC_ARRAY_H
          677 +
          678 +#include "error.h"
          679 +
          680 +#include <stdint.h>
          681 +#include <stdlib.h>
          682 +#include <string.h>
          683 +#include <stdbool.h>
          684 +
          685 +/// Struct for which this file offers functionality in order to expand the array
          686 +/// and set / get its content.
          687 +typedef struct DynamicArray {
          688 +        /// Size of the datatype contained in the array.
          689 +        uint8_t itemSize;
          690 +        /// Amount of bytes currently initialized
          691 +        uint32_t index;
          692 +        /// Amount of bytes currently reserved (not necessarily initialized)
          693 +        uint32_t allocated;
          694 +        /// Actual content.
          695 +        char* content;
          696 +} DynamicArray;
          697 +
          698 +#define EXPAND_STEP 15
          699 +
          700 +/// Default initializers for the dynamic array.
          701 +#define CHAR_ARRAY  {1, 0, 0, NULL}
          702 +#define WORD_ARRAY  {2, 0, 0, NULL}
          703 +#define DWORD_ARRAY {4, 0, 0, NULL}
          704 +#define QWORD_ARRAY {8, 0, 0, NULL}
          705 +/// (Wasteful) utf-8 array, that always used 4 bytes in order to display a
          706 +/// character, even if the space is not required.
          707 +#define UTF8_ARRAY  DWORD_ARRAY
          708 +
          709 +/// Check that at least \p bytes are allocated, if true implying that
          710 +/// \p s->content[\bytes - 1] is allocated.
          711 +static inline bool
          712 +isAllocated(DynamicArray const *s, uint32_t bytes) {
          713 +        return s != NULL && s->allocated >= bytes;
          714 +}
          715 +
          716 +/// @see #isAllocated
          717 +static inline bool
          718 +isInitialized(DynamicArray const *s, uint32_t bytes) {
          719 +        return s != NULL && s->index >= bytes;
          720 +}
          721 +
          722 +/// Return the next element in \p s and increment index without checking bounds.
          723 +static inline char*
          724 +gnext(DynamicArray *s) {
          725 +        ENSURE(s!=NULL, return NULL);
          726 +        ENSURE(s->index % s->itemSize == 0 && "(index not aligned)",
          727 +                        s->index += s->itemSize - (s->index % s->itemSize));
          728 +        ENSURE(isAllocated(s, s->index + 2 * s->itemSize), return NULL);
          729 +        return s->content + (s->index += s->itemSize);
          730 +}
          731 +
          732 +/// View element \p i in \p s.
          733 +static inline char*
          734 +view(DynamicArray const * s, uint32_t i) {
          735 +        ENSURE((s != NULL) && isAllocated(s, (i+1) * s->itemSize), return NULL);
          736 +        return s->content + i*s->itemSize;
          737 +}
          738 +
          739 +/// Inspect element content[size() - 1 - i].
          740 +static inline char *
          741 +viewEnd(DynamicArray const *s, uint32_t i) {
          742 +        ENSURE((s != NULL) && isInitialized(s, i * s->itemSize), return NULL);
          743 +        ENSURE(s->index%s->itemSize == 0 && "(index not aligned)", return NULL);
          744 +        return s->content + s->index - (i + 1) * s->itemSize;
          745 +}
          746 +
          747 +/// Set conent without applying
          748 +static inline bool
          749 +setValues(DynamicArray* s, char const *vals, uint32_t amount) {
          750 +        ENSURE(vals != NULL, return false);
          751 +        ENSURE((s != NULL) && isAllocated(s, s->index + amount), return false);
          752 +        memcpy(s->content + s->index, vals, amount);
          753 +        return true;
          754 +}
          755 +
          756 +static inline bool
          757 +snext(DynamicArray* s, char const *vals, uint32_t amount) {
          758 +        bool const success = setValues(s, vals, amount);
          759 +        ENSURE(success, return false);
          760 +        uint8_t const rest = amount % s->itemSize;
          761 +        uint32_t const newSize = s->index + amount + (rest ? s->itemSize : 0);
          762 +        ENSURE(isAllocated(s, newSize), return false);
          763 +        s->index = newSize;
          764 +        return true;
          765 +}
          766 +
          767 +/// Empty \p s.
          768 +static inline void
          769 +empty(DynamicArray* s) {
          770 +        ENSURE((s != NULL), return);
          771 +        s->index = 0;
          772 +}
          773 +
          774 +/// Check if \p s has initialized content (which can be the case even if memory
          775 +/// is allocated).
          776 +static inline bool
          777 +isEmpty(DynamicArray const * s) {
          778 +        ENSURE((s != NULL), return true);
          779 +        return s->index == 0;
          780 +}
          781 +
          782 +static inline int
          783 +size(DynamicArray const * s) {
          784 +        ENSURE(s != NULL, return 0);
          785 +        ENSURE(s->itemSize != 0, return 0);
          786 +        return s->index / s->itemSize;
          787 +}
          788 +
          789 +static inline void
          790 +pop(DynamicArray* s) {
          791 +        ENSURE((s != NULL), return);
          792 +        ENSURE(s->index % s->itemSize == 0 && "(index not aligned)",
          793 +                        s->index += s->itemSize - (s->index % s->itemSize));
          794 +        ENSURE(isInitialized(s, s->itemSize), return);
          795 +        s->index -= s->itemSize;
          796 +}
          797 +
          798 +static inline bool
          799 +checkSetNext(DynamicArray *s, char const *c, uint32_t amount) {
          800 +        ENSURE(s != NULL && c != NULL, return false);
          801 +        if (s->allocated < s->index + s->itemSize * amount) {
          802 +                uint32_t const diff = s->index+s->itemSize*amount-s->allocated;
          803 +                uint32_t const newAlloSize = s->allocated + (diff > EXPAND_STEP
          804 +                                ? diff : EXPAND_STEP) * s->itemSize;
          805 +                char* tmp = realloc(s->content, newAlloSize);
          806 +                if (tmp == NULL) { return false; }
          807 +                s->allocated = newAlloSize;
          808 +                s->content = tmp;
          809 +                assert(s->allocated >= s->index + s->itemSize * amount);
          810 +        }
          811 +        if (amount) { snext(s, c, amount); }
          812 +        return true;
          813 +}
          814 +
          815 +static inline bool
          816 +checkSetNextV(DynamicArray *s, char const c) {
          817 +        return checkSetNext(s, &c, 1);
          818 +}
          819 +
          820 +static inline bool
          821 +checkSetNextP(DynamicArray *s, char const *c) {
          822 +        ENSURE(c != NULL, return false);
          823 +        return checkSetNext(s, c, strlen(c));
          824 +}
          825 +
          826 +/// Expand the currently initialized content in \p s and the allocated chunk of
          827 +/// memory if required.
          828 +static char *
          829 +expand(DynamicArray *s) {
          830 +        ENSURE(s != NULL, return NULL);
          831 +        if (s->allocated < s->index + s->itemSize) {
          832 +                uint32_t const diff = s->index + s->itemSize - s->allocated;
          833 +                uint32_t const newAlloSize = s->allocated + (diff > EXPAND_STEP
          834 +                                ? diff : EXPAND_STEP) * s->itemSize;
          835 +                char* tmp = realloc(s->content, newAlloSize);
          836 +                if (tmp == NULL) { return NULL; }
          837 +                s->allocated = newAlloSize;
          838 +                s->content = tmp;
          839 +                assert(s->allocated >= s->index + s->itemSize);
          840 +        }
          841 +        s->index+=s->itemSize;
          842 +        return viewEnd(s, 0);
          843 +}
          844 +
          845 +#define append(s, c) checkSetNext((s), (char const *) (c), (s)->itemSize)
          846 +#define appendPartial(s, c, i) checkSetNext((s), (char const *) (c), (i))
          847 +
          848 +
          849 +#endif // DYNAMIC_ARRAY_H
          850 diff --git a/error.h b/error.h
          851 new file mode 100644
          852 index 0000000..91c621f
          853 --- /dev/null
          854 +++ b/error.h
          855 @@ -0,0 +1,47 @@
          856 +#ifndef ERROR_H
          857 +#define ERROR_H
          858 +
          859 +#include <assert.h>
          860 +
          861 +// Flag which determines whether to fail if a required condition is not met, or
          862 +// to adapt the condition in order to work properly.
          863 +// Attention: Be sure to perform a clean build after you alter preprocessor
          864 +//            directives / definitions.
          865 +//#define FAIL_ON_ERROR
          866 +
          867 +#include <stdio.h>
          868 +
          869 +///
          870 +/// Function used in case the fail-on-error mode is disabled (via definition)
          871 +/// to report errors. In debug production mode, alias st to st 2> error.log.
          872 +static void reportError(char const * cond, char const * stt, char const * file,
          873 +                unsigned int line ) {
          874 +        unsigned int const maxErrorCount = 100;
          875 +        static unsigned int errorCount = 0;
          876 +        if (++errorCount == 1) {
          877 +                printf("Report the following bug to "
          878 +                                "https://github.com/juliusHuelsmann/st.\n");
          879 +        }
          880 +        if (errorCount < maxErrorCount) {
          881 +                printf("Bug:\tCondition '%s' evaluates to false.\n\tPerforming"
          882 +                                " '%s' to counteract.\n\tFile:%s:%u\n",
          883 +                                cond, stt, file, line);
          884 +        } else if (errorCount == maxErrorCount) {
          885 +                printf("Max amount of reported errors %u is reached. From here"
          886 +                                "on, no additional errors will be reported.\n",
          887 +                                maxErrorCount);
          888 +        }
          889 +}
          890 +
          891 +/// Note that everyting condition checked / endforced with #ENSURE is
          892 +/// considered an error, and behaves like an error depending on the flag.
          893 +#ifdef FAIL_ON_ERROR
          894 +#define ENSURE(cond, stt) assert(cond);
          895 +#else // FAIL_ON_ERROR
          896 +#define ENSURE(cond, stt) if (!(cond)) {                                       \
          897 +                                  reportError(#cond, #stt, __FILE__, __LINE__);  \
          898 +                                  stt;                                           \
          899 +                          }
          900 +#endif // FAIL_ON_ERROR
          901 +
          902 +#endif // ERROR_H
          903 diff --git a/glyph.h b/glyph.h
          904 new file mode 100644
          905 index 0000000..84aa252
          906 --- /dev/null
          907 +++ b/glyph.h
          908 @@ -0,0 +1,30 @@
          909 +#ifndef LINE_H
          910 +#define LINE_H
          911 +
          912 +//
          913 +// Contains the representation of the entities in the buffer (Line, Gylph), that
          914 +// is used by every part of the software implmeneting terminal logic.
          915 +//
          916 +
          917 +#include <stdint.h>
          918 +
          919 +enum selection_type {
          920 +        SEL_REGULAR = 1,
          921 +        SEL_RECTANGULAR = 2
          922 +};
          923 +
          924 +typedef uint_least32_t Rune;
          925 +
          926 +#define Glyph Glyph_
          927 +
          928 +typedef struct {
          929 +        Rune u;           /* character code */
          930 +        unsigned short mode;      /* attribute flags */
          931 +        uint32_t fg;      /* foreground  */
          932 +        uint32_t bg;      /* background  */
          933 +} Glyph;
          934 +
          935 +
          936 +typedef Glyph *Line;
          937 +
          938 +#endif // LINE_H
          939 diff --git a/normalMode.c b/normalMode.c
          940 new file mode 100644
          941 index 0000000..59a5a89
          942 --- /dev/null
          943 +++ b/normalMode.c
          944 @@ -0,0 +1,750 @@
          945 +/* See LICENSE for license details. */
          946 +#include "normalMode.h"
          947 +#include "dynamicArray.h"
          948 +#include "term.h"
          949 +#include "win.h"
          950 +#include "error.h"
          951 +
          952 +#include <X11/keysym.h>
          953 +#include <X11/XKBlib.h>
          954 +
          955 +#include <ctype.h>
          956 +#include <stdio.h>
          957 +#include <limits.h>
          958 +#include <math.h>
          959 +
          960 +#define LEN(a)                 (sizeof(a) / sizeof(a)[0])
          961 +#define BETWEEN(x, a, b)       ((a) <= (x) && (x) <= (b))
          962 +#define FALLTHROUGH            __attribute__((fallthrough));
          963 +#define SEC(var,ini,h,r)       var = ini; if (!var) { h; return r; }
          964 +#define EXPAND(v1,v2,r)        char *SEC(v1, expand(v2), empty(v2), true)
          965 +#define currentCommand         (toggle ? &commandHist0 : &commandHist1)
          966 +#define lastCommand            (toggle ? &commandHist1 : &commandHist0)
          967 +
          968 +//
          969 +// Interface to the terminal
          970 +extern Glyph const styleCommand, styleSearch;
          971 +extern NormalModeShortcuts normalModeShortcuts[];
          972 +extern size_t const amountNormalModeShortcuts;
          973 +extern char wordDelimSmall[];
          974 +extern char wordDelimLarge[];
          975 +extern unsigned int fgCommandYank, fgCommandVisual, fgCommandVisualLine,
          976 +       bgCommandYank, bgCommandVisual, bgCommandVisualLine, bgPos, fgPos;
          977 +
          978 +extern void selclear(void);
          979 +extern void tsetdirt(int, int);
          980 +extern size_t utf8encode(Rune, char *);
          981 +extern size_t utf8decode(const char *, Rune *, size_t);
          982 +extern size_t utf8decodebyte(char c, size_t *i);
          983 +
          984 +extern void selextend(int, int, int, int, int);
          985 +extern void selstart(int, int, int, int);
          986 +extern char *getsel(void);
          987 +extern void tfulldirt(void);
          988 +
          989 +//
          990 +// `Private` structs
          991 +typedef struct { uint32_t x; uint32_t y; uint32_t yScr; } Position;
          992 +
          993 +/// Entire normal mode state, consisting of an operation and a motion.
          994 +typedef struct {
          995 +        Position initialPosition;
          996 +        struct OperationState {
          997 +                enum Operation {
          998 +                        noop = ' ', visual='v', visualLine='V', yank = 'y' } op;
          999 +                Position startPosition;
         1000 +                enum Infix { infix_none = 0, infix_i = 1, infix_a = 2, } infix;
         1001 +        } command;
         1002 +        struct MotionState {
         1003 +                uint32_t amount;
         1004 +                enum Search {none, forward, backward} search;
         1005 +                Position searchPosition;
         1006 +                bool finished;
         1007 +        } motion;
         1008 +} NormalModeState;
         1009 +
         1010 +/// Default state if no operation is performed.
         1011 +NormalModeState defaultNormalMode = {
         1012 +        {0,0,0},    {noop, {0, 0, 0}, false},   {0, none, {0, 0, 0}, true}
         1013 +};
         1014 +NormalModeState stateVB = {
         1015 +        {0,0,0},    {noop, {0, 0, 0}, false},   {0, none, {0, 0, 0}, true}
         1016 +};
         1017 +
         1018 +DynamicArray searchString =  UTF8_ARRAY;
         1019 +DynamicArray commandHist0 =  UTF8_ARRAY;
         1020 +DynamicArray commandHist1 =  UTF8_ARRAY;
         1021 +DynamicArray highlights   = DWORD_ARRAY;
         1022 +
         1023 +/// History command toggle
         1024 +static bool toggle = false;
         1025 +
         1026 +//
         1027 +// Utility functions
         1028 +static inline int intervalDiff(int v, int a, int b) {
         1029 +        return (v < a) ? (v - a) : ((v > b) ? (v - b) : 0);
         1030 +}
         1031 +static inline void swap(DynamicArray *const a, DynamicArray *const b) {
         1032 +        DynamicArray tmp = *a; *a = *b; *b = tmp;
         1033 +}
         1034 +static inline int max(int a, int b) { return a > b ? a : b; }
         1035 +static inline int min(int a, int b) { return a < b ? a : b; }
         1036 +static inline int mod(int a, int b) { for (; a < 0; a += b); return a % b; }
         1037 +static inline bool contains (char c, char const * values, uint32_t memSize) {
         1038 +        ENSURE(values != NULL, return false);
         1039 +        for (uint32_t i = 0; i < memSize; ++i) if (c == values[i]) return true;
         1040 +        return false;
         1041 +}
         1042 +static inline void applyPosition(Position const *pos) {
         1043 +        ENSURE(pos != NULL, return);
         1044 +        term.c.x = pos->x;
         1045 +        term.c.y = pos->y;
         1046 +        term.scr = pos->yScr;
         1047 +}
         1048 +static inline int getSearchDirection(void) {
         1049 +        return stateVB.motion.search == forward ? 1 : -1;
         1050 +}
         1051 +
         1052 +//  Utilities for working with the current version of the scrollback patch.
         1053 +static bool moveLine(int32_t const amount) {
         1054 +        int32_t const reqShift = intervalDiff(term.c.y+=amount, 0, term.row-1);
         1055 +        term.c.y -= reqShift;
         1056 +        int32_t const sDiff = intervalDiff(term.scr-=reqShift, 0, HISTSIZE-1);
         1057 +        term.scr -= sDiff;
         1058 +        return sDiff == 0;
         1059 +}
         1060 +
         1061 +static void moveLetter(int32_t const amount) {
         1062 +        int32_t value = (term.c.x += amount) / term.col;
         1063 +        if (value -= (term.c.x < 0)) {
         1064 +                term.c.x = moveLine(value) ? mod(term.c.x, term.col)
         1065 +                        : max(min(term.c.x,term.col - 1), 0);
         1066 +        }
         1067 +        assert(BETWEEN(term.c.x,0,term.col-1)&&BETWEEN(term.c.y,0,term.row-1));
         1068 +}
         1069 +
         1070 +//
         1071 +// `Private` functions:
         1072 +
         1073 +// Functions: Temporarily display string on screen.
         1074 +
         1075 +/// Display string at end of a specified line without writing it into the buffer
         1076 +/// @param str  string that is to be displayed
         1077 +/// @param g    glyph
         1078 +/// @param yPos
         1079 +static void
         1080 +displayString(DynamicArray const *str, Glyph const *g, int yPos, bool prePos) {
         1081 +        ENSURE((str != NULL) && (g != NULL) && (term.row > 0), return);
         1082 +        ENSURE(yPos >= 0, yPos = 0);
         1083 +        ENSURE(yPos < term.row, yPos = term.row - 1);
         1084 +        // Arbritary limit to avoid withhelding too much info from user.
         1085 +        int const maxFractionOverridden = 3;
         1086 +        // Threshold: if there is no space to print, do not print, but transfer
         1087 +        //            repsonsibility for printing back to [st].
         1088 +        if (term.col < maxFractionOverridden) {                        // (0)
         1089 +                term.dirty[yPos] = 1;
         1090 +                return;
         1091 +        }
         1092 +        int32_t const botSz = prePos * 6; //< sz for position indication
         1093 +        // Determine the dimensions of used chunk of screen.
         1094 +        int32_t const overrideSize = min(size(str) + botSz,
         1095 +                        term.col / maxFractionOverridden);             // (1)
         1096 +        int32_t const overrideEnd = term.col - 2;
         1097 +        // Has to follow trivially hence th assert:
         1098 +        // overrideSize <(1)= term.col/3  <(0)= term.col = overrideEnd + 1.
         1099 +        assert(overrideSize <= overrideEnd + 1);
         1100 +        int32_t const overrideStart = 1 + overrideEnd - overrideSize;
         1101 +        // display history[history.size() - (overrideSize - botSz)::-1]
         1102 +        Glyph *SEC(line, malloc(sizeof(Glyph) * (overrideSize)),,)
         1103 +        int32_t offset = (size(str) - overrideSize - 1 + botSz) * str->itemSize;
         1104 +        for (uint32_t chr = 0; chr < overrideSize - botSz; ++chr) {
         1105 +                line[chr] = *g;
         1106 +                line[chr].u = *((Rune*) (str->content+(offset+=str->itemSize)));
         1107 +        }
         1108 +        if (prePos) {
         1109 +                ENSURE(term.scr < HISTSIZE, term.scr = HISTSIZE - 1);
         1110 +                int32_t const p=round((HISTSIZE-1-term.scr)*100./(HISTSIZE-1));
         1111 +                char prc [8];
         1112 +                switch (term.scr) {
         1113 +                        case HISTSIZE - 1: strcpy(prc, " [TOP]"); break;
         1114 +                        case 0:            strcpy(prc, " [BOT]"); break;
         1115 +                        default:           sprintf(prc, " % 3d%c  ", p, '%');
         1116 +                }
         1117 +                for (uint32_t chr = 0; chr < botSz; ++chr) {
         1118 +                        line[chr + overrideSize - botSz] =*g;
         1119 +                        line[chr + overrideSize - botSz].fg = fgPos;
         1120 +                        line[chr + overrideSize - botSz].bg = bgPos;
         1121 +                        utf8decode(&prc[chr],&line[chr+overrideSize-botSz].u,1);
         1122 +                }
         1123 +                line[overrideSize - botSz] =*g;
         1124 +        }
         1125 +        xdrawline(TLINE(yPos), 0, yPos, overrideStart);
         1126 +        term.c.y -= term.row; term.c.x -= term.col; // not highlight hack
         1127 +        xdrawline(line-overrideStart, overrideStart, yPos, overrideEnd + 1);
         1128 +        term.c.y += term.row; term.c.x += term.col;
         1129 +        free(line);
         1130 +}
         1131 +
         1132 +static inline void printCommandString(void) {
         1133 +        Glyph g = styleCommand;
         1134 +        switch(stateVB.command.op) {
         1135 +                case yank: g.fg = fgCommandYank; g.bg = bgCommandYank; break;
         1136 +                case visual: g.fg=fgCommandVisual; g.bg=bgCommandVisual; break;
         1137 +                case visualLine: g.fg=fgCommandVisualLine;
         1138 +                                 g.bg=bgCommandVisualLine;
         1139 +        }
         1140 +        displayString(isEmpty(currentCommand) ? lastCommand : currentCommand,
         1141 +                        &g, term.row - 1, true);
         1142 +}
         1143 +
         1144 +static inline void printSearchString(void) {
         1145 +        displayString(&searchString, &styleSearch, term.row - 2, false);
         1146 +}
         1147 +
         1148 +// NormalMode Operation / Motion utilies.
         1149 +
         1150 +static inline bool isMotionFinished(void) { return stateVB.motion.finished; }
         1151 +
         1152 +static inline void finishMotion(void) { stateVB.motion.finished = true; }
         1153 +
         1154 +static inline bool isOperationFinished(void) {
         1155 +        return stateVB.command.op==noop && stateVB.command.infix==infix_none;
         1156 +}
         1157 +
         1158 +/// Register that the current comamnd is finished and a new command is lgoged
         1159 +static inline  void startNewCommand(bool abort) {
         1160 +        if (!abort) { toggle = !toggle; }
         1161 +        empty(currentCommand);
         1162 +}
         1163 +
         1164 +static inline void finishOperation(void) {
         1165 +        stateVB.command = defaultNormalMode.command;
         1166 +        assert(isOperationFinished());
         1167 +        // After an operation is finished, the selection has to be released and
         1168 +        // no highlights are to be released.
         1169 +        selclear();
         1170 +        empty(&highlights);
         1171 +        // THe command string is reset for a new command.
         1172 +        startNewCommand(true);
         1173 +}
         1174 +
         1175 +static inline void enableOperation(enum Operation o) {
         1176 +        finishOperation();
         1177 +        stateVB.command.op = o;
         1178 +        stateVB.command.infix = infix_none;
         1179 +        stateVB.command.startPosition.x = term.c.x;
         1180 +        stateVB.command.startPosition.y = term.c.y;
         1181 +        stateVB.command.startPosition.yScr = term.scr;
         1182 +}
         1183 +
         1184 +/// @param abort: If enabled, the command exits without registering
         1185 +/// @return       Whether the the application is ready to yield control back to
         1186 +//the normal command flow
         1187 +static bool terminateCommand(bool abort) {
         1188 +        bool const exitOperation = isMotionFinished();
         1189 +        bool exitNormalMode = false;
         1190 +        finishMotion();
         1191 +
         1192 +        if (exitOperation) {
         1193 +                exitNormalMode = isOperationFinished();
         1194 +                finishOperation();
         1195 +        }
         1196 +        printCommandString();
         1197 +        printSearchString();
         1198 +        return exitNormalMode;
         1199 +}
         1200 +
         1201 +static inline void exitCommand(void) { terminateCommand(false); }
         1202 +
         1203 +static inline void abortCommand(void) { terminateCommand(true); }
         1204 +
         1205 +/// Go to next occurrence of string relative to the current location
         1206 +/// conduct search, starting at start pos
         1207 +static bool gotoString(int8_t sign) {
         1208 +        moveLetter(sign);
         1209 +        uint32_t const searchStrSize = size(&searchString);
         1210 +        uint32_t const maxIter = (HISTSIZE+term.row) * term.col + searchStrSize;
         1211 +        uint32_t findIdx = 0;
         1212 +        for (uint32_t cIteration = 0; findIdx < searchStrSize
         1213 +                        && ++cIteration <= maxIter; moveLetter(sign)) {
         1214 +                char const * const SEC(next, sign==1
         1215 +                                ? view(&searchString, findIdx)
         1216 +                                : viewEnd(&searchString, findIdx), , false)
         1217 +                uint32_t const searchChar = *((uint32_t*) next);
         1218 +
         1219 +                if (TLINE(term.c.y)[term.c.x].u == searchChar) { ++findIdx; }
         1220 +                else { findIdx = 0; }
         1221 +        }
         1222 +        bool const found = findIdx == searchStrSize;
         1223 +        for (uint32_t i = 0; found && i < searchStrSize; ++i) moveLetter(-sign);
         1224 +        return found;
         1225 +}
         1226 +
         1227 +/// Highlight all found strings on the current screen.
         1228 +static void highlightStringOnScreen(void) {
         1229 +        if (isEmpty(&searchString)) { return; }
         1230 +        empty(&highlights);
         1231 +        uint32_t const searchStringSize = size(&searchString);
         1232 +        uint32_t findIdx = 0;
         1233 +        uint32_t xStart, yStart;
         1234 +        bool success = true;
         1235 +        for (int y = 0; y < term.row && success; y++) {
         1236 +                for (int x = 0; x < term.col && success; x++) {
         1237 +                        char const* const SEC(next,
         1238 +                                        view(&searchString,findIdx),,)
         1239 +                        if (TLINE(y)[x].u == (Rune) *((uint32_t*)(next))) {
         1240 +                                if (++findIdx == 1) {
         1241 +                                        xStart = x;
         1242 +                                        yStart = y;
         1243 +                                }
         1244 +                                if (findIdx == searchStringSize) {
         1245 +                                        success = success
         1246 +                                                && append(&highlights, &xStart)
         1247 +                                                && append(&highlights, &yStart);
         1248 +                                        findIdx = 0; //term.dirty[yStart] = 1;
         1249 +                                }
         1250 +                        } else { findIdx = 0; }
         1251 +                }
         1252 +        }
         1253 +        if (!success) { empty(&highlights); }
         1254 +}
         1255 +
         1256 +static bool gotoStringAndHighlight(int8_t sign) {
         1257 +              // Find hte next occurrence of the #searchString in direction #sign
         1258 +        bool const found = gotoString(sign);
         1259 +        if (!found) {  applyPosition(&stateVB.motion.searchPosition); }
         1260 +        highlightStringOnScreen();
         1261 +        //tsetdirt(0, term.row-3); //< everything except for the 'status bar'
         1262 +        return found;
         1263 +}
         1264 +
         1265 +static bool pressKeys(char const* nullTerminatedString, size_t end) {
         1266 +        bool sc = true;
         1267 +        for (size_t i = 0; i < end && sc; ++i) {
         1268 +                sc = kpressNormalMode(&nullTerminatedString[i], 1, false, NULL);
         1269 +        }
         1270 +        return sc;
         1271 +}
         1272 +
         1273 +static bool executeCommand(DynamicArray const *command) {
         1274 +        size_t end=size(command);
         1275 +        char decoded [32];
         1276 +        bool succ = true;
         1277 +        size_t len;
         1278 +        for (size_t i = 0; i < end && succ; ++i) {
         1279 +                char const *const SEC(nextRune, view(command, i),,false)
         1280 +                len = utf8encode(*((Rune *) nextRune), decoded);
         1281 +                succ = kpressNormalMode(decoded, len, false, NULL);
         1282 +        }
         1283 +        return succ;
         1284 +}
         1285 +
         1286 +struct { char const first; char const second; } const Brackets [] =
         1287 +{ {'(', ')'}, {'<', '>'}, {'{', '}'}, {'[', ']'}, };
         1288 +
         1289 +
         1290 +/// Emits Command prefix and suffix when i motion is performed (e.g. yiw).
         1291 +///
         1292 +/// @param c:             motion character
         1293 +/// @param expandMode:    1 for 'i', 2 for 'a'
         1294 +/// @param first, second: Dynamic arrays in which the prefix and postfix
         1295 +///                       commands will be returned
         1296 +/// @return               whether the command could be extracted successfully.
         1297 +static bool expandExpression(char const c, enum Infix expandMode,
         1298 +                char operation, DynamicArray *cmd) {
         1299 +        empty(cmd);
         1300 +        bool s = true; //< used in order to detect memory allocation errors.
         1301 +        char const lower = tolower(c);
         1302 +        // Motions
         1303 +        if (lower == 'w') {
         1304 +                // translated into wb[command]e resp. WB[command]E, which works
         1305 +                // file even when at the fist letter. Does not work for single
         1306 +                // letter words though.
         1307 +                int const diff = c - lower;
         1308 +                s = s && checkSetNextV(cmd, c);
         1309 +                s = s && checkSetNextV(cmd, (signed char)(((int)'b') + diff));
         1310 +                s = s && checkSetNextV(cmd, operation);
         1311 +                s = s && checkSetNextV(cmd, (signed char)(((int)'e')+ diff));
         1312 +                return s;
         1313 +        }
         1314 +        // Symmetrical brackets (quotation marks)
         1315 +        if (c == '\'' || c == '"') {
         1316 +                // Local ambiguity -> do nothing. It cannot be determined if
         1317 +                // the current char is the 1st or last char of the selection.
         1318 +                //  <---- search here? -- ['] -- or search here? --->
         1319 +                if (TLINE(term.c.y)[term.c.x].u == c) {
         1320 +                        return false;
         1321 +                }
         1322 +                // Prefix
         1323 +                char res [] = {'?', c, '\n'};
         1324 +                s = s && checkSetNextP(cmd, res);
         1325 +                // infix
         1326 +                bool const iffy = expandMode == infix_i;
         1327 +                if (iffy) { s = s && checkSetNextV(cmd, 'l'); }
         1328 +                s = s && checkSetNextV(cmd, operation);
         1329 +                if (!iffy) { s = s && checkSetNextV(cmd, 'l'); }
         1330 +                // suffix
         1331 +                res[0] = '/';
         1332 +                s = s && checkSetNextP(cmd, res);
         1333 +                if (iffy) { s = s && checkSetNextV(cmd, 'h'); }
         1334 +                return s;
         1335 +        }
         1336 +        // Brackets: Does not if in range / if the brackets belong togehter.
         1337 +        for (size_t pid = 0; pid < sizeof(Brackets); ++pid) {
         1338 +                if(Brackets[pid].first == c || Brackets[pid].second == c) {
         1339 +                        if (TLINE(term.c.y)[term.c.x].u!=Brackets[pid].first) {
         1340 +                                s = s && checkSetNextV(cmd, '?');
         1341 +                                s = s && checkSetNextV(cmd, Brackets[pid].first);
         1342 +                                s = s && checkSetNextV(cmd, '\n');
         1343 +                        }
         1344 +                        bool const iffy = expandMode == infix_i;
         1345 +                        if (iffy) { s = s && checkSetNextV(cmd, 'l'); }
         1346 +                        s = s && checkSetNextV(cmd, operation);
         1347 +                        if (!iffy) { s = s && checkSetNextV(cmd, 'l'); }
         1348 +                        s = s && checkSetNextV(cmd, '/');
         1349 +                        s = s && checkSetNextV(cmd, Brackets[pid].second);
         1350 +                        s = s && checkSetNextV(cmd, '\n');
         1351 +                        if (iffy) { s = s && checkSetNextV(cmd, 'h'); }
         1352 +                        return s;
         1353 +                }
         1354 +        }
         1355 +        /**/
         1356 +        // search string
         1357 +        // complicated search operation: <tag>
         1358 +        if (c == 't') {
         1359 +                // XXX: (Bug in vim: @vit )
         1360 +                // <tag_name attr="hier" a2="\<sch\>"> [current pos] </tag_name>
         1361 +
         1362 +                // 1. Copy history ( tag := hist[?<\n:/ \n] )
         1363 +                // 2. Copy history ( first_find := hist[?<\n: next place in
         1364 +                //                   history where count '>' > count '<'
         1365 +                //                   (can be behind current pos) )
         1366 +                // 3. first := [?first_find][#first_ind]l
         1367 +                //    second:= [/tag">"]h
         1368 +                //return true; // XXX: not implmented yet.
         1369 +        }
         1370 +        return false;
         1371 +}
         1372 +
         1373 +//
         1374 +// Public API
         1375 +//
         1376 +
         1377 +void onMove(void) {
         1378 +        stateVB.initialPosition.x = term.c.x;
         1379 +        stateVB.initialPosition.y = term.c.y;
         1380 +        stateVB.initialPosition.yScr = term.scr;
         1381 +}
         1382 +
         1383 +int highlighted(int x, int y) {
         1384 +        // Compute the legal bounds for a hit:
         1385 +        int32_t const stringSize = size(&searchString);
         1386 +        int32_t xMin = x - stringSize;
         1387 +        int32_t yMin = y;
         1388 +        while (xMin < 0 && yMin > 0) {
         1389 +                xMin += term.col;
         1390 +                --yMin;
         1391 +        }
         1392 +        if (xMin < 0) { xMin = 0; }
         1393 +
         1394 +        uint32_t highSize = size(&highlights);
         1395 +        ENSURE(highSize % 2 == 0, empty(&highlights); return false;);
         1396 +        highSize /= 2;
         1397 +        uint32_t *ptr = (uint32_t*) highlights.content;
         1398 +        for (uint32_t i = 0; i < highSize; ++i) {
         1399 +                int32_t const sx = (int32_t) *(ptr++);
         1400 +                int32_t const sy = (int32_t) *(ptr++);
         1401 +                if (BETWEEN(sy, yMin, y) && (sy != yMin || sx > xMin)
         1402 +                                && (sy != y || sx <= x)) {
         1403 +                        return true;
         1404 +                }
         1405 +        }
         1406 +        return false;
         1407 +}
         1408 +
         1409 +ExitState kpressNormalMode(char const * cs, int len, bool ctrl, void const *v) {
         1410 +        KeySym const * const ksym = (KeySym*) v;
         1411 +        bool const esc = ksym &&  *ksym == XK_Escape;
         1412 +        bool const enter = (ksym && *ksym==XK_Return) || (len==1 &&cs[0]=='\n');
         1413 +        bool const quantifier = len == 1 && (BETWEEN(cs[0], 49, 57)
         1414 +                        || (cs[0] == 48 && stateVB.motion.amount));
         1415 +        int const previousScroll = term.scr;
         1416 +        // [ESC] or [ENTER] abort resp. finish the current level of operation.
         1417 +        // Typing 'i' if no operation is currently performed behaves like ESC.
         1418 +        if (esc || enter || (len == 1 && cs[0] == 'i' && isMotionFinished()
         1419 +                                && isOperationFinished())) {
         1420 +                if (terminateCommand(!enter)) {
         1421 +                        applyPosition(&stateVB.initialPosition);
         1422 +                        Position const pc = stateVB.initialPosition;
         1423 +                        stateVB = defaultNormalMode;
         1424 +                        stateVB.initialPosition = pc;
         1425 +                        tfulldirt();
         1426 +                        return finished;
         1427 +                }
         1428 +                len = 0;
         1429 +                goto motionFinish;
         1430 +        }
         1431 +        // Backspace
         1432 +        if (ksym && *ksym == XK_BackSpace) {
         1433 +                bool s = stateVB.motion.search!=none&&!stateVB.motion.finished;
         1434 +                bool q = stateVB.motion.amount != 0;
         1435 +                if (!(s || q)) { return failed; }
         1436 +                len = 0;
         1437 +
         1438 +                if (!isEmpty(currentCommand)) { pop(currentCommand); }
         1439 +                if (s) {
         1440 +                        if (!isEmpty(&searchString)) { pop(&searchString); }
         1441 +                        else if (isEmpty(&searchString)) {
         1442 +                                exitCommand();
         1443 +                                return success;
         1444 +                        }
         1445 +                } else if (q) {
         1446 +                        stateVB.motion.amount /= 10;
         1447 +                        goto finishNoAppend;
         1448 +                }
         1449 +        }
         1450 +
         1451 +        // Search: append to search string, then search & highlight
         1452 +        if (stateVB.motion.search != none && !stateVB.motion.finished) {
         1453 +                if (len >= 1) {
         1454 +                        EXPAND(kSearch, &searchString, true)
         1455 +                        utf8decode(cs, (Rune*)(kSearch), len);
         1456 +                }
         1457 +                applyPosition(&stateVB.motion.searchPosition);
         1458 +                gotoStringAndHighlight(getSearchDirection());
         1459 +                goto finish;
         1460 +        }
         1461 +        if (len == 0) { return failed; }
         1462 +        // Quantifiers
         1463 +        if (quantifier) {
         1464 +                stateVB.motion.amount = min(SHRT_MAX,
         1465 +                                stateVB.motion.amount * 10 + cs[0] - 48);
         1466 +                goto finish;
         1467 +        }
         1468 +        // 'i' mode enabled, hence the expression is to be expanded:
         1469 +        // [start_expression(cs[0])] [operation] [stop_expression(cs[0])]
         1470 +        if (stateVB.command.infix != infix_none && stateVB.command.op != noop) {
         1471 +                DynamicArray cmd = CHAR_ARRAY;
         1472 +                char const operation = stateVB.command.op;
         1473 +                bool succ = expandExpression(cs[0],
         1474 +                                stateVB.command.infix, visual, &cmd);
         1475 +                if (operation == yank) {
         1476 +                        succ = succ && checkSetNextV(&cmd, operation);
         1477 +                }
         1478 +                NormalModeState const st = stateVB;
         1479 +                TCursor         const tc = term.c;
         1480 +                stateVB.command.infix    = infix_none;
         1481 +                if (succ) {
         1482 +                        stateVB.command.op = noop;
         1483 +                        for (int i = 0; i < size(&cmd) && succ; ++i) {
         1484 +                                succ = pressKeys(&cmd.content[i], 1);
         1485 +                        }
         1486 +                        if (!succ) { // go back to the old position, apply op
         1487 +                                stateVB = st;
         1488 +                                term.c = tc;
         1489 +                        }
         1490 +                        empty(currentCommand);
         1491 +                        for (uint32_t i = 0; i < size(&cmd); ++i) {
         1492 +                                EXPAND(kCommand, currentCommand, true)
         1493 +                                utf8decode(cmd.content+i, (Rune*)(kCommand),1);
         1494 +                        }
         1495 +                }
         1496 +                free(cmd.content);
         1497 +                goto finishNoAppend;
         1498 +        }
         1499 +        // Commands (V / v or y)
         1500 +        switch(cs[0]) {
         1501 +                case '.':
         1502 +                {
         1503 +                        if (isEmpty(currentCommand)) { toggle = !toggle; }
         1504 +                        DynamicArray cmd = UTF8_ARRAY;
         1505 +                        swap(&cmd, currentCommand);
         1506 +                        executeCommand(&cmd) ? success : failed;
         1507 +                        swap(&cmd, currentCommand);
         1508 +                        free(cmd.content);
         1509 +                        goto finishNoAppend;
         1510 +                }
         1511 +                case 'i': stateVB.command.infix = infix_i; goto finish;
         1512 +                case 'a': stateVB.command.infix = infix_a; goto finish;
         1513 +                case 'y':
         1514 +                        switch(stateVB.command.op) {
         1515 +                                case noop: //< Start yank mode & set #op
         1516 +                                        enableOperation(yank);
         1517 +                                        selstart(term.c.x, term.c.y,term.scr,0);
         1518 +                                        goto finish;
         1519 +                                case yank: //< Complete yank [y#amount j]
         1520 +                                        selstart(0, term.c.y, term.scr, 0);
         1521 +                                        int const origY = term.c.y;
         1522 +                                        moveLine(max(stateVB.motion.amount, 1));
         1523 +                                        selextend(term.col-1,term.c.y,term.scr,
         1524 +                                                        SEL_RECTANGULAR, 0);
         1525 +                                        term.c.y = origY;
         1526 +                                        FALLTHROUGH
         1527 +                                case visualLine: // Yank visual selection
         1528 +                                case visual:
         1529 +                                        xsetsel(getsel());
         1530 +                                        xclipcopy();
         1531 +                                        exitCommand();
         1532 +                                        goto finish;
         1533 +                                default:
         1534 +                                        return failed;
         1535 +                        }
         1536 +                case visual:
         1537 +                case visualLine:
         1538 +                        if (stateVB.command.op == cs[0]) {
         1539 +                                finishOperation();
         1540 +                                return true;
         1541 +                        } else {
         1542 +                                enableOperation(cs[0]);
         1543 +                                selstart(cs[0] == visualLine ? 0 : term.c.x,
         1544 +                                                term.c.y, term.scr, 0);
         1545 +                                goto finish;
         1546 +                        }
         1547 +        }
         1548 +        // CTRL Motions
         1549 +        int32_t sign = -1;    //< if command goes 'forward'(1) or 'backward'(-1)
         1550 +        if (ctrl) {
         1551 +                if (ksym == NULL) { return false; }
         1552 +                switch(*ksym) {
         1553 +                        case XK_f:
         1554 +                                term.scr = max(term.scr - max(term.row-2,1), 0);
         1555 +                                term.c.y = 0;
         1556 +                                goto finish;
         1557 +                        case XK_b:
         1558 +                                term.scr = min(term.scr + max(term.row - 2, 1),
         1559 +                                                HISTSIZE - 1);
         1560 +                                term.c.y = term.bot;
         1561 +                                goto finish;
         1562 +                        case XK_u:
         1563 +                                term.scr = min(term.scr+term.row/2, HISTSIZE-1);
         1564 +                                goto finish;
         1565 +                        case XK_d:
         1566 +                                term.scr = max(term.scr - term.row / 2, 0);
         1567 +                                goto finish;
         1568 +                        default: return false;
         1569 +                }
         1570 +        }
         1571 +        // Motions
         1572 +        switch(cs[0]) {
         1573 +                case 'c': empty(&commandHist0); empty(&commandHist1);
         1574 +                          goto finishNoAppend;
         1575 +                case 'j': sign = 1; FALLTHROUGH
         1576 +                case 'k': moveLine(max(stateVB.motion.amount,1) * sign);
         1577 +                          goto motionFinish;
         1578 +                case 'H': term.c.y = 0;
         1579 +                          goto motionFinish;
         1580 +                case 'M': term.c.y = term.bot / 2;
         1581 +                          goto motionFinish;
         1582 +                case 'L': term.c.y = term.bot;
         1583 +                          goto motionFinish;
         1584 +                case 'G': applyPosition(&stateVB.initialPosition);
         1585 +                          goto motionFinish;
         1586 +                case 'l': sign = 1; FALLTHROUGH
         1587 +                case 'h': moveLetter(sign * max(stateVB.motion.amount,1));
         1588 +                          goto motionFinish;
         1589 +                case '0': term.c.x = 0;
         1590 +                          goto motionFinish;
         1591 +                case '$': term.c.x = term.col-1;
         1592 +                          goto motionFinish;
         1593 +                case 'w': FALLTHROUGH
         1594 +                case 'W': FALLTHROUGH
         1595 +                case 'e': FALLTHROUGH
         1596 +                case 'E': sign = 1; FALLTHROUGH
         1597 +                case 'B': FALLTHROUGH
         1598 +                case 'b': {
         1599 +                        char const * const wDelim =
         1600 +                                cs[0] <= 90 ? wordDelimLarge : wordDelimSmall;
         1601 +                        uint32_t const wDelimLen = strlen(wDelim);
         1602 +
         1603 +                        bool const startSpaceIsSeparator =
         1604 +                                !(cs[0] == 'w' || cs[0] == 'W');
         1605 +                        // Whether to start & end with offset:
         1606 +                        bool const performOffset = startSpaceIsSeparator;
         1607 +                        // Max iteration := One complete hist traversal.
         1608 +                        uint32_t const maxIter = (HISTSIZE+term.row) * term.col;
         1609 +                        // Doesn't work exactly as in vim: Linebreak is
         1610 +                        // counted as 'normal' separator, hence a jump can
         1611 +                        // span multiple lines here.
         1612 +                        stateVB.motion.amount = max(stateVB.motion.amount, 1);
         1613 +                        for (;stateVB.motion.amount>0;--stateVB.motion.amount) {
         1614 +                                uint8_t state = 0;
         1615 +                                if (performOffset) { moveLetter(sign); }
         1616 +                                for (uint32_t cIt = 0; cIt ++ < maxIter; moveLetter(sign)) {
         1617 +                                        if (startSpaceIsSeparator == contains(TLINE(term.c.y)[term.c.x].u, wDelim, wDelimLen)) {
         1618 +                                                if (state == 1) {
         1619 +                                                        if (performOffset) {
         1620 +                                                                moveLetter(-sign);
         1621 +                                                        }
         1622 +                                                        break;
         1623 +                                                }
         1624 +                                        } else if (state == 0) { state = 1; }
         1625 +                                }
         1626 +                        }
         1627 +                        goto motionFinish;
         1628 +                }
         1629 +                case '/': sign = 1; FALLTHROUGH
         1630 +                case '?':
         1631 +                          empty(&searchString);
         1632 +                          stateVB.motion.search = sign == 1 ? forward : backward;
         1633 +                          stateVB.motion.searchPosition.x = term.c.x;
         1634 +                          stateVB.motion.searchPosition.y = term.c.y;
         1635 +                          stateVB.motion.searchPosition.yScr = term.scr;
         1636 +                          stateVB.motion.finished = false;
         1637 +                          goto finish;
         1638 +                case 'n': sign = 1; FALLTHROUGH
         1639 +                case 'N': {
         1640 +                        if (stateVB.motion.search == none) return failed;
         1641 +                        if (stateVB.motion.search == backward) { sign *= -1; }
         1642 +                        bool b = true; int ox = term.c.x;
         1643 +                        int oy = term.c.y ; int scr = term.scr;
         1644 +                        int32_t i = max(stateVB.motion.amount, 1);
         1645 +                        for (;i>0 && (b=gotoString(sign)); --i) {
         1646 +                          oy = term.c.y; scr = term.scr;
         1647 +                        }
         1648 +                        if (!b) { term.c.x = ox; term.c.y = oy; term.scr = scr;}
         1649 +                        goto motionFinish;
         1650 +                }
         1651 +                case 't': // Toggle selection mode and set dirt.
         1652 +                          sel.type = sel.type == SEL_REGULAR
         1653 +                                  ? SEL_RECTANGULAR : SEL_REGULAR;
         1654 +                          //tsetdirt(sel.nb.y, sel.ne.y);
         1655 +                          goto motionFinish;
         1656 +        }
         1657 +        // Custom commands
         1658 +        for (size_t i = 0; i < amountNormalModeShortcuts; ++i) {
         1659 +                if (cs[0] == normalModeShortcuts[i].key) {
         1660 +                        return pressKeys(normalModeShortcuts[i].value,
         1661 +                                        strlen(normalModeShortcuts[i].value))
         1662 +                                        ? success : failed;
         1663 +                }
         1664 +        }
         1665 +        return failed;
         1666 +motionFinish:
         1667 +        stateVB.motion.amount = 0;
         1668 +        //if (isMotionFinished() && stateVB.command.op == yank) {
         1669 +        if (stateVB.command.op == yank) {
         1670 +                selextend(term.c.x, term.c.y, term.scr, sel.type, 0);
         1671 +                xsetsel(getsel());
         1672 +                xclipcopy();
         1673 +                exitCommand();
         1674 +        }
         1675 +finish:
         1676 +        if (len == 1 && !ctrl) { // XXX: for now.
         1677 +                EXPAND(kCommand, currentCommand, true)
         1678 +                utf8decode(cs, (Rune*)(kCommand), len);
         1679 +        }
         1680 +finishNoAppend:
         1681 +        if (stateVB.command.op == visual) {
         1682 +                selextend(term.c.x, term.c.y, term.scr, sel.type, 0);
         1683 +        } else if (stateVB.command.op == visualLine) {
         1684 +                selextend(term.col-1, term.c.y, term.scr, sel.type, 0);
         1685 +        }
         1686 +
         1687 +        if (previousScroll != term.scr && !isEmpty(&searchString)) {
         1688 +                highlightStringOnScreen();
         1689 +        }
         1690 +        tsetdirt(0, term.row-3); //< Required because of the cursor cross.
         1691 +        printCommandString();
         1692 +        printSearchString();
         1693 +        return success;
         1694 +}
         1695 diff --git a/normalMode.h b/normalMode.h
         1696 new file mode 100644
         1697 index 0000000..7d88259
         1698 --- /dev/null
         1699 +++ b/normalMode.h
         1700 @@ -0,0 +1,36 @@
         1701 +/* See LICENSE for license details. */
         1702 +#ifndef NORMAL_MODE_H
         1703 +#define NORMAL_MODE_H
         1704 +
         1705 +#include <stdbool.h>
         1706 +#include <stddef.h>
         1707 +#include <stdint.h>
         1708 +
         1709 +/// Used in the configuration file to define custom shortcuts.
         1710 +typedef struct NormalModeShortcuts {
         1711 +        char key;
         1712 +        char *value;
         1713 +} NormalModeShortcuts;
         1714 +
         1715 +/// Holds the exit status of the #kpressNormalMode function, which informs the
         1716 +/// caller when to exit normal mode.
         1717 +typedef enum ExitState {
         1718 +        failed = 0,
         1719 +        success = 1,
         1720 +        finished = 2,
         1721 +} ExitState;
         1722 +
         1723 +/// Called when curr position is altered.
         1724 +void onMove(void);
         1725 +
         1726 +/// Function which returns whether the value at position provided as arguments
         1727 +/// is to be highlighted.
         1728 +int highlighted(int, int);
         1729 +
         1730 +/// Handles keys in normal mode.
         1731 +ExitState kpressNormalMode(char const * decoded, int len, bool ctrlPressed,
         1732 +                void const * ksym);
         1733 +                //bool esc, bool enter, bool backspace, void* keysym);
         1734 +
         1735 +
         1736 +#endif // NORMAL_MODE_H
         1737 diff --git a/st.c b/st.c
         1738 index 3e48410..d8bf7ab 100644
         1739 --- a/st.c
         1740 +++ b/st.c
         1741 @@ -1,8 +1,10 @@
         1742  /* See LICENSE for license details. */
         1743 +#include <assert.h>
         1744  #include <ctype.h>
         1745  #include <errno.h>
         1746  #include <fcntl.h>
         1747  #include <limits.h>
         1748 +#include <math.h>
         1749  #include <pwd.h>
         1750  #include <stdarg.h>
         1751  #include <stdio.h>
         1752 @@ -17,6 +19,8 @@
         1753  #include <unistd.h>
         1754  #include <wchar.h>
         1755  
         1756 +
         1757 +#include "term.h"
         1758  #include "st.h"
         1759  #include "win.h"
         1760  
         1761 @@ -42,6 +46,7 @@
         1762  #define ISCONTROLC1(c)                (BETWEEN(c, 0x80, 0x9f))
         1763  #define ISCONTROL(c)                (ISCONTROLC0(c) || ISCONTROLC1(c))
         1764  #define ISDELIM(u)                (u && wcschr(worddelimiters, u))
         1765 +#define INTERVAL(x, a, b)        (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
         1766  
         1767  enum term_mode {
         1768          MODE_WRAP        = 1 << 0,
         1769 @@ -86,51 +91,6 @@ enum escape_state {
         1770          ESC_DCS        =128,
         1771  };
         1772  
         1773 -typedef struct {
         1774 -        Glyph attr; /* current char attributes */
         1775 -        int x;
         1776 -        int y;
         1777 -        char state;
         1778 -} TCursor;
         1779 -
         1780 -typedef struct {
         1781 -        int mode;
         1782 -        int type;
         1783 -        int snap;
         1784 -        /*
         1785 -         * Selection variables:
         1786 -         * nb – normalized coordinates of the beginning of the selection
         1787 -         * ne – normalized coordinates of the end of the selection
         1788 -         * ob – original coordinates of the beginning of the selection
         1789 -         * oe – original coordinates of the end of the selection
         1790 -         */
         1791 -        struct {
         1792 -                int x, y;
         1793 -        } nb, ne, ob, oe;
         1794 -
         1795 -        int alt;
         1796 -} Selection;
         1797 -
         1798 -/* Internal representation of the screen */
         1799 -typedef struct {
         1800 -        int row;      /* nb row */
         1801 -        int col;      /* nb col */
         1802 -        Line *line;   /* screen */
         1803 -        Line *alt;    /* alternate screen */
         1804 -        int *dirty;   /* dirtyness of lines */
         1805 -        TCursor c;    /* cursor */
         1806 -        int ocx;      /* old cursor col */
         1807 -        int ocy;      /* old cursor row */
         1808 -        int top;      /* top    scroll limit */
         1809 -        int bot;      /* bottom scroll limit */
         1810 -        int mode;     /* terminal mode flags */
         1811 -        int esc;      /* escape state flags */
         1812 -        char trantbl[4]; /* charset table translation */
         1813 -        int charset;  /* current charset */
         1814 -        int icharset; /* selected charset for sequence */
         1815 -        int *tabs;
         1816 -} Term;
         1817 -
         1818  /* CSI Escape sequence structs */
         1819  /* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */
         1820  typedef struct {
         1821 @@ -153,6 +113,8 @@ typedef struct {
         1822          int narg;              /* nb of args */
         1823  } STREscape;
         1824  
         1825 +void tfulldirt(void);
         1826 +
         1827  static void execsh(char *, char **);
         1828  static void stty(char **);
         1829  static void sigchld(int);
         1830 @@ -185,16 +147,14 @@ static void tnewline(int);
         1831  static void tputtab(int);
         1832  static void tputc(Rune);
         1833  static void treset(void);
         1834 -static void tscrollup(int, int);
         1835 -static void tscrolldown(int, int);
         1836 +static void tscrollup(int, int, int);
         1837 +static void tscrolldown(int, int, int);
         1838  static void tsetattr(int *, int);
         1839  static void tsetchar(Rune, Glyph *, int, int);
         1840 -static void tsetdirt(int, int);
         1841  static void tsetscroll(int, int);
         1842  static void tswapscreen(void);
         1843  static void tsetmode(int, int, int *, int);
         1844  static int twrite(const char *, int, int);
         1845 -static void tfulldirt(void);
         1846  static void tcontrolcode(uchar );
         1847  static void tdectest(char );
         1848  static void tdefutf8(char);
         1849 @@ -208,8 +168,6 @@ static void selnormalize(void);
         1850  static void selscroll(int, int);
         1851  static void selsnap(int *, int *, int);
         1852  
         1853 -static size_t utf8decode(const char *, Rune *, size_t);
         1854 -static Rune utf8decodebyte(char, size_t *);
         1855  static char utf8encodebyte(Rune, size_t);
         1856  static size_t utf8validate(Rune *, size_t);
         1857  
         1858 @@ -219,8 +177,8 @@ static char base64dec_getc(const char **);
         1859  static ssize_t xwrite(int, const char *, size_t);
         1860  
         1861  /* Globals */
         1862 -static Term term;
         1863 -static Selection sel;
         1864 +Term term;
         1865 +Selection sel;
         1866  static CSIEscape csiescseq;
         1867  static STREscape strescseq;
         1868  static int iofd = 1;
         1869 @@ -414,17 +372,22 @@ tlinelen(int y)
         1870  {
         1871          int i = term.col;
         1872  
         1873 -        if (term.line[y][i - 1].mode & ATTR_WRAP)
         1874 +        if (TLINE(y)[i - 1].mode & ATTR_WRAP)
         1875                  return i;
         1876  
         1877 -        while (i > 0 && term.line[y][i - 1].u == ' ')
         1878 +        while (i > 0 && TLINE(y)[i - 1].u == ' ')
         1879                  --i;
         1880  
         1881          return i;
         1882  }
         1883  
         1884  void
         1885 -selstart(int col, int row, int snap)
         1886 +xselstart(int col, int row, int snap) {
         1887 +        selstart(col, row, term.scr, snap);
         1888 +}
         1889 +
         1890 +void
         1891 +selstart(int col, int row, int scroll, int snap)
         1892  {
         1893          selclear();
         1894          sel.mode = SEL_EMPTY;
         1895 @@ -433,6 +396,7 @@ selstart(int col, int row, int snap)
         1896          sel.snap = snap;
         1897          sel.oe.x = sel.ob.x = col;
         1898          sel.oe.y = sel.ob.y = row;
         1899 +        sel.oe.scroll = sel.ob.scroll = scroll;
         1900          selnormalize();
         1901  
         1902          if (sel.snap != 0)
         1903 @@ -441,10 +405,13 @@ selstart(int col, int row, int snap)
         1904  }
         1905  
         1906  void
         1907 -selextend(int col, int row, int type, int done)
         1908 -{
         1909 -        int oldey, oldex, oldsby, oldsey, oldtype;
         1910 +xselextend(int col, int row, int type, int done) {
         1911 +        selextend(col, row, term.scr, type, done);
         1912 +}
         1913  
         1914 +void
         1915 +selextend(int col, int row, int scroll, int type, int done)
         1916 +{
         1917          if (sel.mode == SEL_IDLE)
         1918                  return;
         1919          if (done && sel.mode == SEL_EMPTY) {
         1920 @@ -452,18 +419,22 @@ selextend(int col, int row, int type, int done)
         1921                  return;
         1922          }
         1923  
         1924 -        oldey = sel.oe.y;
         1925 -        oldex = sel.oe.x;
         1926 -        oldsby = sel.nb.y;
         1927 -        oldsey = sel.ne.y;
         1928 -        oldtype = sel.type;
         1929 +        int const oldey = sel.oe.y;
         1930 +        int const oldex = sel.oe.x;
         1931 +        int const oldscroll = sel.oe.scroll;
         1932 +        int const oldsby = sel.nb.y;
         1933 +        int const oldsey = sel.ne.y;
         1934 +        int const oldtype = sel.type;
         1935  
         1936          sel.oe.x = col;
         1937          sel.oe.y = row;
         1938 +        sel.oe.scroll = scroll;
         1939 +
         1940          selnormalize();
         1941          sel.type = type;
         1942  
         1943 -        if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY)
         1944 +        if (oldey != sel.oe.y || oldex != sel.oe.x || oldscroll != sel.oe.scroll
         1945 +                        || oldtype != sel.type || sel.mode == SEL_EMPTY)
         1946                  tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
         1947  
         1948          sel.mode = done ? SEL_IDLE : SEL_READY;
         1949 @@ -472,17 +443,21 @@ selextend(int col, int row, int type, int done)
         1950  void
         1951  selnormalize(void)
         1952  {
         1953 -        int i;
         1954 -
         1955 -        if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {
         1956 -                sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
         1957 -                sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
         1958 +        sel.nb.y = INTERVAL(sel.ob.y + term.scr - sel.ob.scroll, 0, term.bot);
         1959 +        sel.ne.y = INTERVAL(sel.oe.y + term.scr - sel.oe.scroll, 0, term.bot);
         1960 +        if (sel.type == SEL_REGULAR && sel.nb.y != sel.ne.y) {
         1961 +                sel.nb.x = sel.nb.y < sel.ne.y ? sel.ob.x : sel.oe.x;
         1962 +                sel.ne.x = sel.nb.y < sel.ne.y ? sel.oe.x : sel.ob.x;
         1963          } else {
         1964                  sel.nb.x = MIN(sel.ob.x, sel.oe.x);
         1965                  sel.ne.x = MAX(sel.ob.x, sel.oe.x);
         1966          }
         1967 -        sel.nb.y = MIN(sel.ob.y, sel.oe.y);
         1968 -        sel.ne.y = MAX(sel.ob.y, sel.oe.y);
         1969 +
         1970 +        if (sel.nb.y > sel.ne.y) {
         1971 +                int32_t const tmp = sel.nb.y;
         1972 +                sel.nb.y = sel.ne.y;
         1973 +                sel.ne.y = tmp;
         1974 +        }
         1975  
         1976          selsnap(&sel.nb.x, &sel.nb.y, -1);
         1977          selsnap(&sel.ne.x, &sel.ne.y, +1);
         1978 @@ -490,7 +465,7 @@ selnormalize(void)
         1979          /* expand selection over line breaks */
         1980          if (sel.type == SEL_RECTANGULAR)
         1981                  return;
         1982 -        i = tlinelen(sel.nb.y);
         1983 +        int i = tlinelen(sel.nb.y);
         1984          if (i < sel.nb.x)
         1985                  sel.nb.x = i;
         1986          if (tlinelen(sel.ne.y) <= sel.ne.x)
         1987 @@ -526,7 +501,7 @@ selsnap(int *x, int *y, int direction)
         1988                   * Snap around if the word wraps around at the end or
         1989                   * beginning of a line.
         1990                   */
         1991 -                prevgp = &term.line[*y][*x];
         1992 +                prevgp = &TLINE(*y)[*x];
         1993                  prevdelim = ISDELIM(prevgp->u);
         1994                  for (;;) {
         1995                          newx = *x + direction;
         1996 @@ -541,14 +516,14 @@ selsnap(int *x, int *y, int direction)
         1997                                          yt = *y, xt = *x;
         1998                                  else
         1999                                          yt = newy, xt = newx;
         2000 -                                if (!(term.line[yt][xt].mode & ATTR_WRAP))
         2001 +                                if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
         2002                                          break;
         2003                          }
         2004  
         2005                          if (newx >= tlinelen(newy))
         2006                                  break;
         2007  
         2008 -                        gp = &term.line[newy][newx];
         2009 +                        gp = &TLINE(newy)[newx];
         2010                          delim = ISDELIM(gp->u);
         2011                          if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
         2012                                          || (delim && gp->u != prevgp->u)))
         2013 @@ -569,14 +544,14 @@ selsnap(int *x, int *y, int direction)
         2014                  *x = (direction < 0) ? 0 : term.col - 1;
         2015                  if (direction < 0) {
         2016                          for (; *y > 0; *y += direction) {
         2017 -                                if (!(term.line[*y-1][term.col-1].mode
         2018 +                                if (!(TLINE(*y-1)[term.col-1].mode
         2019                                                  & ATTR_WRAP)) {
         2020                                          break;
         2021                                  }
         2022                          }
         2023                  } else if (direction > 0) {
         2024                          for (; *y < term.row-1; *y += direction) {
         2025 -                                if (!(term.line[*y][term.col-1].mode
         2026 +                                if (!(TLINE(*y)[term.col-1].mode
         2027                                                  & ATTR_WRAP)) {
         2028                                          break;
         2029                                  }
         2030 @@ -596,24 +571,32 @@ getsel(void)
         2031          if (sel.ob.x == -1)
         2032                  return NULL;
         2033  
         2034 -        bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ;
         2035 +        int32_t syb = sel.ob.y - sel.ob.scroll + term.scr;
         2036 +        int32_t sye = sel.oe.y - sel.oe.scroll + term.scr;
         2037 +        if (syb > sye) {
         2038 +                int32_t tmp = sye;
         2039 +                sye = syb;
         2040 +                syb = tmp;
         2041 +        }
         2042 +
         2043 +        bufsize = (term.col+1) * (sye - syb + 1) * UTF_SIZ;
         2044          ptr = str = xmalloc(bufsize);
         2045  
         2046          /* append every set & selected glyph to the selection */
         2047 -        for (y = sel.nb.y; y <= sel.ne.y; y++) {
         2048 +        for (y = syb; y <= sye; y++) {
         2049                  if ((linelen = tlinelen(y)) == 0) {
         2050                          *ptr++ = '\n';
         2051                          continue;
         2052                  }
         2053  
         2054                  if (sel.type == SEL_RECTANGULAR) {
         2055 -                        gp = &term.line[y][sel.nb.x];
         2056 +                        gp = &TLINE(y)[sel.nb.x];
         2057                          lastx = sel.ne.x;
         2058                  } else {
         2059 -                        gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
         2060 -                        lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
         2061 +                        gp = &TLINE(y)[syb == y ? sel.nb.x : 0];
         2062 +                        lastx = (sye == y) ? sel.ne.x : term.col-1;
         2063                  }
         2064 -                last = &term.line[y][MIN(lastx, linelen-1)];
         2065 +                last = &TLINE(y)[MIN(lastx, linelen-1)];
         2066                  while (last >= gp && last->u == ' ')
         2067                          --last;
         2068  
         2069 @@ -836,6 +819,9 @@ void
         2070  ttywrite(const char *s, size_t n, int may_echo)
         2071  {
         2072          const char *next;
         2073 +        Arg arg = (Arg) { .i = term.scr };
         2074 +
         2075 +        kscrolldown(&arg);
         2076  
         2077          if (may_echo && IS_SET(MODE_ECHO))
         2078                  twrite(s, n, 1);
         2079 @@ -1047,13 +1033,53 @@ tswapscreen(void)
         2080  }
         2081  
         2082  void
         2083 -tscrolldown(int orig, int n)
         2084 +kscrolldown(const Arg* a)
         2085 +{
         2086 +        int n = a->i;
         2087 +
         2088 +        if (n < 0)
         2089 +                n = term.row + n;
         2090 +
         2091 +        if (n > term.scr)
         2092 +                n = term.scr;
         2093 +
         2094 +        if (term.scr > 0) {
         2095 +                term.scr -= n;
         2096 +                selscroll(0, -n);
         2097 +                tfulldirt();
         2098 +        }
         2099 +}
         2100 +
         2101 +void
         2102 +kscrollup(const Arg* a)
         2103 +{
         2104 +        int n = a->i;
         2105 +
         2106 +        if (n < 0)
         2107 +                n = term.row + n;
         2108 +
         2109 +        if (term.scr <= HISTSIZE-n) {
         2110 +                term.scr += n;
         2111 +                selscroll(0, n);
         2112 +                tfulldirt();
         2113 +        }
         2114 +}
         2115 +
         2116 +void
         2117 +tscrolldown(int orig, int n, int copyhist)
         2118  {
         2119          int i;
         2120          Line temp;
         2121  
         2122          LIMIT(n, 0, term.bot-orig+1);
         2123  
         2124 +        if (copyhist) {
         2125 +                term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE;
         2126 +                temp = term.hist[term.histi];
         2127 +                term.hist[term.histi] = term.line[term.bot];
         2128 +                term.line[term.bot] = temp;
         2129 +        }
         2130 +
         2131          tsetdirt(orig, term.bot-n);
         2132          tclearregion(0, term.bot-n+1, term.col-1, term.bot);
         2133  
         2134 @@ -1067,13 +1093,23 @@ tscrolldown(int orig, int n)
         2135  }
         2136  
         2137  void
         2138 -tscrollup(int orig, int n)
         2139 +tscrollup(int orig, int n, int copyhist)
         2140  {
         2141          int i;
         2142          Line temp;
         2143  
         2144          LIMIT(n, 0, term.bot-orig+1);
         2145  
         2146 +        if (copyhist) {
         2147 +                term.histi = (term.histi + 1) % HISTSIZE;
         2148 +                temp = term.hist[term.histi];
         2149 +                term.hist[term.histi] = term.line[orig];
         2150 +                term.line[orig] = temp;
         2151 +        }
         2152 +
         2153 +        if (term.scr > 0 && term.scr < HISTSIZE)
         2154 +                term.scr = MIN(term.scr + n, HISTSIZE-1);
         2155 +
         2156          tclearregion(0, orig, term.col-1, orig+n-1);
         2157          tsetdirt(orig+n, term.bot);
         2158  
         2159 @@ -1093,6 +1129,7 @@ selscroll(int orig, int n)
         2160                  return;
         2161  
         2162          if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.bot)) {
         2163 +                sel.oe.scroll = sel.ob.scroll = term.scr;
         2164                  if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) {
         2165                          selclear();
         2166                          return;
         2167 @@ -1122,13 +1159,19 @@ tnewline(int first_col)
         2168          int y = term.c.y;
         2169  
         2170          if (y == term.bot) {
         2171 -                tscrollup(term.top, 1);
         2172 +                tscrollup(term.top, 1, 1);
         2173          } else {
         2174                  y++;
         2175          }
         2176          tmoveto(first_col ? 0 : term.c.x, y);
         2177  }
         2178  
         2179 +int
         2180 +currentLine(int x, int y)
         2181 +{
         2182 +        return (x == term.c.x || y == term.c.y);
         2183 +}
         2184 +
         2185  void
         2186  csiparse(void)
         2187  {
         2188 @@ -1181,6 +1224,8 @@ tmoveto(int x, int y)
         2189          term.c.state &= ~CURSOR_WRAPNEXT;
         2190          term.c.x = LIMIT(x, 0, term.col-1);
         2191          term.c.y = LIMIT(y, miny, maxy);
         2192 +        // Set the last position in order to restore after normal mode exits.
         2193 +        onMove();
         2194  }
         2195  
         2196  void
         2197 @@ -1287,14 +1332,14 @@ void
         2198  tinsertblankline(int n)
         2199  {
         2200          if (BETWEEN(term.c.y, term.top, term.bot))
         2201 -                tscrolldown(term.c.y, n);
         2202 +                tscrolldown(term.c.y, n, 0);
         2203  }
         2204  
         2205  void
         2206  tdeleteline(int n)
         2207  {
         2208          if (BETWEEN(term.c.y, term.top, term.bot))
         2209 -                tscrollup(term.c.y, n);
         2210 +                tscrollup(term.c.y, n, 0);
         2211  }
         2212  
         2213  int32_t
         2214 @@ -1725,11 +1770,11 @@ csihandle(void)
         2215                  break;
         2216          case 'S': /* SU -- Scroll <n> line up */
         2217                  DEFAULT(csiescseq.arg[0], 1);
         2218 -                tscrollup(term.top, csiescseq.arg[0]);
         2219 +                tscrollup(term.top, csiescseq.arg[0], 0);
         2220                  break;
         2221          case 'T': /* SD -- Scroll <n> line down */
         2222                  DEFAULT(csiescseq.arg[0], 1);
         2223 -                tscrolldown(term.top, csiescseq.arg[0]);
         2224 +                tscrolldown(term.top, csiescseq.arg[0], 0);
         2225                  break;
         2226          case 'L': /* IL -- Insert <n> blank lines */
         2227                  DEFAULT(csiescseq.arg[0], 1);
         2228 @@ -2235,7 +2280,7 @@ eschandle(uchar ascii)
         2229                  return 0;
         2230          case 'D': /* IND -- Linefeed */
         2231                  if (term.c.y == term.bot) {
         2232 -                        tscrollup(term.top, 1);
         2233 +                        tscrollup(term.top, 1, 1);
         2234                  } else {
         2235                          tmoveto(term.c.x, term.c.y+1);
         2236                  }
         2237 @@ -2248,7 +2293,7 @@ eschandle(uchar ascii)
         2238                  break;
         2239          case 'M': /* RI -- Reverse index */
         2240                  if (term.c.y == term.top) {
         2241 -                        tscrolldown(term.top, 1);
         2242 +                        tscrolldown(term.top, 1, 1);
         2243                  } else {
         2244                          tmoveto(term.c.x, term.c.y-1);
         2245                  }
         2246 @@ -2290,7 +2335,7 @@ tputc(Rune u)
         2247  {
         2248          char c[UTF_SIZ];
         2249          int control;
         2250 -        int width, len;
         2251 +        int width = 0, len;
         2252          Glyph *gp;
         2253  
         2254          control = ISCONTROL(u);
         2255 @@ -2469,7 +2514,7 @@ twrite(const char *buf, int buflen, int show_ctrl)
         2256  void
         2257  tresize(int col, int row)
         2258  {
         2259 -        int i;
         2260 +        int i, j;
         2261          int minrow = MIN(row, term.row);
         2262          int mincol = MIN(col, term.col);
         2263          int *bp;
         2264 @@ -2506,6 +2551,14 @@ tresize(int col, int row)
         2265          term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
         2266          term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
         2267  
         2268 +        for (i = 0; i < HISTSIZE; i++) {
         2269 +                term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph));
         2270 +                for (j = mincol; j < col; j++) {
         2271 +                        term.hist[i][j] = term.c.attr;
         2272 +                        term.hist[i][j].u = ' ';
         2273 +                }
         2274 +        }
         2275 +
         2276          /* resize each row to new width, zero-pad if needed */
         2277          for (i = 0; i < minrow; i++) {
         2278                  term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
         2279 @@ -2563,7 +2616,7 @@ drawregion(int x1, int y1, int x2, int y2)
         2280                          continue;
         2281  
         2282                  term.dirty[y] = 0;
         2283 -                xdrawline(term.line[y], x1, y, x2);
         2284 +                xdrawline(TLINE(y), x1, y, x2);
         2285          }
         2286  }
         2287  
         2288 @@ -2584,8 +2637,8 @@ draw(void)
         2289                  cx--;
         2290  
         2291          drawregion(0, 0, term.col, term.row);
         2292 -        xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
         2293 -                        term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
         2294 +        xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
         2295 +                        term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
         2296          term.ocx = cx, term.ocy = term.c.y;
         2297          xfinishdraw();
         2298          xximspot(term.ocx, term.ocy);
         2299 diff --git a/st.h b/st.h
         2300 index a1928ca..3aad9f3 100644
         2301 --- a/st.h
         2302 +++ b/st.h
         2303 @@ -1,5 +1,8 @@
         2304  /* See LICENSE for license details. */
         2305  
         2306 +#include "glyph.h"
         2307 +#include "normalMode.h"
         2308 +
         2309  #include <stdint.h>
         2310  #include <sys/types.h>
         2311  
         2312 @@ -33,6 +36,8 @@ enum glyph_attribute {
         2313          ATTR_WRAP       = 1 << 8,
         2314          ATTR_WIDE       = 1 << 9,
         2315          ATTR_WDUMMY     = 1 << 10,
         2316 +        ATTR_HIGHLIGHT  = 1 << 12,
         2317 +        ATTR_CURRENT    = 1 << 13,
         2318          ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
         2319  };
         2320  
         2321 @@ -42,11 +47,6 @@ enum selection_mode {
         2322          SEL_READY = 2
         2323  };
         2324  
         2325 -enum selection_type {
         2326 -        SEL_REGULAR = 1,
         2327 -        SEL_RECTANGULAR = 2
         2328 -};
         2329 -
         2330  enum selection_snap {
         2331          SNAP_WORD = 1,
         2332          SNAP_LINE = 2
         2333 @@ -57,18 +57,6 @@ typedef unsigned int uint;
         2334  typedef unsigned long ulong;
         2335  typedef unsigned short ushort;
         2336  
         2337 -typedef uint_least32_t Rune;
         2338 -
         2339 -#define Glyph Glyph_
         2340 -typedef struct {
         2341 -        Rune u;           /* character code */
         2342 -        ushort mode;      /* attribute flags */
         2343 -        uint32_t fg;      /* foreground  */
         2344 -        uint32_t bg;      /* background  */
         2345 -} Glyph;
         2346 -
         2347 -typedef Glyph *Line;
         2348 -
         2349  typedef union {
         2350          int i;
         2351          uint ui;
         2352 @@ -81,6 +69,11 @@ void die(const char *, ...);
         2353  void redraw(void);
         2354  void draw(void);
         2355  
         2356 +int currentLine(int, int);
         2357 +void kscrolldown(const Arg *);
         2358 +void kscrollup(const Arg *);
         2359 +void normalMode(Arg const *);
         2360 +
         2361  void printscreen(const Arg *);
         2362  void printsel(const Arg *);
         2363  void sendbreak(const Arg *);
         2364 @@ -90,6 +83,9 @@ int tattrset(int);
         2365  void tnew(int, int);
         2366  void tresize(int, int);
         2367  void tsetdirtattr(int);
         2368 +size_t utf8decode(const char *, Rune *, size_t);
         2369 +Rune utf8decodebyte(char, size_t *);
         2370 +void tsetdirt(int, int);
         2371  void ttyhangup(void);
         2372  int ttynew(char *, char *, char *, char **);
         2373  size_t ttyread(void);
         2374 @@ -100,8 +96,10 @@ void resettitle(void);
         2375  
         2376  void selclear(void);
         2377  void selinit(void);
         2378 -void selstart(int, int, int);
         2379 -void selextend(int, int, int, int);
         2380 +void selstart(int, int, int, int);
         2381 +void xselstart(int, int, int);
         2382 +void selextend(int, int, int, int, int);
         2383 +void xselextend(int, int, int, int);
         2384  int selected(int, int);
         2385  char *getsel(void);
         2386  
         2387 diff --git a/term.h b/term.h
         2388 new file mode 100644
         2389 index 0000000..23adf0e
         2390 --- /dev/null
         2391 +++ b/term.h
         2392 @@ -0,0 +1,73 @@
         2393 +#ifndef TERM_H
         2394 +#define TERM_H
         2395 +
         2396 +//
         2397 +// Internal terminal structs.
         2398 +//
         2399 +
         2400 +#include "glyph.h"
         2401 +
         2402 +#include <stdint.h>
         2403 +
         2404 +#define HISTSIZE      2500
         2405 +
         2406 +typedef struct {
         2407 +        Glyph attr; /* current char attributes */
         2408 +        int x;
         2409 +        int y;
         2410 +        char state;
         2411 +} TCursor;
         2412 +
         2413 +typedef struct {
         2414 +        int mode;
         2415 +        int type;
         2416 +        int snap;
         2417 +        /// Selection variables:
         2418 +        /// ob – original coordinates of the beginning of the selection
         2419 +        /// oe – original coordinates of the end of the selection
         2420 +        struct {
         2421 +                int x, y, scroll;
         2422 +        } ob, oe;
         2423 +        /// Selection variables; currently displayed chunk.
         2424 +        /// nb – normalized coordinates of the beginning of the selection
         2425 +        /// ne – normalized coordinates of the end of the selection
         2426 +        struct {
         2427 +                int x, y;
         2428 +        } nb, ne;
         2429 +
         2430 +        int alt;
         2431 +} Selection;
         2432 +
         2433 +/* Internal representation of the screen */
         2434 +typedef struct {
         2435 +        int row;      /* nb row */
         2436 +        int col;      /* nb col */
         2437 +        Line *line;   /* screen */
         2438 +        Line *alt;    /* alternate screen */
         2439 +        Line hist[HISTSIZE]; /* history buffer */
         2440 +        int histi;    /* history index */
         2441 +        int scr;      /* scroll back */
         2442 +        int *dirty;   /* dirtyness of lines */
         2443 +        TCursor c;    /* cursor */
         2444 +        int ocx;      /* old cursor col */
         2445 +        int ocy;      /* old cursor row */
         2446 +        int top;      /* top    scroll limit */
         2447 +        int bot;      /* bottom scroll limit */
         2448 +        int mode;     /* terminal mode flags */
         2449 +        int esc;      /* escape state flags */
         2450 +        char trantbl[4]; /* charset table translation */
         2451 +        int charset;  /* current charset */
         2452 +        int icharset; /* selected charset for sequence */
         2453 +        int *tabs;
         2454 +} Term;
         2455 +
         2456 +extern Term term;
         2457 +
         2458 +#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \
         2459 +                 term.scr + HISTSIZE + 1) % HISTSIZE] : \
         2460 +                 term.line[(y) - term.scr])
         2461 +
         2462 +extern Selection sel;
         2463 +
         2464 +
         2465 +#endif // TERM_H
         2466 diff --git a/win.h b/win.h
         2467 index a6ef1b9..1a6fefe 100644
         2468 --- a/win.h
         2469 +++ b/win.h
         2470 @@ -19,6 +19,7 @@ enum win_mode {
         2471          MODE_MOUSEMANY   = 1 << 15,
         2472          MODE_BRCKTPASTE  = 1 << 16,
         2473          MODE_NUMLOCK     = 1 << 17,
         2474 +        MODE_NORMAL      = 1 << 18,
         2475          MODE_MOUSE       = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
         2476                            |MODE_MOUSEMANY,
         2477  };
         2478 @@ -27,6 +28,7 @@ void xbell(void);
         2479  void xclipcopy(void);
         2480  void xdrawcursor(int, int, Glyph, int, int, Glyph);
         2481  void xdrawline(Line, int, int, int);
         2482 +void xdrawglyph(Glyph, int, int);
         2483  void xfinishdraw(void);
         2484  void xloadcols(void);
         2485  int xsetcolorname(int, const char *);
         2486 diff --git a/x.c b/x.c
         2487 index 1f62129..e297946 100644
         2488 --- a/x.c
         2489 +++ b/x.c
         2490 @@ -143,7 +143,6 @@ typedef struct {
         2491  static inline ushort sixd_to_16bit(int);
         2492  static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
         2493  static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
         2494 -static void xdrawglyph(Glyph, int, int);
         2495  static void xclear(int, int, int, int);
         2496  static int xgeommasktogravity(int);
         2497  static int ximopen(Display *);
         2498 @@ -355,7 +354,7 @@ mousesel(XEvent *e, int done)
         2499                          break;
         2500                  }
         2501          }
         2502 -        selextend(evcol(e), evrow(e), seltype, done);
         2503 +        xselextend(evcol(e), evrow(e), seltype, done);
         2504          if (done)
         2505                  setsel(getsel(), e->xbutton.time);
         2506  }
         2507 @@ -471,7 +470,7 @@ bpress(XEvent *e)
         2508                  xsel.tclick2 = xsel.tclick1;
         2509                  xsel.tclick1 = now;
         2510  
         2511 -                selstart(evcol(e), evrow(e), snap);
         2512 +                xselstart(evcol(e), evrow(e), snap);
         2513          }
         2514  }
         2515  
         2516 @@ -757,6 +756,13 @@ xloadcolor(int i, const char *name, Color *ncolor)
         2517          return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor);
         2518  }
         2519  
         2520 +void
         2521 +normalMode(Arg const *_) {
         2522 +        (void) _;
         2523 +        win.mode ^= MODE_NORMAL;
         2524 +}
         2525 +
         2526 +
         2527  void
         2528  xloadcols(void)
         2529  {
         2530 @@ -1344,6 +1350,14 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
         2531                  base.fg = defaultattr;
         2532          }
         2533  
         2534 +        if (base.mode & ATTR_HIGHLIGHT) {
         2535 +                base.bg = highlightBg;
         2536 +                base.fg = highlightFg;
         2537 +        } else if ((base.mode & ATTR_CURRENT) && (win.mode & MODE_NORMAL)) {
         2538 +                base.bg = currentBg;
         2539 +                base.fg = currentFg;
         2540 +        }
         2541 +
         2542          if (IS_TRUECOL(base.fg)) {
         2543                  colfg.alpha = 0xffff;
         2544                  colfg.red = TRUERED(base.fg);
         2545 @@ -1433,7 +1447,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
         2546                  xclear(winx, winy + win.ch, winx + width, win.h);
         2547  
         2548          /* Clean up the region we want to draw to. */
         2549 -        XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
         2550 +         XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
         2551  
         2552          /* Set the clip region because Xft is sometimes dirty. */
         2553          r.x = 0;
         2554 @@ -1476,8 +1490,9 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
         2555          Color drawcol;
         2556  
         2557          /* remove the old cursor */
         2558 -        if (selected(ox, oy))
         2559 -                og.mode ^= ATTR_REVERSE;
         2560 +        if (selected(ox, oy)) og.mode ^= ATTR_REVERSE;
         2561 +        if (highlighted(ox, oy)) { og.mode ^= ATTR_HIGHLIGHT; }
         2562 +        if (currentLine(ox, oy)) { og.mode ^= ATTR_CURRENT; }
         2563          xdrawglyph(og, ox, oy);
         2564  
         2565          if (IS_SET(MODE_HIDE))
         2566 @@ -1509,6 +1524,11 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
         2567                  drawcol = dc.col[g.bg];
         2568          }
         2569  
         2570 +        if ((g.mode & ATTR_CURRENT) && (win.mode & MODE_NORMAL)) {
         2571 +                g.bg = currentBg;
         2572 +                g.fg = currentFg;
         2573 +        }
         2574 +
         2575          /* draw the new one */
         2576          if (IS_SET(MODE_FOCUSED)) {
         2577                  switch (win.cursor) {
         2578 @@ -1592,12 +1612,18 @@ xdrawline(Line line, int x1, int y1, int x2)
         2579  
         2580          numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
         2581          i = ox = 0;
         2582 -        for (x = x1; x < x2 && i < numspecs; x++) {
         2583 +        for (x = x1; x < x2 && i < numspecs; ++x) {
         2584                  new = line[x];
         2585                  if (new.mode == ATTR_WDUMMY)
         2586                          continue;
         2587                  if (selected(x, y1))
         2588                          new.mode ^= ATTR_REVERSE;
         2589 +                if (highlighted(x, y1)) {
         2590 +                        new.mode ^= ATTR_HIGHLIGHT;
         2591 +                }
         2592 +                if (currentLine(x, y1)) {
         2593 +                        new.mode ^= ATTR_CURRENT;
         2594 +                }
         2595                  if (i > 0 && ATTRCMP(base, new)) {
         2596                          xdrawglyphfontspecs(specs, base, i, ox, y1);
         2597                          specs += i;
         2598 @@ -1786,6 +1812,14 @@ kpress(XEvent *ev)
         2599                  len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status);
         2600          else
         2601                  len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
         2602 +
         2603 +        if (IS_SET(MODE_NORMAL)) {
         2604 +                ExitState const es = kpressNormalMode(buf, len, // strlen(buf),
         2605 +                                 match(ControlMask, e->state),
         2606 +                                 &ksym);
         2607 +                 if (es == finished) { normalMode(NULL); }
         2608 +                return;
         2609 +        }
         2610          /* 1. shortcuts */
         2611          for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
         2612                  if (ksym == bp->keysym && match(bp->mod, e->state)) {
         2613 -- 
         2614 2.25.0
         2615