sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | 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