[wiki] [sites] [st][patch][vimbrowse]The most recent patch no longer applied cleanly to the latest st version, this fixes it. I also took the liberty of making it such that the patch now only modifies config.def.h and no longer modifies config.h directly. || dadaurs

From: <git_AT_suckless.org>
Date: Thu, 04 Jun 2020 11:29:03 +0200

commit 8695d862cea9c589cb373b00b9d1a05639a5b04e
Author: dadaurs <david.wiedemann_AT_outlook.com>
Date: Thu Jun 4 11:24:42 2020 +0200

    [st][patch][vimbrowse]The most recent patch no longer applied cleanly to
    the latest st version, this fixes it.
    I also took the liberty of making it such that the patch now only
    modifies config.def.h and no longer modifies config.h directly.

diff --git a/st.suckless.org/patches/vim_browse/index.md b/st.suckless.org/patches/vim_browse/index.md
index b866802c..1789b115 100644
--- a/st.suckless.org/patches/vim_browse/index.md
+++ b/st.suckless.org/patches/vim_browse/index.md
_AT_@ -114,10 +114,12 @@ operating e.g. with the current head of master (`26cdfeb`, 02-2020).
 **All versions based on `26cdfeb` (from old to new) **:
 * [st-vimBrowse-20200212-26cdfeb.diff (attached)](st-vimBrowse-20200212-26cdfeb.diff)
 * [st-vimBrowse-20200212-26cdfeb.diff (Github)](https://github.com/juliusHuelsmann/st/releases/download/patchesV3/st-vimBrowse-20200212-26cdfeb.diff)
-
-**Most Recent based on `26cdfeb` **:
 * [st-vimBrowse-20200212-26cdfeb.diff (Github)](https://github.com/juliusHuelsmann/st/releases/download/patchesV3/st-vimBrowse-20200212-26cdfeb.diff)
 
+**Most Recent based on `295a43f` **:
+[st-vimBrowse-20200604-295a43f.diff](st-vimBrowse-20200604-295a43f.diff)
+
+
 Authors of the [Scrollback patch](https://st.suckless.org/patches/scrollback/)
 ------------------------------------------------------------------------------
 * Jochen Sprickerhof - <st_AT_jochen.sprickerhof.de>
diff --git a/st.suckless.org/patches/vim_browse/st-vimBrowse-20200604-295a43f.diff b/st.suckless.org/patches/vim_browse/st-vimBrowse-20200604-295a43f.diff
new file mode 100644
index 00000000..60165a76
--- /dev/null
+++ b/st.suckless.org/patches/vim_browse/st-vimBrowse-20200604-295a43f.diff
_AT_@ -0,0 +1,2188 @@
+diff -ruN st-default/config.def.h st1/config.def.h
+--- st-default/config.def.h 2020-06-04 11:15:55.164135902 +0200
++++ st1/config.def.h 2020-06-04 11:15:28.476134951 +0200
+_AT_@ -56,6 +56,10 @@
+ static double minlatency = 8;
+ static double maxlatency = 33;
+
++/* frames per second st should at maximum draw to the screen */
++static unsigned int xfps = 120;
++static unsigned int actionfps = 30;
++
+ /*
+ * blinking timeout (set to 0 to disable blinking) for the terminal blinking
+ * attribute.
+_AT_@ -160,6 +164,14 @@
+ * doesn't match the ones requested.
+ */
+ static unsigned int defaultattr = 11;
++/// Colors for the entities that are 'highlighted' in normal mode (search
++/// results currently on screen) [Vim Browse].
++static unsigned int highlightBg = 160;
++static unsigned int highlightFg = 15;
++/// Colors for highlighting the current cursor position (row + col) in normal
++/// mode [Vim Browse].
++static unsigned int currentBg = 8;
++static unsigned int currentFg = 15;
+
+ /*
+ * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set).
+_AT_@ -175,18 +187,18 @@
+ static MouseShortcut mshortcuts[] = {
+ /* mask button function argument release */
+ { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 },
+- { ShiftMask, Button4, ttysend, {.s = "[5;2~"} },
+ { XK_ANY_MOD, Button4, ttysend, {.s = ""} },
+- { ShiftMask, Button5, ttysend, {.s = "[6;2~"} },
+ { XK_ANY_MOD, Button5, ttysend, {.s = ""} },
+ };
+
+ /* Internal keyboard shortcuts. */
+ #define MODKEY Mod1Mask
++#define AltMask Mod1Mask
+ #define TERMMOD (ControlMask|ShiftMask)
+
+ static Shortcut shortcuts[] = {
+ /* mask keysym function argument */
++ { AltMask, XK_c, normalMode, {.i = 0} },
+ { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} },
+ { ControlMask, XK_Print, toggleprinter, {.i = 0} },
+ { ShiftMask, XK_Print, printscreen, {.i = 0} },
+_AT_@ -199,6 +211,8 @@
+ { TERMMOD, XK_Y, selpaste, {.i = 0} },
+ { ShiftMask, XK_Insert, selpaste, {.i = 0} },
+ { TERMMOD, XK_Num_Lock, numlock, {.i = 0} },
++ { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} },
++ { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} },
+ };
+
+ /*
+_AT_@ -470,3 +484,45 @@
+ " !\"#$%&'()*+,-./0123456789:;<=>?"
+ "_AT_ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_"
+ "`abcdefghijklmnopqrstuvwxyz{|}~";
++
++
++/// word sepearors normal mode
++/// [Vim Browse].
++char wordDelimSmall[] = " !\"#$%&'()*+,-./:;<=>?_AT_[\]^_`{|}~";
++char wordDelimLarge[] = " "; /// <Word sepearors normal mode (capital W)
++
++/// Shortcusts executed in normal mode (which should not already be in use)
++/// [Vim Browse].
++struct NormalModeShortcuts normalModeShortcuts [] = {
++ { 'R', "?Building
" },
++ { 'r', "/Building
" },
++ { 'F', "?: error:
" },
++ { 'f', "/: error:
" },
++ { 'Q', "?[Leaving vim, starting execution]
" },
++ { 'S', "Qf" },
++ { 'X', "?juli_AT_machine
" },
++ { 'x', "/juli_AT_machine
" },
++};
++
++size_t const amountNormalModeShortcuts = sizeof(normalModeShortcuts) / sizeof(*normalModeShortcuts);
++
++/// Style of the command string visualized in normal mode in the right corner
++/// [Vim Browse].
++Glyph const styleCommand = {' ', ATTR_ITALIC | ATTR_FAINT, 7, 16};
++/// Style of the search string visualized in normal mode in the right corner.
++/// [Vim Browse].
++Glyph const styleSearch = {' ', ATTR_ITALIC | ATTR_BOLD_FAINT, 7, 16};
++
++/// Colors used in normal mode in order to highlight different operations and
++/// empathise the current position on screen in the status area [Vim Browse].
++unsigned int bgCommandYank = 11;
++unsigned int bgCommandVisual = 4;
++unsigned int bgCommandVisualLine = 12;
++
++unsigned int fgCommandYank = 232;
++unsigned int fgCommandVisual = 232;
++unsigned int fgCommandVisualLine = 232;
++
++unsigned int bgPos = 15;
++unsigned int fgPos = 16;
++
+diff -ruN st-default/dynamicArray.h st1/dynamicArray.h
+--- st-default/dynamicArray.h 1970-01-01 01:00:00.000000000 +0100
++++ st1/dynamicArray.h 2020-06-04 11:04:30.227111509 +0200
+_AT_@ -0,0 +1,175 @@
++#ifndef DYNAMIC_ARRAY_H
++#define DYNAMIC_ARRAY_H
++
++#include "error.h"
++
++#include <stdint.h>
++#include <stdlib.h>
++#include <string.h>
++#include <stdbool.h>
++
++/// Struct for which this file offers functionality in order to expand the array
++/// and set / get its content.
++typedef struct DynamicArray {
++ /// Size of the datatype contained in the array.
++ uint8_t itemSize;
++ /// Amount of bytes currently initialized
++ uint32_t index;
++ /// Amount of bytes currently reserved (not necessarily initialized)
++ uint32_t allocated;
++ /// Actual content.
++ char* content;
++} DynamicArray;
++
++#define EXPAND_STEP 15
++
++/// Default initializers for the dynamic array.
++#define CHAR_ARRAY {1, 0, 0, NULL}
++#define WORD_ARRAY {2, 0, 0, NULL}
++#define DWORD_ARRAY {4, 0, 0, NULL}
++#define QWORD_ARRAY {8, 0, 0, NULL}
++/// (Wasteful) utf-8 array, that always used 4 bytes in order to display a
++/// character, even if the space is not required.
++#define UTF8_ARRAY DWORD_ARRAY
++
++/// Check that at least \p bytes are allocated, if true implying that
++/// \p s->content[ytes - 1] is allocated.
++static inline bool
++isAllocated(DynamicArray const *s, uint32_t bytes) {
++ return s != NULL && s->allocated >= bytes;
++}
++
++/// _AT_see #isAllocated
++static inline bool
++isInitialized(DynamicArray const *s, uint32_t bytes) {
++ return s != NULL && s->index >= bytes;
++}
++
++/// Return the next element in \p s and increment index without checking bounds.
++static inline char*
++gnext(DynamicArray *s) {
++ ENSURE(s!=NULL, return NULL);
++ ENSURE(s->index % s->itemSize == 0 && "(index not aligned)",
++ s->index += s->itemSize - (s->index % s->itemSize));
++ ENSURE(isAllocated(s, s->index + 2 * s->itemSize), return NULL);
++ return s->content + (s->index += s->itemSize);
++}
++
++/// View element \p i in \p s.
++static inline char*
++view(DynamicArray const * s, uint32_t i) {
++ ENSURE((s != NULL) && isAllocated(s, (i+1) * s->itemSize), return NULL);
++ return s->content + i*s->itemSize;
++}
++
++/// Inspect element content[size() - 1 - i].
++static inline char *
++viewEnd(DynamicArray const *s, uint32_t i) {
++ ENSURE((s != NULL) && isInitialized(s, i * s->itemSize), return NULL);
++ ENSURE(s->index%s->itemSize == 0 && "(index not aligned)", return NULL);
++ return s->content + s->index - (i + 1) * s->itemSize;
++}
++
++/// Set conent without applying
++static inline bool
++setValues(DynamicArray* s, char const *vals, uint32_t amount) {
++ ENSURE(vals != NULL, return false);
++ ENSURE((s != NULL) && isAllocated(s, s->index + amount), return false);
++ memcpy(s->content + s->index, vals, amount);
++ return true;
++}
++
++static inline bool
++snext(DynamicArray* s, char const *vals, uint32_t amount) {
++ bool const success = setValues(s, vals, amount);
++ ENSURE(success, return false);
++ uint8_t const rest = amount % s->itemSize;
++ uint32_t const newSize = s->index + amount + (rest ? s->itemSize : 0);
++ ENSURE(isAllocated(s, newSize), return false);
++ s->index = newSize;
++ return true;
++}
++
++/// Empty \p s.
++static inline void
++empty(DynamicArray* s) {
++ ENSURE((s != NULL), return);
++ s->index = 0;
++}
++
++/// Check if \p s has initialized content (which can be the case even if memory
++/// is allocated).
++static inline bool
++isEmpty(DynamicArray const * s) {
++ ENSURE((s != NULL), return true);
++ return s->index == 0;
++}
++
++static inline int
++size(DynamicArray const * s) {
++ ENSURE(s != NULL, return 0);
++ ENSURE(s->itemSize != 0, return 0);
++ return s->index / s->itemSize;
++}
++
++static inline void
++pop(DynamicArray* s) {
++ ENSURE((s != NULL), return);
++ ENSURE(s->index % s->itemSize == 0 && "(index not aligned)",
++ s->index += s->itemSize - (s->index % s->itemSize));
++ ENSURE(isInitialized(s, s->itemSize), return);
++ s->index -= s->itemSize;
++}
++
++static inline bool
++checkSetNext(DynamicArray *s, char const *c, uint32_t amount) {
++ ENSURE(s != NULL && c != NULL, return false);
++ if (s->allocated < s->index + s->itemSize * amount) {
++ uint32_t const diff = s->index+s->itemSize*amount-s->allocated;
++ uint32_t const newAlloSize = s->allocated + (diff > EXPAND_STEP
++ ? diff : EXPAND_STEP) * s->itemSize;
++ char* tmp = realloc(s->content, newAlloSize);
++ if (tmp == NULL) { return false; }
++ s->allocated = newAlloSize;
++ s->content = tmp;
++ assert(s->allocated >= s->index + s->itemSize * amount);
++ }
++ if (amount) { snext(s, c, amount); }
++ return true;
++}
++
++static inline bool
++checkSetNextV(DynamicArray *s, char const c) {
++ return checkSetNext(s, &c, 1);
++}
++
++static inline bool
++checkSetNextP(DynamicArray *s, char const *c) {
++ ENSURE(c != NULL, return false);
++ return checkSetNext(s, c, strlen(c));
++}
++
++/// Expand the currently initialized content in \p s and the allocated chunk of
++/// memory if required.
++static char *
++expand(DynamicArray *s) {
++ ENSURE(s != NULL, return NULL);
++ if (s->allocated < s->index + s->itemSize) {
++ uint32_t const diff = s->index + s->itemSize - s->allocated;
++ uint32_t const newAlloSize = s->allocated + (diff > EXPAND_STEP
++ ? diff : EXPAND_STEP) * s->itemSize;
++ char* tmp = realloc(s->content, newAlloSize);
++ if (tmp == NULL) { return NULL; }
++ s->allocated = newAlloSize;
++ s->content = tmp;
++ assert(s->allocated >= s->index + s->itemSize);
++ }
++ s->index+=s->itemSize;
++ return viewEnd(s, 0);
++}
++
++#define append(s, c) checkSetNext((s), (char const *) (c), (s)->itemSize)
++#define appendPartial(s, c, i) checkSetNext((s), (char const *) (c), (i))
++
++
++#endif // DYNAMIC_ARRAY_H
+diff -ruN st-default/error.h st1/error.h
+--- st-default/error.h 1970-01-01 01:00:00.000000000 +0100
++++ st1/error.h 2020-06-04 11:04:30.227111509 +0200
+_AT_@ -0,0 +1,47 @@
++#ifndef ERROR_H
++#define ERROR_H
++
++#include <assert.h>
++
++// Flag which determines whether to fail if a required condition is not met, or
++// to adapt the condition in order to work properly.
++// Attention: Be sure to perform a clean build after you alter preprocessor
++// directives / definitions.
++//#define FAIL_ON_ERROR
++
++#include <stdio.h>
++
++///
++/// Function used in case the fail-on-error mode is disabled (via definition)
++/// to report errors. In debug production mode, alias st to st 2> error.log.
++static void reportError(char const * cond, char const * stt, char const * file,
++ unsigned int line ) {
++ unsigned int const maxErrorCount = 100;
++ static unsigned int errorCount = 0;
++ if (++errorCount == 1) {
++ printf("Report the following bug to "
++ "https://github.com/juliusHuelsmann/st.
");
++ }
++ if (errorCount < maxErrorCount) {
++ printf("Bug: Condition '%s' evaluates to false.
        Performing"
++ " '%s' to counteract.
        File:%s:%u
",
++ cond, stt, file, line);
++ } else if (errorCount == maxErrorCount) {
++ printf("Max amount of reported errors %u is reached. From here"
++ "on, no additional errors will be reported.
",
++ maxErrorCount);
++ }
++}
++
++/// Note that everyting condition checked / endforced with #ENSURE is
++/// considered an error, and behaves like an error depending on the flag.
++#ifdef FAIL_ON_ERROR
++#define ENSURE(cond, stt) assert(cond);
++#else // FAIL_ON_ERROR
++#define ENSURE(cond, stt) if (!(cond)) { \
++ reportError(#cond, #stt, __FILE__, __LINE__); \
++ stt; \
++ }
++#endif // FAIL_ON_ERROR
++
++#endif // ERROR_H
+diff -ruN st-default/glyph.h st1/glyph.h
+--- st-default/glyph.h 1970-01-01 01:00:00.000000000 +0100
++++ st1/glyph.h 2020-06-04 11:04:30.228111510 +0200
+_AT_@ -0,0 +1,30 @@
++#ifndef LINE_H
++#define LINE_H
++
++//
++// Contains the representation of the entities in the buffer (Line, Gylph), that
++// is used by every part of the software implmeneting terminal logic.
++//
++
++#include <stdint.h>
++
++enum selection_type {
++ SEL_REGULAR = 1,
++ SEL_RECTANGULAR = 2
++};
++
++typedef uint_least32_t Rune;
++
++#define Glyph Glyph_
++
++typedef struct {
++ Rune u; /* character code */
++ unsigned short mode; /* attribute flags */
++ uint32_t fg; /* foreground */
++ uint32_t bg; /* background */
++} Glyph;
++
++
++typedef Glyph *Line;
++
++#endif // LINE_H
+diff -ruN st-default/Makefile st1/Makefile
+--- st-default/Makefile 2020-06-04 11:15:55.164135902 +0200
++++ st1/Makefile 2020-06-04 11:04:30.228111510 +0200
+_AT_@ -4,7 +4,7 @@
+
+ include config.mk
+
+-SRC = st.c x.c
++SRC = st.c x.c normalMode.c
+ OBJ = $(SRC:.c=.o)
+
+ all: options st
+_AT_@ -21,8 +21,8 @@
+ .c.o:
+ $(CC) $(STCFLAGS) -c $<
+
+-st.o: config.h st.h win.h
+-x.o: arg.h config.h st.h win.h
++st.o: config.h st.h win.h dynamicArray.h normalMode.h term.h glyph.h error.h
++x.o: arg.h config.h st.h win.h dynamicArray.h normalMode.h term.h glyph.h error.h
+
+ $(OBJ): config.h config.mk
+
+_AT_@ -35,7 +35,8 @@
+ dist: clean
+ mkdir -p st-$(VERSION)
+ cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\
+- config.def.h st.info st.1 arg.h st.h win.h $(SRC)\
++ config.def.h st.info st.1 arg.h st.h win.h dynamicArray.h\
++ normalMode.h term.h error.h $(SRC)\
+ st-$(VERSION)
+ tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz
+ rm -rf st-$(VERSION)
+diff -ruN st-default/normalMode.c st1/normalMode.c
+--- st-default/normalMode.c 1970-01-01 01:00:00.000000000 +0100
++++ st1/normalMode.c 2020-06-04 11:04:30.229111510 +0200
+_AT_@ -0,0 +1,752 @@
++/* See LICENSE for license details. */
++#include "normalMode.h"
++#include "dynamicArray.h"
++#include "term.h"
++#include "win.h"
++#include "error.h"
++
++#include <X11/keysym.h>
++#include <X11/XKBlib.h>
++
++#include <ctype.h>
++#include <stdio.h>
++#include <limits.h>
++#include <math.h>
++
++#define LEN(a) (sizeof(a) / sizeof(a)[0])
++#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
++//#define FALLTHROUGH __attribute__((fallthrough));
++#define FALLTHROUGH
++#define SEC(var,ini,h,r) var = ini; if (!var) { h; return r; }
++#define EXPAND(v1,v2,r) char *SEC(v1, expand(v2), empty(v2), true)
++#define currentCommand (toggle ? &commandHist0 : &commandHist1)
++#define lastCommand (toggle ? &commandHist1 : &commandHist0)
++
++//
++// Interface to the terminal
++extern Glyph const styleCommand, styleSearch;
++extern NormalModeShortcuts normalModeShortcuts[];
++extern size_t const amountNormalModeShortcuts;
++extern char wordDelimSmall[];
++extern char wordDelimLarge[];
++extern unsigned int fgCommandYank, fgCommandVisual, fgCommandVisualLine,
++ bgCommandYank, bgCommandVisual, bgCommandVisualLine, bgPos, fgPos;
++
++extern void selclear(void);
++extern void tsetdirt(int, int);
++extern size_t utf8encode(Rune, char *);
++extern size_t utf8decode(const char *, Rune *, size_t);
++extern size_t utf8decodebyte(char c, size_t *i);
++
++extern void selextend(int, int, int, int, int);
++extern void selstart(int, int, int, int);
++extern char *getsel(void);
++extern void tfulldirt(void);
++
++//
++// `Private` structs
++typedef struct { uint32_t x; uint32_t y; uint32_t yScr; } Position;
++
++/// Entire normal mode state, consisting of an operation and a motion.
++typedef struct {
++ Position initialPosition;
++ struct OperationState {
++ enum Operation {
++ noop = ' ', visual='v', visualLine='V', yank = 'y' } op;
++ Position startPosition;
++ enum Infix { infix_none = 0, infix_i = 1, infix_a = 2, } infix;
++ } command;
++ struct MotionState {
++ uint32_t amount;
++ enum Search {none, forward, backward} search;
++ Position searchPosition;
++ bool finished;
++ } motion;
++} NormalModeState;
++
++/// Default state if no operation is performed.
++NormalModeState defaultNormalMode = {
++ {0,0,0}, {noop, {0, 0, 0}, false}, {0, none, {0, 0, 0}, true}
++};
++NormalModeState stateVB = {
++ {0,0,0}, {noop, {0, 0, 0}, false}, {0, none, {0, 0, 0}, true}
++};
++
++DynamicArray searchString = UTF8_ARRAY;
++DynamicArray commandHist0 = UTF8_ARRAY;
++DynamicArray commandHist1 = UTF8_ARRAY;
++DynamicArray highlights = DWORD_ARRAY;
++
++/// History command toggle
++static bool toggle = false;
++
++//
++// Utility functions
++static inline int intervalDiff(int v, int a, int b) {
++ return (v < a) ? (v - a) : ((v > b) ? (v - b) : 0);
++}
++static inline void swap(DynamicArray *const a, DynamicArray *const b) {
++ DynamicArray tmp = *a; *a = *b; *b = tmp;
++}
++static inline int max(int a, int b) { return a > b ? a : b; }
++static inline int min(int a, int b) { return a < b ? a : b; }
++static inline int mod(int a, int b) { for (; a < 0; a += b); return a % b; }
++static inline bool contains (char c, char const * values, uint32_t memSize) {
++ ENSURE(values != NULL, return false);
++ for (uint32_t i = 0; i < memSize; ++i) if (c == values[i]) return true;
++ return false;
++}
++static inline void applyPosition(Position const *pos) {
++ ENSURE(pos != NULL, return);
++ term.c.x = pos->x;
++ term.c.y = pos->y;
++ term.scr = pos->yScr;
++}
++static inline int getSearchDirection(void) {
++ return stateVB.motion.search == forward ? 1 : -1;
++}
++
++// Utilities for working with the current version of the scrollback patch.
++static bool moveLine(int32_t const amount) {
++ int32_t const reqShift = intervalDiff(term.c.y+=amount, 0, term.row-1);
++ term.c.y -= reqShift;
++ int32_t const sDiff = intervalDiff(term.scr-=reqShift, 0, HISTSIZE-1);
++ term.scr -= sDiff;
++ return sDiff == 0;
++}
++
++static void moveLetter(int32_t const amount) {
++ int32_t value = (term.c.x += amount) / term.col;
++ if (value -= (term.c.x < 0)) {
++ term.c.x = moveLine(value) ? mod(term.c.x, term.col)
++ : max(min(term.c.x,term.col - 1), 0);
++ }
++ assert(BETWEEN(term.c.x,0,term.col-1)&&BETWEEN(term.c.y,0,term.row-1));
++}
++
++//
++// `Private` functions:
++
++// Functions: Temporarily display string on screen.
++
++/// Display string at end of a specified line without writing it into the buffer
++/// _AT_param str string that is to be displayed
++/// _AT_param g glyph
++/// _AT_param yPos
++static void
++displayString(DynamicArray const *str, Glyph const *g, int yPos, bool prePos) {
++ ENSURE((str != NULL) && (g != NULL) && (term.row > 0), return);
++ ENSURE(yPos >= 0, yPos = 0);
++ ENSURE(yPos < term.row, yPos = term.row - 1);
++ // Arbritary limit to avoid withhelding too much info from user.
++ int const maxFractionOverridden = 3;
++ // Threshold: if there is no space to print, do not print, but transfer
++ // repsonsibility for printing back to [st].
++ if (term.col < maxFractionOverridden) { // (0)
++ term.dirty[yPos] = 1;
++ return;
++ }
++ int32_t const botSz = prePos * 6; //< sz for position indication
++ // Determine the dimensions of used chunk of screen.
++ int32_t const overrideSize = min(size(str) + botSz,
++ term.col / maxFractionOverridden); // (1)
++ int32_t const overrideEnd = term.col - 2;
++ // Has to follow trivially hence th assert:
++ // overrideSize <(1)= term.col/3 <(0)= term.col = overrideEnd + 1.
++ assert(overrideSize <= overrideEnd + 1);
++ int32_t const overrideStart = 1 + overrideEnd - overrideSize;
++ // display history[history.size() - (overrideSize - botSz)::-1]
++ Glyph *SEC(line, malloc(sizeof(Glyph) * (overrideSize)),,)
++ int32_t offset = (size(str) - overrideSize - 1 + botSz) * str->itemSize;
++ for (uint32_t chr = 0; chr < overrideSize - botSz; ++chr) {
++ line[chr] = *g;
++ line[chr].u = *((Rune*) (str->content+(offset+=str->itemSize)));
++ }
++ if (prePos) {
++ ENSURE(term.scr < HISTSIZE, term.scr = HISTSIZE - 1);
++ int const p=(int)(0.5+(HISTSIZE-1-term.scr)*100./(HISTSIZE-1));
++ int const v = min(max(p, 0), 100);
++ char prc [10];
++ switch (term.scr) {
++ case HISTSIZE - 1: strcpy(prc, " [TOP]"); break;
++ case 0: strcpy(prc, " [BOT]"); break;
++ default: sprintf(prc, " % 3d%c ", v, '%');
++ }
++ for (uint32_t chr = 0; chr < botSz; ++chr) {
++ line[chr + overrideSize - botSz] =*g;
++ line[chr + overrideSize - botSz].fg = fgPos;
++ line[chr + overrideSize - botSz].bg = bgPos;
++ utf8decode(&prc[chr],&line[chr+overrideSize-botSz].u,1);
++ }
++ line[overrideSize - botSz] =*g;
++ }
++ xdrawline(TLINE(yPos), 0, yPos, overrideStart);
++ term.c.y -= term.row; term.c.x -= term.col; // not highlight hack
++ xdrawline(line-overrideStart, overrideStart, yPos, overrideEnd + 1);
++ term.c.y += term.row; term.c.x += term.col;
++ free(line);
++}
++
++static inline void printCommandString(void) {
++ Glyph g = styleCommand;
++ switch(stateVB.command.op) {
++ case yank: g.fg = fgCommandYank; g.bg = bgCommandYank; break;
++ case visual: g.fg=fgCommandVisual; g.bg=bgCommandVisual; break;
++ case visualLine: g.fg=fgCommandVisualLine;
++ g.bg=bgCommandVisualLine;
++ }
++ displayString(isEmpty(currentCommand) ? lastCommand : currentCommand,
++ &g, term.row - 1, true);
++}
++
++static inline void printSearchString(void) {
++ displayString(&searchString, &styleSearch, term.row - 2, false);
++}
++
++// NormalMode Operation / Motion utilies.
++
++static inline bool isMotionFinished(void) { return stateVB.motion.finished; }
++
++static inline void finishMotion(void) { stateVB.motion.finished = true; }
++
++static inline bool isOperationFinished(void) {
++ return stateVB.command.op==noop && stateVB.command.infix==infix_none;
++}
++
++/// Register that the current comamnd is finished and a new command is lgoged
++static inline void startNewCommand(bool abort) {
++ if (!abort) { toggle = !toggle; }
++ empty(currentCommand);
++}
++
++static inline void finishOperation(void) {
++ stateVB.command = defaultNormalMode.command;
++ assert(isOperationFinished());
++ // After an operation is finished, the selection has to be released and
++ // no highlights are to be released.
++ selclear();
++ empty(&highlights);
++ // THe command string is reset for a new command.
++ startNewCommand(true);
++}
++
++static inline void enableOperation(enum Operation o) {
++ finishOperation();
++ stateVB.command.op = o;
++ stateVB.command.infix = infix_none;
++ stateVB.command.startPosition.x = term.c.x;
++ stateVB.command.startPosition.y = term.c.y;
++ stateVB.command.startPosition.yScr = term.scr;
++}
++
++/// _AT_param abort: If enabled, the command exits without registering
++/// _AT_return Whether the the application is ready to yield control back to
++//the normal command flow
++static bool terminateCommand(bool abort) {
++ bool const exitOperation = isMotionFinished();
++ bool exitNormalMode = false;
++ finishMotion();
++
++ if (exitOperation) {
++ exitNormalMode = isOperationFinished();
++ finishOperation();
++ }
++ printCommandString();
++ printSearchString();
++ return exitNormalMode;
++}
++
++static inline void exitCommand(void) { terminateCommand(false); }
++
++static inline void abortCommand(void) { terminateCommand(true); }
++
++/// Go to next occurrence of string relative to the current location
++/// conduct search, starting at start pos
++static bool gotoString(int8_t sign) {
++ moveLetter(sign);
++ uint32_t const searchStrSize = size(&searchString);
++ uint32_t const maxIter = (HISTSIZE+term.row) * term.col + searchStrSize;
++ uint32_t findIdx = 0;
++ for (uint32_t cIteration = 0; findIdx < searchStrSize
++ && ++cIteration <= maxIter; moveLetter(sign)) {
++ char const * const SEC(next, sign==1
++ ? view(&searchString, findIdx)
++ : viewEnd(&searchString, findIdx), , false)
++ uint32_t const searchChar = *((uint32_t*) next);
++
++ if (TLINE(term.c.y)[term.c.x].u == searchChar) { ++findIdx; }
++ else { findIdx = 0; }
++ }
++ bool const found = findIdx == searchStrSize;
++ for (uint32_t i = 0; found && i < searchStrSize; ++i) moveLetter(-sign);
++ return found;
++}
++
++/// Highlight all found strings on the current screen.
++static void highlightStringOnScreen(void) {
++ if (isEmpty(&searchString)) { return; }
++ empty(&highlights);
++ uint32_t const searchStringSize = size(&searchString);
++ uint32_t findIdx = 0;
++ uint32_t xStart, yStart;
++ bool success = true;
++ for (int y = 0; y < term.row && success; y++) {
++ for (int x = 0; x < term.col && success; x++) {
++ char const* const SEC(next,
++ view(&searchString,findIdx),,)
++ if (TLINE(y)[x].u == (Rune) *((uint32_t*)(next))) {
++ if (++findIdx == 1) {
++ xStart = x;
++ yStart = y;
++ }
++ if (findIdx == searchStringSize) {
++ success = success
++ && append(&highlights, &xStart)
++ && append(&highlights, &yStart);
++ findIdx = 0; //term.dirty[yStart] = 1;
++ }
++ } else { findIdx = 0; }
++ }
++ }
++ if (!success) { empty(&highlights); }
++}
++
++static bool gotoStringAndHighlight(int8_t sign) {
++ // Find hte next occurrence of the #searchString in direction #sign
++ bool const found = gotoString(sign);
++ if (!found) { applyPosition(&stateVB.motion.searchPosition); }
++ highlightStringOnScreen();
++ //tsetdirt(0, term.row-3); //< everything except for the 'status bar'
++ return found;
++}
++
++static bool pressKeys(char const* nullTerminatedString, size_t end) {
++ bool sc = true;
++ for (size_t i = 0; i < end && sc; ++i) {
++ sc = kpressNormalMode(&nullTerminatedString[i], 1, false, NULL);
++ }
++ return sc;
++}
++
++static bool executeCommand(DynamicArray const *command) {
++ size_t end=size(command);
++ char decoded [32];
++ bool succ = true;
++ size_t len;
++ for (size_t i = 0; i < end && succ; ++i) {
++ char const *const SEC(nextRune, view(command, i),,false)
++ len = utf8encode(*((Rune *) nextRune), decoded);
++ succ = kpressNormalMode(decoded, len, false, NULL);
++ }
++ return succ;
++}
++
++struct { char const first; char const second; } const Brackets [] =
++{ {'(', ')'}, {'<', '>'}, {'{', '}'}, {'[', ']'}, };
++
++
++/// Emits Command prefix and suffix when i motion is performed (e.g. yiw).
++///
++/// _AT_param c: motion character
++/// _AT_param expandMode: 1 for 'i', 2 for 'a'
++/// _AT_param first, second: Dynamic arrays in which the prefix and postfix
++/// commands will be returned
++/// _AT_return whether the command could be extracted successfully.
++static bool expandExpression(char const c, enum Infix expandMode,
++ char operation, DynamicArray *cmd) {
++ empty(cmd);
++ bool s = true; //< used in order to detect memory allocation errors.
++ char const lower = tolower(c);
++ // Motions
++ if (lower == 'w') {
++ // translated into wb[command]e resp. WB[command]E, which works
++ // file even when at the fist letter. Does not work for single
++ // letter words though.
++ int const diff = c - lower;
++ s = s && checkSetNextV(cmd, c);
++ s = s && checkSetNextV(cmd, (signed char)(((int)'b') + diff));
++ s = s && checkSetNextV(cmd, operation);
++ s = s && checkSetNextV(cmd, (signed char)(((int)'e')+ diff));
++ return s;
++ }
++ // Symmetrical brackets (quotation marks)
++ if (c == '\'' || c == '"') {
++ // Local ambiguity -> do nothing. It cannot be determined if
++ // the current char is the 1st or last char of the selection.
++ // <---- search here? -- ['] -- or search here? --->
++ if (TLINE(term.c.y)[term.c.x].u == c) {
++ return false;
++ }
++ // Prefix
++ char res [] = {'?', c, '
'};
++ s = s && checkSetNextP(cmd, res);
++ // infix
++ bool const iffy = expandMode == infix_i;
++ if (iffy) { s = s && checkSetNextV(cmd, 'l'); }
++ s = s && checkSetNextV(cmd, operation);
++ if (!iffy) { s = s && checkSetNextV(cmd, 'l'); }
++ // suffix
++ res[0] = '/';
++ s = s && checkSetNextP(cmd, res);
++ if (iffy) { s = s && checkSetNextV(cmd, 'h'); }
++ return s;
++ }
++ // Brackets: Does not if in range / if the brackets belong togehter.
++ for (size_t pid = 0; pid < sizeof(Brackets); ++pid) {
++ if(Brackets[pid].first == c || Brackets[pid].second == c) {
++ if (TLINE(term.c.y)[term.c.x].u!=Brackets[pid].first) {
++ s = s && checkSetNextV(cmd, '?');
++ s = s && checkSetNextV(cmd, Brackets[pid].first);
++ s = s && checkSetNextV(cmd, '
');
++ }
++ bool const iffy = expandMode == infix_i;
++ if (iffy) { s = s && checkSetNextV(cmd, 'l'); }
++ s = s && checkSetNextV(cmd, operation);
++ if (!iffy) { s = s && checkSetNextV(cmd, 'l'); }
++ s = s && checkSetNextV(cmd, '/');
++ s = s && checkSetNextV(cmd, Brackets[pid].second);
++ s = s && checkSetNextV(cmd, '
');
++ if (iffy) { s = s && checkSetNextV(cmd, 'h'); }
++ return s;
++ }
++ }
++ /**/
++ // search string
++ // complicated search operation: <tag>
++ if (c == 't') {
++ // XXX: (Bug in vim: _AT_vit )
++ // <tag_name attr="hier" a2="\<sch\>"> [current pos] </tag_name>
++
++ // 1. Copy history ( tag := hist[?<
:/
] )
++ // 2. Copy history ( first_find := hist[?<
: next place in
++ // history where count '>' > count '<'
++ // (can be behind current pos) )
++ // 3. first := [?first_find][#first_ind]l
++ // second:= [/tag">"]h
++ //return true; // XXX: not implmented yet.
++ }
++ return false;
++}
++
++//
++// Public API
++//
++
++void onMove(void) {
++ stateVB.initialPosition.x = term.c.x;
++ stateVB.initialPosition.y = term.c.y;
++ stateVB.initialPosition.yScr = term.scr;
++}
++
++int highlighted(int x, int y) {
++ // Compute the legal bounds for a hit:
++ int32_t const stringSize = size(&searchString);
++ int32_t xMin = x - stringSize;
++ int32_t yMin = y;
++ while (xMin < 0 && yMin > 0) {
++ xMin += term.col;
++ --yMin;
++ }
++ if (xMin < 0) { xMin = 0; }
++
++ uint32_t highSize = size(&highlights);
++ ENSURE(highSize % 2 == 0, empty(&highlights); return false;);
++ highSize /= 2;
++ uint32_t *ptr = (uint32_t*) highlights.content;
++ for (uint32_t i = 0; i < highSize; ++i) {
++ int32_t const sx = (int32_t) *(ptr++);
++ int32_t const sy = (int32_t) *(ptr++);
++ if (BETWEEN(sy, yMin, y) && (sy != yMin || sx > xMin)
++ && (sy != y || sx <= x)) {
++ return true;
++ }
++ }
++ return false;
++}
++
++ExitState kpressNormalMode(char const * cs, int len, bool ctrl, void const *v) {
++ KeySym const * const ksym = (KeySym*) v;
++ bool const esc = ksym && *ksym == XK_Escape;
++ bool const enter = (ksym && *ksym==XK_Return) || (len==1 &&cs[0]=='
');
++ bool const quantifier = len == 1 && (BETWEEN(cs[0], 49, 57)
++ || (cs[0] == 48 && stateVB.motion.amount));
++ int const previousScroll = term.scr;
++ // [ESC] or [ENTER] abort resp. finish the current level of operation.
++ // Typing 'i' if no operation is currently performed behaves like ESC.
++ if (esc || enter || (len == 1 && cs[0] == 'i' && isMotionFinished()
++ && isOperationFinished())) {
++ if (terminateCommand(!enter)) {
++ applyPosition(&stateVB.initialPosition);
++ Position const pc = stateVB.initialPosition;
++ stateVB = defaultNormalMode;
++ stateVB.initialPosition = pc;
++ tfulldirt();
++ return finished;
++ }
++ len = 0;
++ goto motionFinish;
++ }
++ // Backspace
++ if (ksym && *ksym == XK_BackSpace) {
++ bool s = stateVB.motion.search!=none&&!stateVB.motion.finished;
++ bool q = stateVB.motion.amount != 0;
++ if (!(s || q)) { return failed; }
++ len = 0;
++
++ if (!isEmpty(currentCommand)) { pop(currentCommand); }
++ if (s) {
++ if (!isEmpty(&searchString)) { pop(&searchString); }
++ else if (isEmpty(&searchString)) {
++ exitCommand();
++ return success;
++ }
++ } else if (q) {
++ stateVB.motion.amount /= 10;
++ goto finishNoAppend;
++ }
++ }
++
++ // Search: append to search string, then search & highlight
++ if (stateVB.motion.search != none && !stateVB.motion.finished) {
++ if (len >= 1) {
++ EXPAND(kSearch, &searchString, true)
++ utf8decode(cs, (Rune*)(kSearch), len);
++ }
++ applyPosition(&stateVB.motion.searchPosition);
++ gotoStringAndHighlight(getSearchDirection());
++ goto finish;
++ }
++ if (len == 0) { return failed; }
++ // Quantifiers
++ if (quantifier) {
++ stateVB.motion.amount = min(SHRT_MAX,
++ stateVB.motion.amount * 10 + cs[0] - 48);
++ goto finish;
++ }
++ // 'i' mode enabled, hence the expression is to be expanded:
++ // [start_expression(cs[0])] [operation] [stop_expression(cs[0])]
++ if (stateVB.command.infix != infix_none && stateVB.command.op != noop) {
++ DynamicArray cmd = CHAR_ARRAY;
++ char const operation = stateVB.command.op;
++ bool succ = expandExpression(cs[0],
++ stateVB.command.infix, visual, &cmd);
++ if (operation == yank) {
++ succ = succ && checkSetNextV(&cmd, operation);
++ }
++ NormalModeState const st = stateVB;
++ TCursor const tc = term.c;
++ stateVB.command.infix = infix_none;
++ if (succ) {
++ stateVB.command.op = noop;
++ for (int i = 0; i < size(&cmd) && succ; ++i) {
++ succ = pressKeys(&cmd.content[i], 1);
++ }
++ if (!succ) { // go back to the old position, apply op
++ stateVB = st;
++ term.c = tc;
++ }
++ empty(currentCommand);
++ for (uint32_t i = 0; i < size(&cmd); ++i) {
++ EXPAND(kCommand, currentCommand, true)
++ utf8decode(cmd.content+i, (Rune*)(kCommand),1);
++ }
++ }
++ free(cmd.content);
++ goto finishNoAppend;
++ }
++ // Commands (V / v or y)
++ switch(cs[0]) {
++ case '.':
++ {
++ if (isEmpty(currentCommand)) { toggle = !toggle; }
++ DynamicArray cmd = UTF8_ARRAY;
++ swap(&cmd, currentCommand);
++ executeCommand(&cmd) ? success : failed;
++ swap(&cmd, currentCommand);
++ free(cmd.content);
++ goto finishNoAppend;
++ }
++ case 'i': stateVB.command.infix = infix_i; goto finish;
++ case 'a': stateVB.command.infix = infix_a; goto finish;
++ case 'y':
++ switch(stateVB.command.op) {
++ case noop: //< Start yank mode & set #op
++ enableOperation(yank);
++ selstart(term.c.x, term.c.y,term.scr,0);
++ goto finish;
++ case yank: //< Complete yank [y#amount j]
++ selstart(0, term.c.y, term.scr, 0);
++ int const origY = term.c.y;
++ moveLine(max(stateVB.motion.amount, 1));
++ selextend(term.col-1,term.c.y,term.scr,
++ SEL_RECTANGULAR, 0);
++ term.c.y = origY;
++ FALLTHROUGH
++ case visualLine: // Yank visual selection
++ case visual:
++ xsetsel(getsel());
++ xclipcopy();
++ exitCommand();
++ goto finish;
++ default:
++ return failed;
++ }
++ case visual:
++ case visualLine:
++ if (stateVB.command.op == cs[0]) {
++ finishOperation();
++ return true;
++ } else {
++ enableOperation(cs[0]);
++ selstart(cs[0] == visualLine ? 0 : term.c.x,
++ term.c.y, term.scr, 0);
++ goto finish;
++ }
++ }
++ // CTRL Motions
++ int32_t sign = -1; //< if command goes 'forward'(1) or 'backward'(-1)
++ if (ctrl) {
++ if (ksym == NULL) { return false; }
++ switch(*ksym) {
++ case XK_f:
++ term.scr = max(term.scr - max(term.row-2,1), 0);
++ term.c.y = 0;
++ goto finish;
++ case XK_b:
++ term.scr = min(term.scr + max(term.row - 2, 1),
++ HISTSIZE - 1);
++ term.c.y = term.bot;
++ goto finish;
++ case XK_u:
++ term.scr = min(term.scr+term.row/2, HISTSIZE-1);
++ goto finish;
++ case XK_d:
++ term.scr = max(term.scr - term.row / 2, 0);
++ goto finish;
++ default: return false;
++ }
++ }
++ // Motions
++ switch(cs[0]) {
++ case 'c': empty(&commandHist0); empty(&commandHist1);
++ goto finishNoAppend;
++ case 'j': sign = 1; FALLTHROUGH
++ case 'k': moveLine(max(stateVB.motion.amount,1) * sign);
++ goto motionFinish;
++ case 'H': term.c.y = 0;
++ goto motionFinish;
++ case 'M': term.c.y = term.bot / 2;
++ goto motionFinish;
++ case 'L': term.c.y = term.bot;
++ goto motionFinish;
++ case 'G': applyPosition(&stateVB.initialPosition);
++ goto motionFinish;
++ case 'l': sign = 1; FALLTHROUGH
++ case 'h': moveLetter(sign * max(stateVB.motion.amount,1));
++ goto motionFinish;
++ case '0': term.c.x = 0;
++ goto motionFinish;
++ case '$': term.c.x = term.col-1;
++ goto motionFinish;
++ case 'w': FALLTHROUGH
++ case 'W': FALLTHROUGH
++ case 'e': FALLTHROUGH
++ case 'E': sign = 1; FALLTHROUGH
++ case 'B': FALLTHROUGH
++ case 'b': {
++ char const * const wDelim =
++ cs[0] <= 90 ? wordDelimLarge : wordDelimSmall;
++ uint32_t const wDelimLen = strlen(wDelim);
++
++ bool const startSpaceIsSeparator =
++ !(cs[0] == 'w' || cs[0] == 'W');
++ // Whether to start & end with offset:
++ bool const performOffset = startSpaceIsSeparator;
++ // Max iteration := One complete hist traversal.
++ uint32_t const maxIter = (HISTSIZE+term.row) * term.col;
++ // Doesn't work exactly as in vim: Linebreak is
++ // counted as 'normal' separator, hence a jump can
++ // span multiple lines here.
++ stateVB.motion.amount = max(stateVB.motion.amount, 1);
++ for (;stateVB.motion.amount>0;--stateVB.motion.amount) {
++ uint8_t state = 0;
++ if (performOffset) { moveLetter(sign); }
++ for (uint32_t cIt = 0; cIt ++ < maxIter; moveLetter(sign)) {
++ if (startSpaceIsSeparator == contains(TLINE(term.c.y)[term.c.x].u, wDelim, wDelimLen)) {
++ if (state == 1) {
++ if (performOffset) {
++ moveLetter(-sign);
++ }
++ break;
++ }
++ } else if (state == 0) { state = 1; }
++ }
++ }
++ goto motionFinish;
++ }
++ case '/': sign = 1; FALLTHROUGH
++ case '?':
++ empty(&searchString);
++ stateVB.motion.search = sign == 1 ? forward : backward;
++ stateVB.motion.searchPosition.x = term.c.x;
++ stateVB.motion.searchPosition.y = term.c.y;
++ stateVB.motion.searchPosition.yScr = term.scr;
++ stateVB.motion.finished = false;
++ goto finish;
++ case 'n': sign = 1; FALLTHROUGH
++ case 'N': {
++ if (stateVB.motion.search == none) return failed;
++ if (stateVB.motion.search == backward) { sign *= -1; }
++ bool b = true; int ox = term.c.x;
++ int oy = term.c.y ; int scr = term.scr;
++ int32_t i = max(stateVB.motion.amount, 1);
++ for (;i>0 && (b=gotoString(sign)); --i) {
++ oy = term.c.y; scr = term.scr;
++ }
++ if (!b) { term.c.x = ox; term.c.y = oy; term.scr = scr;}
++ goto motionFinish;
++ }
++ case 't': // Toggle selection mode and set dirt.
++ sel.type = sel.type == SEL_REGULAR
++ ? SEL_RECTANGULAR : SEL_REGULAR;
++ //tsetdirt(sel.nb.y, sel.ne.y);
++ goto motionFinish;
++ }
++ // Custom commands
++ for (size_t i = 0; i < amountNormalModeShortcuts; ++i) {
++ if (cs[0] == normalModeShortcuts[i].key) {
++ return pressKeys(normalModeShortcuts[i].value,
++ strlen(normalModeShortcuts[i].value))
++ ? success : failed;
++ }
++ }
++ return failed;
++motionFinish:
++ stateVB.motion.amount = 0;
++ //if (isMotionFinished() && stateVB.command.op == yank) {
++ if (stateVB.command.op == yank) {
++ selextend(term.c.x, term.c.y, term.scr, sel.type, 0);
++ xsetsel(getsel());
++ xclipcopy();
++ exitCommand();
++ }
++finish:
++ if (len == 1 && !ctrl) { // XXX: for now.
++ EXPAND(kCommand, currentCommand, true)
++ utf8decode(cs, (Rune*)(kCommand), len);
++ }
++finishNoAppend:
++ if (stateVB.command.op == visual) {
++ selextend(term.c.x, term.c.y, term.scr, sel.type, 0);
++ } else if (stateVB.command.op == visualLine) {
++ selextend(term.col-1, term.c.y, term.scr, sel.type, 0);
++ }
++
++ if (previousScroll != term.scr && !isEmpty(&searchString)) {
++ highlightStringOnScreen();
++ }
++ tsetdirt(0, term.row-3); //< Required because of the cursor cross.
++ printCommandString();
++ printSearchString();
++ return success;
++}
+diff -ruN st-default/normalMode.h st1/normalMode.h
+--- st-default/normalMode.h 1970-01-01 01:00:00.000000000 +0100
++++ st1/normalMode.h 2020-06-04 11:04:30.229111510 +0200
+_AT_@ -0,0 +1,36 @@
++/* See LICENSE for license details. */
++#ifndef NORMAL_MODE_H
++#define NORMAL_MODE_H
++
++#include <stdbool.h>
++#include <stddef.h>
++#include <stdint.h>
++
++/// Used in the configuration file to define custom shortcuts.
++typedef struct NormalModeShortcuts {
++ char key;
++ char *value;
++} NormalModeShortcuts;
++
++/// Holds the exit status of the #kpressNormalMode function, which informs the
++/// caller when to exit normal mode.
++typedef enum ExitState {
++ failed = 0,
++ success = 1,
++ finished = 2,
++} ExitState;
++
++/// Called when curr position is altered.
++void onMove(void);
++
++/// Function which returns whether the value at position provided as arguments
++/// is to be highlighted.
++int highlighted(int, int);
++
++/// Handles keys in normal mode.
++ExitState kpressNormalMode(char const * decoded, int len, bool ctrlPressed,
++ void const * ksym);
++ //bool esc, bool enter, bool backspace, void* keysym);
++
++
++#endif // NORMAL_MODE_H
+Binary files st-default/normalMode.o and st1/normalMode.o differ
+Binary files st-default/st and st1/st differ
+diff -ruN st-default/st.c st1/st.c
+--- st-default/st.c 2020-06-04 11:15:55.165135902 +0200
++++ st1/st.c 2020-06-04 11:04:30.231111510 +0200
+_AT_@ -1,8 +1,10 @@
+ /* See LICENSE for license details. */
++#include <assert.h>
+ #include <ctype.h>
+ #include <errno.h>
+ #include <fcntl.h>
+ #include <limits.h>
++#include <math.h>
+ #include <pwd.h>
+ #include <stdarg.h>
+ #include <stdio.h>
+_AT_@ -17,6 +19,8 @@
+ #include <unistd.h>
+ #include <wchar.h>
+
++
++#include "term.h"
+ #include "st.h"
+ #include "win.h"
+
+_AT_@ -42,6 +46,7 @@
+ #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
+ #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
+ #define ISDELIM(u) (u && wcschr(worddelimiters, u))
++#define INTERVAL(x, a, b) (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
+
+ enum term_mode {
+ MODE_WRAP = 1 << 0,
+_AT_@ -86,17 +91,17 @@
+ ESC_DCS =128,
+ };
+
+-typedef struct {
+- Glyph attr; /* current char attributes */
+- int x;
+- int y;
+- char state;
+-} TCursor;
+-
+-typedef struct {
+- int mode;
+- int type;
+- int snap;
++/*typedef struct {*/
++ /*Glyph attr; [> current char attributes <]*/
++ /*int x;*/
++ /*int y;*/
++ /*char state;*/
++/*} TCursor;*/
++
++/*typedef struct {*/
++ /*int mode;*/
++ /*int type;*/
++ /*int snap;*/
+ /*
+ * Selection variables:
+ * nb – normalized coordinates of the beginning of the selection
+_AT_@ -104,33 +109,33 @@
+ * ob – original coordinates of the beginning of the selection
+ * oe – original coordinates of the end of the selection
+ */
+- struct {
+- int x, y;
+- } nb, ne, ob, oe;
+-
+- int alt;
+-} Selection;
+-
+-/* Internal representation of the screen */
+-typedef struct {
+- int row; /* nb row */
+- int col; /* nb col */
+- Line *line; /* screen */
+- Line *alt; /* alternate screen */
+- int *dirty; /* dirtyness of lines */
+- TCursor c; /* cursor */
+- int ocx; /* old cursor col */
+- int ocy; /* old cursor row */
+- int top; /* top scroll limit */
+- int bot; /* bottom scroll limit */
+- int mode; /* terminal mode flags */
+- int esc; /* escape state flags */
+- char trantbl[4]; /* charset table translation */
+- int charset; /* current charset */
+- int icharset; /* selected charset for sequence */
+- int *tabs;
+- Rune lastc; /* last printed char outside of sequence, 0 if control */
+-} Term;
++ /*struct {*/
++ /*int x, y;*/
++ /*} nb, ne, ob, oe;*/
++
++ /*int alt;*/
++/*} Selection;*/
++
++/*[> Internal representation of the screen <]*/
++/*typedef struct {*/
++ /*int row; [> nb row <]*/
++ /*int col; [> nb col <]*/
++ /*Line *line; [> screen <]*/
++ /*Line *alt; [> alternate screen <]*/
++ /*int *dirty; [> dirtyness of lines <]*/
++ /*TCursor c; [> cursor <]*/
++ /*int ocx; [> old cursor col <]*/
++ /*int ocy; [> old cursor row <]*/
++ /*int top; [> top scroll limit <]*/
++ /*int bot; [> bottom scroll limit <]*/
++ /*int mode; [> terminal mode flags <]*/
++ /*int esc; [> escape state flags <]*/
++ /*char trantbl[4]; [> charset table translation <]*/
++ /*int charset; [> current charset <]*/
++ /*int icharset; [> selected charset for sequence <]*/
++ /*int *tabs;*/
++ /*Rune lastc; [> last printed char outside of sequence, 0 if control <]*/
++/*} Term;*/
+
+ /* CSI Escape sequence structs */
+ /* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */
+_AT_@ -154,6 +159,8 @@
+ int narg; /* nb of args */
+ } STREscape;
+
++void tfulldirt(void);
++
+ static void execsh(char *, char **);
+ static void stty(char **);
+ static void sigchld(int);
+_AT_@ -186,16 +193,14 @@
+ static void tputtab(int);
+ static void tputc(Rune);
+ static void treset(void);
+-static void tscrollup(int, int);
+-static void tscrolldown(int, int);
++static void tscrollup(int, int, int);
++static void tscrolldown(int, int, int);
+ static void tsetattr(int *, int);
+ static void tsetchar(Rune, Glyph *, int, int);
+-static void tsetdirt(int, int);
+ static void tsetscroll(int, int);
+ static void tswapscreen(void);
+ static void tsetmode(int, int, int *, int);
+ static int twrite(const char *, int, int);
+-static void tfulldirt(void);
+ static void tcontrolcode(uchar );
+ static void tdectest(char );
+ static void tdefutf8(char);
+_AT_@ -209,8 +214,6 @@
+ static void selscroll(int, int);
+ static void selsnap(int *, int *, int);
+
+-static size_t utf8decode(const char *, Rune *, size_t);
+-static Rune utf8decodebyte(char, size_t *);
+ static char utf8encodebyte(Rune, size_t);
+ static size_t utf8validate(Rune *, size_t);
+
+_AT_@ -220,8 +223,8 @@
+ static ssize_t xwrite(int, const char *, size_t);
+
+ /* Globals */
+-static Term term;
+-static Selection sel;
++Term term;
++Selection sel;
+ static CSIEscape csiescseq;
+ static STREscape strescseq;
+ static int iofd = 1;
+_AT_@ -416,17 +419,22 @@
+ {
+ int i = term.col;
+
+- if (term.line[y][i - 1].mode & ATTR_WRAP)
++ if (TLINE(y)[i - 1].mode & ATTR_WRAP)
+ return i;
+
+- while (i > 0 && term.line[y][i - 1].u == ' ')
++ while (i > 0 && TLINE(y)[i - 1].u == ' ')
+ --i;
+
+ return i;
+ }
+
+ void
+-selstart(int col, int row, int snap)
++xselstart(int col, int row, int snap) {
++ selstart(col, row, term.scr, snap);
++}
++
++void
++selstart(int col, int row, int scroll, int snap)
+ {
+ selclear();
+ sel.mode = SEL_EMPTY;
+_AT_@ -435,6 +443,7 @@
+ sel.snap = snap;
+ sel.oe.x = sel.ob.x = col;
+ sel.oe.y = sel.ob.y = row;
++ sel.oe.scroll = sel.ob.scroll = scroll;
+ selnormalize();
+
+ if (sel.snap != 0)
+_AT_@ -443,10 +452,13 @@
+ }
+
+ void
+-selextend(int col, int row, int type, int done)
+-{
+- int oldey, oldex, oldsby, oldsey, oldtype;
++xselextend(int col, int row, int type, int done) {
++ selextend(col, row, term.scr, type, done);
++}
+
++void
++selextend(int col, int row, int scroll, int type, int done)
++{
+ if (sel.mode == SEL_IDLE)
+ return;
+ if (done && sel.mode == SEL_EMPTY) {
+_AT_@ -454,18 +466,22 @@
+ return;
+ }
+
+- oldey = sel.oe.y;
+- oldex = sel.oe.x;
+- oldsby = sel.nb.y;
+- oldsey = sel.ne.y;
+- oldtype = sel.type;
++ int const oldey = sel.oe.y;
++ int const oldex = sel.oe.x;
++ int const oldscroll = sel.oe.scroll;
++ int const oldsby = sel.nb.y;
++ int const oldsey = sel.ne.y;
++ int const oldtype = sel.type;
+
+ sel.oe.x = col;
+ sel.oe.y = row;
++ sel.oe.scroll = scroll;
++
+ selnormalize();
+ sel.type = type;
+
+- if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY)
++ if (oldey != sel.oe.y || oldex != sel.oe.x || oldscroll != sel.oe.scroll
++ || oldtype != sel.type || sel.mode == SEL_EMPTY)
+ tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
+
+ sel.mode = done ? SEL_IDLE : SEL_READY;
+_AT_@ -474,17 +490,21 @@
+ void
+ selnormalize(void)
+ {
+- int i;
+-
+- if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {
+- sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
+- sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
++ sel.nb.y = INTERVAL(sel.ob.y + term.scr - sel.ob.scroll, 0, term.bot);
++ sel.ne.y = INTERVAL(sel.oe.y + term.scr - sel.oe.scroll, 0, term.bot);
++ if (sel.type == SEL_REGULAR && sel.nb.y != sel.ne.y) {
++ sel.nb.x = sel.nb.y < sel.ne.y ? sel.ob.x : sel.oe.x;
++ sel.ne.x = sel.nb.y < sel.ne.y ? sel.oe.x : sel.ob.x;
+ } else {
+ sel.nb.x = MIN(sel.ob.x, sel.oe.x);
+ sel.ne.x = MAX(sel.ob.x, sel.oe.x);
+ }
+- sel.nb.y = MIN(sel.ob.y, sel.oe.y);
+- sel.ne.y = MAX(sel.ob.y, sel.oe.y);
++
++ if (sel.nb.y > sel.ne.y) {
++ int32_t const tmp = sel.nb.y;
++ sel.nb.y = sel.ne.y;
++ sel.ne.y = tmp;
++ }
+
+ selsnap(&sel.nb.x, &sel.nb.y, -1);
+ selsnap(&sel.ne.x, &sel.ne.y, +1);
+_AT_@ -492,7 +512,7 @@
+ /* expand selection over line breaks */
+ if (sel.type == SEL_RECTANGULAR)
+ return;
+- i = tlinelen(sel.nb.y);
++ int i = tlinelen(sel.nb.y);
+ if (i < sel.nb.x)
+ sel.nb.x = i;
+ if (tlinelen(sel.ne.y) <= sel.ne.x)
+_AT_@ -528,7 +548,7 @@
+ * Snap around if the word wraps around at the end or
+ * beginning of a line.
+ */
+- prevgp = &term.line[*y][*x];
++ prevgp = &TLINE(*y)[*x];
+ prevdelim = ISDELIM(prevgp->u);
+ for (;;) {
+ newx = *x + direction;
+_AT_@ -543,14 +563,14 @@
+ yt = *y, xt = *x;
+ else
+ yt = newy, xt = newx;
+- if (!(term.line[yt][xt].mode & ATTR_WRAP))
++ if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
+ break;
+ }
+
+ if (newx >= tlinelen(newy))
+ break;
+
+- gp = &term.line[newy][newx];
++ gp = &TLINE(newy)[newx];
+ delim = ISDELIM(gp->u);
+ if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
+ || (delim && gp->u != prevgp->u)))
+_AT_@ -571,14 +591,14 @@
+ *x = (direction < 0) ? 0 : term.col - 1;
+ if (direction < 0) {
+ for (; *y > 0; *y += direction) {
+- if (!(term.line[*y-1][term.col-1].mode
++ if (!(TLINE(*y-1)[term.col-1].mode
+ & ATTR_WRAP)) {
+ break;
+ }
+ }
+ } else if (direction > 0) {
+ for (; *y < term.row-1; *y += direction) {
+- if (!(term.line[*y][term.col-1].mode
++ if (!(TLINE(*y)[term.col-1].mode
+ & ATTR_WRAP)) {
+ break;
+ }
+_AT_@ -598,24 +618,32 @@
+ if (sel.ob.x == -1)
+ return NULL;
+
+- bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ;
++ int32_t syb = sel.ob.y - sel.ob.scroll + term.scr;
++ int32_t sye = sel.oe.y - sel.oe.scroll + term.scr;
++ if (syb > sye) {
++ int32_t tmp = sye;
++ sye = syb;
++ syb = tmp;
++ }
++
++ bufsize = (term.col+1) * (sye - syb + 1) * UTF_SIZ;
+ ptr = str = xmalloc(bufsize);
+
+ /* append every set & selected glyph to the selection */
+- for (y = sel.nb.y; y <= sel.ne.y; y++) {
++ for (y = syb; y <= sye; y++) {
+ if ((linelen = tlinelen(y)) == 0) {
+ *ptr++ = '
';
+ continue;
+ }
+
+ if (sel.type == SEL_RECTANGULAR) {
+- gp = &term.line[y][sel.nb.x];
++ gp = &TLINE(y)[sel.nb.x];
+ lastx = sel.ne.x;
+ } else {
+- gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
+- lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
++ gp = &TLINE(y)[syb == y ? sel.nb.x : 0];
++ lastx = (sye == y) ? sel.ne.x : term.col-1;
+ }
+- last = &term.line[y][MIN(lastx, linelen-1)];
++ last = &TLINE(y)[MIN(lastx, linelen-1)];
+ while (last >= gp && last->u == ' ')
+ --last;
+
+_AT_@ -850,6 +878,9 @@
+ ttywrite(const char *s, size_t n, int may_echo)
+ {
+ const char *next;
++ Arg arg = (Arg) { .i = term.scr };
++
++ kscrolldown(&arg);
+
+ if (may_echo && IS_SET(MODE_ECHO))
+ twrite(s, n, 1);
+_AT_@ -1061,13 +1092,53 @@
+ }
+
+ void
+-tscrolldown(int orig, int n)
++kscrolldown(const Arg* a)
++{
++ int n = a->i;
++
++ if (n < 0)
++ n = term.row + n;
++
++ if (n > term.scr)
++ n = term.scr;
++
++ if (term.scr > 0) {
++ term.scr -= n;
++ selscroll(0, -n);
++ tfulldirt();
++ }
++}
++
++void
++kscrollup(const Arg* a)
++{
++ int n = a->i;
++
++ if (n < 0)
++ n = term.row + n;
++
++ if (term.scr <= HISTSIZE-n) {
++ term.scr += n;
++ selscroll(0, n);
++ tfulldirt();
++ }
++}
++
++void
++tscrolldown(int orig, int n, int copyhist)
+ {
+ int i;
+ Line temp;
+
+ LIMIT(n, 0, term.bot-orig+1);
+
++ if (copyhist) {
++ term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE;
++ temp = term.hist[term.histi];
++ term.hist[term.histi] = term.line[term.bot];
++ term.line[term.bot] = temp;
++ }
++
+ tsetdirt(orig, term.bot-n);
+ tclearregion(0, term.bot-n+1, term.col-1, term.bot);
+
+_AT_@ -1081,13 +1152,23 @@
+ }
+
+ void
+-tscrollup(int orig, int n)
++tscrollup(int orig, int n, int copyhist)
+ {
+ int i;
+ Line temp;
+
+ LIMIT(n, 0, term.bot-orig+1);
+
++ if (copyhist) {
++ term.histi = (term.histi + 1) % HISTSIZE;
++ temp = term.hist[term.histi];
++ term.hist[term.histi] = term.line[orig];
++ term.line[orig] = temp;
++ }
++
++ if (term.scr > 0 && term.scr < HISTSIZE)
++ term.scr = MIN(term.scr + n, HISTSIZE-1);
++
+ tclearregion(0, orig, term.col-1, orig+n-1);
+ tsetdirt(orig+n, term.bot);
+
+_AT_@ -1109,6 +1190,7 @@
+ if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) {
+ selclear();
+ } else if (BETWEEN(sel.nb.y, orig, term.bot)) {
++ sel.oe.scroll = sel.ob.scroll = term.scr;
+ sel.ob.y += n;
+ sel.oe.y += n;
+ if (sel.ob.y < term.top || sel.ob.y > term.bot ||
+_AT_@ -1126,13 +1208,19 @@
+ int y = term.c.y;
+
+ if (y == term.bot) {
+- tscrollup(term.top, 1);
++ tscrollup(term.top, 1, 1);
+ } else {
+ y++;
+ }
+ tmoveto(first_col ? 0 : term.c.x, y);
+ }
+
++int
++currentLine(int x, int y)
++{
++ return (x == term.c.x || y == term.c.y);
++}
++
+ void
+ csiparse(void)
+ {
+_AT_@ -1185,6 +1273,8 @@
+ term.c.state &= ~CURSOR_WRAPNEXT;
+ term.c.x = LIMIT(x, 0, term.col-1);
+ term.c.y = LIMIT(y, miny, maxy);
++ // Set the last position in order to restore after normal mode exits.
++ onMove();
+ }
+
+ void
+_AT_@ -1291,14 +1381,14 @@
+ tinsertblankline(int n)
+ {
+ if (BETWEEN(term.c.y, term.top, term.bot))
+- tscrolldown(term.c.y, n);
++ tscrolldown(term.c.y, n, 0);
+ }
+
+ void
+ tdeleteline(int n)
+ {
+ if (BETWEEN(term.c.y, term.top, term.bot))
+- tscrollup(term.c.y, n);
++ tscrollup(term.c.y, n, 0);
+ }
+
+ int32_t
+_AT_@ -1735,11 +1825,11 @@
+ break;
+ case 'S': /* SU -- Scroll <n> line up */
+ DEFAULT(csiescseq.arg[0], 1);
+- tscrollup(term.top, csiescseq.arg[0]);
++ tscrollup(term.top, csiescseq.arg[0], 0);
+ break;
+ case 'T': /* SD -- Scroll <n> line down */
+ DEFAULT(csiescseq.arg[0], 1);
+- tscrolldown(term.top, csiescseq.arg[0]);
++ tscrolldown(term.top, csiescseq.arg[0], 0);
+ break;
+ case 'L': /* IL -- Insert <n> blank lines */
+ DEFAULT(csiescseq.arg[0], 1);
+_AT_@ -2246,7 +2336,7 @@
+ return 0;
+ case 'D': /* IND -- Linefeed */
+ if (term.c.y == term.bot) {
+- tscrollup(term.top, 1);
++ tscrollup(term.top, 1, 1);
+ } else {
+ tmoveto(term.c.x, term.c.y+1);
+ }
+_AT_@ -2259,7 +2349,7 @@
+ break;
+ case 'M': /* RI -- Reverse index */
+ if (term.c.y == term.top) {
+- tscrolldown(term.top, 1);
++ tscrolldown(term.top, 1, 1);
+ } else {
+ tmoveto(term.c.x, term.c.y-1);
+ }
+_AT_@ -2301,7 +2391,7 @@
+ {
+ char c[UTF_SIZ];
+ int control;
+- int width, len;
++ int width = 0, len;
+ Glyph *gp;
+
+ control = ISCONTROL(u);
+_AT_@ -2481,7 +2571,7 @@
+ void
+ tresize(int col, int row)
+ {
+- int i;
++ int i, j;
+ int minrow = MIN(row, term.row);
+ int mincol = MIN(col, term.col);
+ int *bp;
+_AT_@ -2518,6 +2608,14 @@
+ term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
+ term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
+
++ for (i = 0; i < HISTSIZE; i++) {
++ term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph));
++ for (j = mincol; j < col; j++) {
++ term.hist[i][j] = term.c.attr;
++ term.hist[i][j].u = ' ';
++ }
++ }
++
+ /* resize each row to new width, zero-pad if needed */
+ for (i = 0; i < minrow; i++) {
+ term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
+_AT_@ -2576,7 +2674,7 @@
+ continue;
+
+ term.dirty[y] = 0;
+- xdrawline(term.line[y], x1, y, x2);
++ xdrawline(TLINE(y), x1, y, x2);
+ }
+ }
+
+_AT_@ -2597,8 +2695,8 @@
+ cx--;
+
+ drawregion(0, 0, term.col, term.row);
+- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
+- term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
++ xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
++ term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
+ term.ocx = cx;
+ term.ocy = term.c.y;
+ xfinishdraw();
+diff -ruN st-default/st.c.rej st1/st.c.rej
+--- st-default/st.c.rej 1970-01-01 01:00:00.000000000 +0100
++++ st1/st.c.rej 2020-06-04 11:04:30.231111510 +0200
+_AT_@ -0,0 +1,73 @@
++--- st.c
+++++ st.c
++_AT_@ -91,51 +96,6 @@ enum escape_state {
++ ESC_DCS =128,
++ };
++
++-typedef struct {
++- Glyph attr; /* current char attributes */
++- int x;
++- int y;
++- char state;
++-} TCursor;
++-
++-typedef struct {
++- int mode;
++- int type;
++- int snap;
++- /*
++- * Selection variables:
++- * nb – normalized coordinates of the beginning of the selection
++- * ne – normalized coordinates of the end of the selection
++- * ob – original coordinates of the beginning of the selection
++- * oe – original coordinates of the end of the selection
++- */
++- struct {
++- int x, y;
++- } nb, ne, ob, oe;
++-
++- int alt;
++-} Selection;
++-
++-/* Internal representation of the screen */
++-typedef struct {
++- int row; /* nb row */
++- int col; /* nb col */
++- Line *line; /* screen */
++- Line *alt; /* alternate screen */
++- int *dirty; /* dirtyness of lines */
++- TCursor c; /* cursor */
++- int ocx; /* old cursor col */
++- int ocy; /* old cursor row */
++- int top; /* top scroll limit */
++- int bot; /* bottom scroll limit */
++- int mode; /* terminal mode flags */
++- int esc; /* escape state flags */
++- char trantbl[4]; /* charset table translation */
++- int charset; /* current charset */
++- int icharset; /* selected charset for sequence */
++- int *tabs;
++-} Term;
++-
++ /* CSI Escape sequence structs */
++ /* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */
++ typedef struct {
++_AT_@ -1174,6 +1210,7 @@ selscroll(int orig, int n)
++ return;
++
++ if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.bot)) {
+++ sel.oe.scroll = sel.ob.scroll = term.scr;
++ if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) {
++ selclear();
++ return;
++_AT_@ -2681,8 +2734,8 @@ draw(void)
++ cx--;
++
++ drawregion(0, 0, term.col, term.row);
++- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
++- term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
+++ xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
+++ term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
++ term.ocx = cx, term.ocy = term.c.y;
++ xfinishdraw();
++ xximspot(term.ocx, term.ocy);
+diff -ruN st-default/st.h st1/st.h
+--- st-default/st.h 2020-06-04 11:15:55.165135902 +0200
++++ st1/st.h 2020-06-04 11:04:30.232111510 +0200
+_AT_@ -1,5 +1,8 @@
+ /* See LICENSE for license details. */
+
++#include "glyph.h"
++#include "normalMode.h"
++
+ #include <stdint.h>
+ #include <sys/types.h>
+
+_AT_@ -33,6 +36,8 @@
+ ATTR_WRAP = 1 << 8,
+ ATTR_WIDE = 1 << 9,
+ ATTR_WDUMMY = 1 << 10,
++ ATTR_HIGHLIGHT = 1 << 12,
++ ATTR_CURRENT = 1 << 13,
+ ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
+ };
+
+_AT_@ -42,11 +47,6 @@
+ SEL_READY = 2
+ };
+
+-enum selection_type {
+- SEL_REGULAR = 1,
+- SEL_RECTANGULAR = 2
+-};
+-
+ enum selection_snap {
+ SNAP_WORD = 1,
+ SNAP_LINE = 2
+_AT_@ -57,18 +57,6 @@
+ typedef unsigned long ulong;
+ typedef unsigned short ushort;
+
+-typedef uint_least32_t Rune;
+-
+-#define Glyph Glyph_
+-typedef struct {
+- Rune u; /* character code */
+- ushort mode; /* attribute flags */
+- uint32_t fg; /* foreground */
+- uint32_t bg; /* background */
+-} Glyph;
+-
+-typedef Glyph *Line;
+-
+ typedef union {
+ int i;
+ uint ui;
+_AT_@ -81,6 +69,11 @@
+ void redraw(void);
+ void draw(void);
+
++int currentLine(int, int);
++void kscrolldown(const Arg *);
++void kscrollup(const Arg *);
++void normalMode(Arg const *);
++
+ void printscreen(const Arg *);
+ void printsel(const Arg *);
+ void sendbreak(const Arg *);
+_AT_@ -90,6 +83,9 @@
+ void tnew(int, int);
+ void tresize(int, int);
+ void tsetdirtattr(int);
++size_t utf8decode(const char *, Rune *, size_t);
++Rune utf8decodebyte(char, size_t *);
++void tsetdirt(int, int);
+ void ttyhangup(void);
+ int ttynew(char *, char *, char *, char **);
+ size_t ttyread(void);
+_AT_@ -100,8 +96,10 @@
+
+ void selclear(void);
+ void selinit(void);
+-void selstart(int, int, int);
+-void selextend(int, int, int, int);
++void selstart(int, int, int, int);
++void xselstart(int, int, int);
++void selextend(int, int, int, int, int);
++void xselextend(int, int, int, int);
+ int selected(int, int);
+ char *getsel(void);
+
+Binary files st-default/st.o and st1/st.o differ
+diff -ruN st-default/term.h st1/term.h
+--- st-default/term.h 1970-01-01 01:00:00.000000000 +0100
++++ st1/term.h 2020-06-04 11:04:30.232111510 +0200
+_AT_@ -0,0 +1,74 @@
++#ifndef TERM_H
++#define TERM_H
++
++//
++// Internal terminal structs.
++//
++
++#include "glyph.h"
++
++#include <stdint.h>
++
++#define HISTSIZE 2500
++
++typedef struct {
++ Glyph attr; /* current char attributes */
++ int x;
++ int y;
++ char state;
++} TCursor;
++
++typedef struct {
++ int mode;
++ int type;
++ int snap;
++ /// Selection variables:
++ /// ob – original coordinates of the beginning of the selection
++ /// oe – original coordinates of the end of the selection
++ struct {
++ int x, y, scroll;
++ } ob, oe;
++ /// Selection variables; currently displayed chunk.
++ /// nb – normalized coordinates of the beginning of the selection
++ /// ne – normalized coordinates of the end of the selection
++ struct {
++ int x, y;
++ } nb, ne;
++
++ int alt;
++} Selection;
++
++/* Internal representation of the screen */
++typedef struct {
++ int row; /* nb row */
++ int col; /* nb col */
++ Line *line; /* screen */
++ Line *alt; /* alternate screen */
++ Line hist[HISTSIZE]; /* history buffer */
++ int histi; /* history index */
++ int scr; /* scroll back */
++ int *dirty; /* dirtyness of lines */
++ TCursor c; /* cursor */
++ int ocx; /* old cursor col */
++ int ocy; /* old cursor row */
++ int top; /* top scroll limit */
++ int bot; /* bottom scroll limit */
++ int mode; /* terminal mode flags */
++ int esc; /* escape state flags */
++ char trantbl[4]; /* charset table translation */
++ int charset; /* current charset */
++ int icharset; /* selected charset for sequence */
++ int *tabs;
++ Rune lastc;
++} Term;
++
++extern Term term;
++
++#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \
++ term.scr + HISTSIZE + 1) % HISTSIZE] : \
++ term.line[(y) - term.scr])
++
++extern Selection sel;
++
++
++#endif // TERM_H
+diff -ruN st-default/win.h st1/win.h
+--- st-default/win.h 2020-06-04 11:15:55.166135902 +0200
++++ st1/win.h 2020-06-04 11:04:30.232111510 +0200
+_AT_@ -19,6 +19,7 @@
+ MODE_MOUSEMANY = 1 << 15,
+ MODE_BRCKTPASTE = 1 << 16,
+ MODE_NUMLOCK = 1 << 17,
++ MODE_NORMAL = 1 << 18,
+ MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
+ |MODE_MOUSEMANY,
+ };
+_AT_@ -27,6 +28,7 @@
+ void xclipcopy(void);
+ void xdrawcursor(int, int, Glyph, int, int, Glyph);
+ void xdrawline(Line, int, int, int);
++void xdrawglyph(Glyph, int, int);
+ void xfinishdraw(void);
+ void xloadcols(void);
+ int xsetcolorname(int, const char *);
+diff -ruN st-default/x.c st1/x.c
+--- st-default/x.c 2020-06-04 11:15:55.166135902 +0200
++++ st1/x.c 2020-06-04 11:04:30.232111510 +0200
+_AT_@ -143,7 +143,6 @@
+ static inline ushort sixd_to_16bit(int);
+ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
+ static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
+-static void xdrawglyph(Glyph, int, int);
+ static void xclear(int, int, int, int);
+ static int xgeommasktogravity(int);
+ static int ximopen(Display *);
+_AT_@ -356,7 +355,7 @@
+ break;
+ }
+ }
+- selextend(evcol(e), evrow(e), seltype, done);
++ xselextend(evcol(e), evrow(e), seltype, done);
+ if (done)
+ setsel(getsel(), e->xbutton.time);
+ }
+_AT_@ -486,7 +485,7 @@
+ xsel.tclick2 = xsel.tclick1;
+ xsel.tclick1 = now;
+
+- selstart(evcol(e), evrow(e), snap);
++ xselstart(evcol(e), evrow(e), snap);
+ }
+ }
+
+_AT_@ -773,6 +772,13 @@
+ }
+
+ void
++normalMode(Arg const *_) {
++ (void) _;
++ win.mode ^= MODE_NORMAL;
++}
++
++
++void
+ xloadcols(void)
+ {
+ int i;
+_AT_@ -1358,6 +1364,14 @@
+ base.fg = defaultattr;
+ }
+
++ if (base.mode & ATTR_HIGHLIGHT) {
++ base.bg = highlightBg;
++ base.fg = highlightFg;
++ } else if ((base.mode & ATTR_CURRENT) && (win.mode & MODE_NORMAL)) {
++ base.bg = currentBg;
++ base.fg = currentFg;
++ }
++
+ if (IS_TRUECOL(base.fg)) {
+ colfg.alpha = 0xffff;
+ colfg.red = TRUERED(base.fg);
+_AT_@ -1447,7 +1461,7 @@
+ xclear(winx, winy + win.ch, winx + width, win.h);
+
+ /* Clean up the region we want to draw to. */
+- XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
++ XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
+
+ /* Set the clip region because Xft is sometimes dirty. */
+ r.x = 0;
+_AT_@ -1490,8 +1504,9 @@
+ Color drawcol;
+
+ /* remove the old cursor */
+- if (selected(ox, oy))
+- og.mode ^= ATTR_REVERSE;
++ if (selected(ox, oy)) og.mode ^= ATTR_REVERSE;
++ if (highlighted(ox, oy)) { og.mode ^= ATTR_HIGHLIGHT; }
++ if (currentLine(ox, oy)) { og.mode ^= ATTR_CURRENT; }
+ xdrawglyph(og, ox, oy);
+
+ if (IS_SET(MODE_HIDE))
+_AT_@ -1523,6 +1538,11 @@
+ drawcol = dc.col[g.bg];
+ }
+
++ if ((g.mode & ATTR_CURRENT) && (win.mode & MODE_NORMAL)) {
++ g.bg = currentBg;
++ g.fg = currentFg;
++ }
++
+ /* draw the new one */
+ if (IS_SET(MODE_FOCUSED)) {
+ switch (win.cursor) {
+_AT_@ -1607,12 +1627,18 @@
+
+ numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
+ i = ox = 0;
+- for (x = x1; x < x2 && i < numspecs; x++) {
++ for (x = x1; x < x2 && i < numspecs; ++x) {
+ new = line[x];
+ if (new.mode == ATTR_WDUMMY)
+ continue;
+ if (selected(x, y1))
+ new.mode ^= ATTR_REVERSE;
++ if (highlighted(x, y1)) {
++ new.mode ^= ATTR_HIGHLIGHT;
++ }
++ if (currentLine(x, y1)) {
++ new.mode ^= ATTR_CURRENT;
++ }
+ if (i > 0 && ATTRCMP(base, new)) {
+ xdrawglyphfontspecs(specs, base, i, ox, y1);
+ specs += i;
+_AT_@ -1800,6 +1826,14 @@
+ len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status);
+ else
+ len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
++
++ if (IS_SET(MODE_NORMAL)) {
++ ExitState const es = kpressNormalMode(buf, len, // strlen(buf),
++ match(ControlMask, e->state),
++ &ksym);
++ if (es == finished) { normalMode(NULL); }
++ return;
++ }
+ /* 1. shortcuts */
+ for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
+ if (ksym == bp->keysym && match(bp->mod, e->state)) {
+Binary files st-default/x.o and st1/x.o differ
Received on Thu Jun 04 2020 - 11:29:03 CEST

This archive was generated by hypermail 2.3.0 : Thu Jun 04 2020 - 11:36:46 CEST