Introduction
Introduction Statistics Contact Development Disclaimer Help
st-vimBrowse-20200212-26cdfeb.diff - sites - public wiki contents of suckless.o…
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 <[email protected]>
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 er…
49 +x.o: arg.h config.h st.h win.h dynamicArray.h normalMode.h term.h glyph…
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…
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 (sear…
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 …
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 i…
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 argumen…
91 + { AltMask, XK_c, normalMode, {.i = …
92 { XK_ANY_MOD, XK_Break, sendbreak, {.i = …
93 { ControlMask, XK_Print, toggleprinter, {.i = …
94 { ShiftMask, XK_Print, printscreen, {.i = …
95 @@ -186,6 +196,8 @@ static Shortcut shortcuts[] = {
96 { TERMMOD, XK_Y, selpaste, {.i = …
97 { ShiftMask, XK_Insert, selpaste, {.i = …
98 { TERMMOD, XK_Num_Lock, numlock, {.i = …
99 + { ShiftMask, XK_Page_Up, kscrollup, {.i = -…
100 + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -…
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…
114 +
115 +/// Shortcusts executed in normal mode (which should not already be in …
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) / …
129 +
130 +/// Style of the command string visualized in normal mode in the right …
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 c…
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 operatio…
138 +/// empathise the current position on screen in the status area [Vim …
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…
162 + */
163 +static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohi…
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 bli…
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 disa…
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« v…
227 + * the st.info and appropriately install the st.info in the environment…
228 + * you use this st version.
229 + *
230 + * it#$tabspaces,
231 + *
232 + * Secondly make sure your kernel is not expanding tabs. When running `…
233 + * -a` »tab0« should appear. You can tell the terminal to not expand …
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 fon…
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 (sear…
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 …
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 i…
318 + * Note that if you want to use ShiftMask with selmasks, set this to an…
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 …
329 + { XK_ANY_MOD, Button2, selpaste, {.i = 0}, …
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 argumen…
341 + { AltMask, XK_c, normalMode, {.i = …
342 + { XK_ANY_MOD, XK_Break, sendbreak, {.i = …
343 + { ControlMask, XK_Print, toggleprinter, {.i = …
344 + { ShiftMask, XK_Print, printscreen, {.i = …
345 + { XK_ANY_MOD, XK_Print, printsel, {.i = …
346 + { TERMMOD, XK_Prior, zoom, {.f = +…
347 + { TERMMOD, XK_Next, zoom, {.f = -…
348 + { TERMMOD, XK_Home, zoomreset, {.f = …
349 + { TERMMOD, XK_C, clipcopy, {.i = …
350 + { TERMMOD, XK_V, clippaste, {.i = …
351 + { TERMMOD, XK_Y, selpaste, {.i = …
352 + { ShiftMask, XK_Insert, selpaste, {.i = …
353 + { TERMMOD, XK_Num_Lock, numlock, {.i = …
354 + { ShiftMask, XK_Page_Up, kscrollup, {.i = -…
355 + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -…
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 Li…
393 + * world. Please decide about changes wisely.
394 + */
395 +static Key key[] = {
396 + /* keysym mask string appkey appcurso…
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…
633 +
634 +/// Shortcusts executed in normal mode (which should not already be in …
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) / …
648 +
649 +/// Style of the command string visualized in normal mode in the right …
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 c…
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 operatio…
657 +/// empathise the current position on screen in the status area [Vim …
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 …
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 initial…
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 displa…
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 checkin…
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->itemSi…
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), retu…
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…
743 + ENSURE(s->index%s->itemSize == 0 && "(index not aligned)", retu…
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…
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->itemSiz…
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 i…
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->itemSi…
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->al…
803 + uint32_t const newAlloSize = s->allocated + (diff > EXP…
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 …
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->alloc…
833 + uint32_t const newAlloSize = s->allocated + (diff > EXP…
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)->itemSiz…
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…
862 +// to adapt the condition in order to work properly.
863 +// Attention: Be sure to perform a clean build after you alter preproce…
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 defin…
871 +/// to report errors. In debug production mode, alias st to st 2> error…
872 +static void reportError(char const * cond, char const * stt, char const…
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.…
879 + }
880 + if (errorCount < maxErrorCount) {
881 + printf("Bug:\tCondition '%s' evaluates to false.\n\tPer…
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. Fr…
886 + "on, no additional errors will be repor…
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 fla…
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__, __…
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, Gyl…
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), tru…
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, fgPo…
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 = …
999 + Position startPosition;
1000 + enum Infix { infix_none = 0, infix_i = 1, infix_a = 2, …
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}, tr…
1013 +};
1014 +NormalModeState stateVB = {
1015 + {0,0,0}, {noop, {0, 0, 0}, false}, {0, none, {0, 0, 0}, tr…
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 %…
1037 +static inline bool contains (char c, char const * values, uint32_t memS…
1038 + ENSURE(values != NULL, return false);
1039 + for (uint32_t i = 0; i < memSize; ++i) if (c == values[i]) retu…
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 pa…
1053 +static bool moveLine(int32_t const amount) {
1054 + int32_t const reqShift = intervalDiff(term.c.y+=amount, 0, term…
1055 + term.c.y -= reqShift;
1056 + int32_t const sDiff = intervalDiff(term.scr-=reqShift, 0, HISTS…
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.…
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 t…
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 p…
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 …
1087 + // repsonsibility for printing back to [st].
1088 + if (term.col < maxFractionOverridden) { …
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); …
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 …
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->…
1104 + for (uint32_t chr = 0; chr < overrideSize - botSz; ++chr) {
1105 + line[chr] = *g;
1106 + line[chr].u = *((Rune*) (str->content+(offset+=str->ite…
1107 + }
1108 + if (prePos) {
1109 + ENSURE(term.scr < HISTSIZE, term.scr = HISTSIZE - 1);
1110 + int32_t const p=round((HISTSIZE-1-term.scr)*100./(HISTS…
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-bot…
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 ha…
1127 + xdrawline(line-overrideStart, overrideStart, yPos, overrideEnd …
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; …
1136 + case visual: g.fg=fgCommandVisual; g.bg=bgCommandVisual…
1137 + case visualLine: g.fg=fgCommandVisualLine;
1138 + g.bg=bgCommandVisualLine;
1139 + }
1140 + displayString(isEmpty(currentCommand) ? lastCommand : currentCo…
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.finis…
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…
1156 +}
1157 +
1158 +/// Register that the current comamnd is finished and a new command is …
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 rele…
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…
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 + searc…
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), , fa…
1217 + uint32_t const searchChar = *((uint32_t*) next);
1218 +
1219 + if (TLINE(term.c.y)[term.c.x].u == searchChar) { ++find…
1220 + else { findIdx = 0; }
1221 + }
1222 + bool const found = findIdx == searchStrSize;
1223 + for (uint32_t i = 0; found && i < searchStrSize; ++i) moveLette…
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, …
1247 + && append(&highlights, …
1248 + findIdx = 0; //term.dirty[yStar…
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 direc…
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 'statu…
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, fals…
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. yi…
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 succes…
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 er…
1301 + char const lower = tolower(c);
1302 + // Motions
1303 + if (lower == 'w') {
1304 + // translated into wb[command]e resp. WB[command]E, whi…
1305 + // file even when at the fist letter. Does not work for…
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') + …
1310 + s = s && checkSetNextV(cmd, operation);
1311 + s = s && checkSetNextV(cmd, (signed char)(((int)'e')+ d…
1312 + return s;
1313 + }
1314 + // Symmetrical brackets (quotation marks)
1315 + if (c == '\'' || c == '"') {
1316 + // Local ambiguity -> do nothing. It cannot be determin…
1317 + // the current char is the 1st or last char of the sele…
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 toge…
1337 + for (size_t pid = 0; pid < sizeof(Brackets); ++pid) {
1338 + if(Brackets[pid].first == c || Brackets[pid].second == …
1339 + if (TLINE(term.c.y)[term.c.x].u!=Brackets[pid].…
1340 + s = s && checkSetNextV(cmd, '?');
1341 + s = s && checkSetNextV(cmd, Brackets[pi…
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].secon…
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] </…
1361 +
1362 + // 1. Copy history ( tag := hist[?<\n:/ \n] )
1363 + // 2. Copy history ( first_find := hist[?<\n: next plac…
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 co…
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…
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 ope…
1417 + // Typing 'i' if no operation is currently performed behaves li…
1418 + if (esc || enter || (len == 1 && cs[0] == 'i' && isMotionFinish…
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.f…
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(&searchStrin…
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 !…
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, ap…
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*)(kComm…
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,ter…
1518 + goto finish;
1519 + case yank: //< Complete yank [y#amount …
1520 + selstart(0, term.c.y, term.scr,…
1521 + int const origY = term.c.y;
1522 + moveLine(max(stateVB.motion.amo…
1523 + selextend(term.col-1,term.c.y,t…
1524 + SEL_RECTANGULAR…
1525 + term.c.y = origY;
1526 + FALLTHROUGH
1527 + case visualLine: // Yank visual selecti…
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…
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 'back…
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-…
1555 + term.c.y = 0;
1556 + goto finish;
1557 + case XK_b:
1558 + term.scr = min(term.scr + max(term.row …
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, HIS…
1564 + goto finish;
1565 + case XK_d:
1566 + term.scr = max(term.scr - term.row / 2,…
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 : wordDeli…
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 = startSpaceIsSeparato…
1607 + // Max iteration := One complete hist traversal.
1608 + uint32_t const maxIter = (HISTSIZE+term.row) * …
1609 + // Doesn't work exactly as in vim: Linebreak is
1610 + // counted as 'normal' separator, hence a jump …
1611 + // span multiple lines here.
1612 + stateVB.motion.amount = max(stateVB.motion.amou…
1613 + for (;stateVB.motion.amount>0;--stateVB.motion.…
1614 + uint8_t state = 0;
1615 + if (performOffset) { moveLetter(sign); }
1616 + for (uint32_t cIt = 0; cIt ++ < maxIter…
1617 + if (startSpaceIsSeparator == co…
1618 + if (state == 1) {
1619 + if (performOffs…
1620 + moveLet…
1621 + }
1622 + break;
1623 + }
1624 + } else if (state == 0) { state …
1625 + }
1626 + }
1627 + goto motionFinish;
1628 + }
1629 + case '/': sign = 1; FALLTHROUGH
1630 + case '?':
1631 + empty(&searchString);
1632 + stateVB.motion.search = sign == 1 ? forward :…
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 faile…
1641 + if (stateVB.motion.search == backward) { sign *…
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.sc…
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].v…
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 cro…
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 info…
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 ar…
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 ctrlPres…
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 select…
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.ty…
1944 + if (oldey != sel.oe.y || oldex != sel.oe.x || oldscroll != sel.…
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, ter…
1959 + sel.ne.y = INTERVAL(sel.oe.y + term.scr - sel.oe.scroll, 0, ter…
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_WRA…
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 != prev…
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…
2163 + sel.oe.scroll = sel.ob.scroll = term.scr;
2164 if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < ter…
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 …
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(Glyp…
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(Glyp…
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.oc…
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 selecti…
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 selec…
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, …
2493 static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, i…
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…
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_NORMA…
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,…
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 o…
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 …
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, y…
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, &…
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, // strl…
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
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.