Introduction
Introduction Statistics Contact Development Disclaimer Help
st-vim-0.8.3.patch - sites - public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log
Files
Refs
---
st-vim-0.8.3.patch (39609B)
---
1 From 65989bfc0ee4566318ae0a123ee3c40a686c20ad Mon Sep 17 00:00:00 2001
2 From: Julius Huelsmann <[email protected]>
3 Date: Sun, 25 Apr 2021 23:06:37 +0200
4 Subject: [PATCH] meta-patch: repaint
5
6 ---
7 Makefile | 2 +-
8 config.def.h | 16 +++
9 normalMode.c | 284 +++++++++++++++++++++++++++++++++++++++++
10 normalMode.h | 8 ++
11 st.c | 349 +++++++++++++++++++++++++++------------------------
12 st.h | 2 +
13 utils.h | 23 ++++
14 win.h | 1 +
15 x.c | 51 ++++++--
16 9 files changed, 558 insertions(+), 178 deletions(-)
17 create mode 100644 normalMode.c
18 create mode 100644 normalMode.h
19 create mode 100644 utils.h
20
21 diff --git a/Makefile b/Makefile
22 index 470ac86..6688a58 100644
23 --- a/Makefile
24 +++ b/Makefile
25 @@ -21,7 +21,7 @@ config.h:
26 .c.o:
27 $(CC) $(STCFLAGS) -c $<
28
29 -st.o: config.h st.h win.h
30 +st.o: config.h st.h win.h normalMode.h normalMode.c utils.h
31 x.o: arg.h config.h st.h win.h
32
33 $(OBJ): config.h config.mk
34 diff --git a/config.def.h b/config.def.h
35 index 0895a1f..b33cef9 100644
36 --- a/config.def.h
37 +++ b/config.def.h
38 @@ -122,6 +122,21 @@ unsigned int defaultfg = 7;
39 unsigned int defaultbg = 0;
40 static unsigned int defaultcs = 256;
41 static unsigned int defaultrcs = 257;
42 +unsigned int const currentBg = 6, buffSize = 2048;
43 +/// Enable double / triple click yanking / selection of word / line.
44 +int const mouseYank = 1, mouseSelect = 0;
45 +/// [Vim Browse] Colors for search results currently on screen.
46 +unsigned int const highlightBg = 160, highlightFg = 15;
47 +char const wDelS[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", wDelL[] = " \…
48 +char *nmKeys [] = { ///< Shortcusts executed in normal mode
49 + "R/Building\nN", "r/Building\n", "X/juli@machine\nN", "x/juli@machine…
50 + "Q?[Leaving vim, starting execution]\n","F/: error:\nN", "f/: error:\…
51 +};
52 +unsigned int const amountNmKeys = sizeof(nmKeys) / sizeof(*nmKeys);
53 +/// Style of the {command, search} string shown in the right corner (y,…
54 +Glyph styleSearch = {' ', ATTR_ITALIC | ATTR_BOLD_FAINT, 7, 16};
55 +Glyph style[] = {{' ',ATTR_ITALIC|ATTR_FAINT,15,16}, {' ',ATTR_ITALIC,2…
56 + {' ', ATTR_ITALIC, 232, 4}, {' ', ATTR_ITALIC, 232, 12…
57
58 /*
59 * Default shape of cursor
60 @@ -188,6 +203,7 @@ static Shortcut shortcuts[] = {
61 { TERMMOD, XK_Y, selpaste, {.i = …
62 { ShiftMask, XK_Insert, selpaste, {.i = …
63 { TERMMOD, XK_Num_Lock, numlock, {.i = …
64 + { MODKEY, XK_c, normalMode, {.i = …
65 };
66
67 /*
68 diff --git a/normalMode.c b/normalMode.c
69 new file mode 100644
70 index 0000000..f0c7e30
71 --- /dev/null
72 +++ b/normalMode.c
73 @@ -0,0 +1,284 @@
74 +#include <X11/keysym.h>
75 +#include <X11/XKBlib.h>
76 +
77 +#include "normalMode.h"
78 +#include "utils.h"
79 +
80 +extern Glyph const styleSearch, style[];
81 +extern char const wDelS[], wDelL[], *nmKeys[];
82 +extern unsigned int bg[], fg, currentBg, highlightBg, highlightFg, amou…
83 +
84 +typedef struct { int p[3]; } Pos;
85 +
86 +typedef enum {visual='v', visualLine='V', yank = 'y'} Op;
87 +typedef enum {infix_none=0, infix_i='i', infix_a='a'} Infix;
88 +typedef enum {fw='/', bw='?'} Search;
89 +struct NormalModeState {
90 + struct OperationState { Op op; Infix infix; } cmd;
91 + struct MotionState { uint32_t c; int active; Pos searchPos; Sea…
92 +} defaultNormalMode, state;
93 +
94 +DynamicArray searchStr=UTF8_ARRAY, cCmd=UTF8_ARRAY, lCmd=UTF8_ARRAY;
95 +Glyph styleCmd;
96 +char posBuffer[10], braces[6][3] = { {"()"}, {"<>"}, {"{}"}, {"[]"}, {"…
97 +int exited=1, overlay=1;
98 +static inline Rune cChar() { return term.line[term.c.y][term.c.x].u; }
99 +static inline int pos(int p, int h) {return IS_SET(MODE_ALTSCREEN)?p:ra…
100 +static inline int contains(Rune l, char const * values, size_t const me…
101 + for (uint32_t i = 0; i < memSize; ++i) if (l == values[i]) retu…
102 + return 0;
103 +}
104 +static inline void decodeTo(char const *cs, size_t len, DynamicArray *a…
105 + char *var = expand(arr);
106 + if (!var) empty(arr); else utf8decode(cs, (Rune*)(var), len);
107 +}
108 +static inline void applyPos(Pos p) {
109 + term.c.x = p.p[0], term.c.y = p.p[1];
110 + if (!IS_SET(MODE_ALTSCREEN) && histOp) term.line = &buf[histOff…
111 +}
112 +/// Find string in history buffer, and provide string-match-lookup for …
113 +static int highlighted(int x, int y) {
114 + int const s=term.row*term.col, i=y*term.col+x, sz=size(&searchS…
115 + return sz && i<s && mark[i]!=sz && i+mark[i]<s && !mark[i+mark[…
116 +}
117 +static void markSearchMatches(int all) {
118 + int sz = size(&searchStr), ox = 0, oy = 0, oi=0;
119 + for (int y=0; sz && all && y<term.row; ++y)
120 + for (int x=0; x<term.col; ++x) term.dirty[y] |= highlig…
121 + for (int y = 0, wi=0, owi=0, i=0; sz && y < term.row; ++y)
122 + for (int x=0; x<term.col; ++x, wi%=sz, ++i, owi=wi)
123 + if (all || term.dirty[y]) {
124 + mark[i]=sz-(wi=(getU32(&searchStr,wi,1)…
125 + if (wi==1) ox=x, oy=y, oi=i; else if (!…
126 + }
127 + for (int y=0; sz &&all &&y<term.row; ++y)
128 + for (int x=0; x<term.col; ++x) term.dirty[y] |= highlig…
129 +}
130 +static int findString(int s, int all) {
131 + Pos p = (Pos) {.p={term.c.x, term.c.y, IS_SET(MODE_ALTSCREEN) ?…
132 + historyMove(s, 0, 0);
133 + uint32_t strSz=size(&searchStr), maxIter=rows()*term.col+strSz,…
134 + for (uint32_t i=0, wi = 0; wIdx<strSz && ++i<=maxIter; historyM…
135 + wIdx = (getU32(&searchStr, wIdx, s>0)==cChar())?wIdx+1:…
136 + if (wi && !wIdx) historyMove(-(int)(s*wi), 0, 0);
137 + }
138 + if (wIdx == strSz && wIdx) historyMove(-(int)(s*strSz), 0, 0);
139 + else applyPos(p);
140 + markSearchMatches(all);
141 + return wIdx == strSz;
142 +}
143 +/// Execute series of normal-mode commands from char array / decoded fr…
144 +ExitState pressKeys(char const* s, size_t e) {
145 + ExitState x=success;
146 + for (size_t i=0; i<e && (x=(!s[i] ? x : kPressHist(&s[i], 1, 0,…
147 + return x;
148 +}
149 +static ExitState executeCommand(uint32_t *cs, size_t z) {
150 + ExitState x=success;
151 + char dc [32];
152 + for (size_t i=0; i<z && (x=kPressHist(dc, utf8encode(cs[i],dc),…
153 + return x;
154 +}
155 +/// Get character for overlay, if the overlay (st) has something to sho…
156 +static void getChar(DynamicArray *st, Glyph *glyphChange, int y, int xE…
157 + if (x < xEnd - min(min(width,xEnd), size(st))) *glyphChange = t…
158 + else if (x<xEnd) glyphChange->u = *((Rune*)(st->content + (size…
159 +}
160 +/// Expand "infix" expression: for instance (w =>) l b | …
161 +static ExitState expandExpression(char l) { // ({ =>) l ? { …
162 + int a=state.cmd.infix==infix_a, yank=state.cmd.op=='y', lc=tolo…
163 + state.cmd.infix = infix_none;
164 + if(!yank && state.cmd.op!=visual && state.cmd.op!=visualLine) r…
165 + char mot[11] = {'l', 0, 'b', 0, 0, 'v', 0, 'e', 0, 0, (char)(ya…
166 + if (lc == 'w') mot[2] = (char) ('b' - lc + l), mot[7] = (char) …
167 + else {
168 + mot[1]='?', mot[3]=mot[8]='\n', mot[6]='/', mot[4]=(cha…
169 + for (int i=found=0; !found && i < 6; ++i)
170 + if ((found=contains(l,braces[i],2))) mot[2]=bra…
171 + }
172 + if (!found) return failed;
173 + assign(&lCmd, &cCmd);
174 + empty(&cCmd);
175 + state.cmd = defaultNormalMode.cmd;
176 + return pressKeys(mot, 11);
177 +}
178 +
179 +ExitState executeMotion(char const cs, KeySym const *const ks) {
180 + state.m.c = state.m.c < 1u ? 1u : state.m.c;
181 + if (ks && *ks == XK_d) historyMove(0, 0, term.row / 2);
182 + else if (ks && *ks == XK_u) historyMove(0, 0, -term.row / 2);
183 + else if (ks && *ks == XK_f) historyMove(0, 0, term.row-1+(term.…
184 + else if (ks && *ks == XK_b) historyMove(0, 0, -(term.c.y=term.r…
185 + else if (ks && *ks == XK_h) overlay = !overlay;
186 + else if (cs == 'K') historyMove(0, 0, -(int)state.m.c);
187 + else if (cs == 'J') historyMove(0, 0, (int)state.m.c);
188 + else if (cs == 'k') historyMove(0, -(int)state.m.c, 0);
189 + else if (cs == 'j') historyMove(0, (int)state.m.c, 0);
190 + else if (cs == 'h') historyMove(-(int)state.m.c, 0, 0);
191 + else if (cs == 'l') historyMove( (int)state.m.c, 0, 0);
192 + else if (cs == 'H') term.c.y = 0;
193 + else if (cs == 'M') term.c.y = term.bot / 2;
194 + else if (cs == 'L') term.c.y = term.bot;
195 + else if (cs == 's' || cs == 'S') altToggle = cs == 's' ? !altTo…
196 + else if (cs == 'G' || cs == 'g') {
197 + if (cs == 'G') term.c = c[0] = c[IS_SET(MODE_ALTSCREEN)…
198 + if (!IS_SET(MODE_ALTSCREEN)) term.line = &buf[histOff=i…
199 + } else if (cs == '0') term.c.x = 0;
200 + else if (cs == '$') term.c.x = term.col-1;
201 + else if (cs == 't') sel.type = sel.type==SEL_REGULAR ? SEL_RECT…
202 + else if (cs == 'n' || cs == 'N') {
203 + int const d = ((cs=='N')!=(state.m.search==bw))?-1:1;
204 + for (uint32_t i = state.m.c; i && findString(d, 0); --i…
205 + } else if (contains(cs, "wWeEbB", 6)) {
206 + int const low=cs<=90, off=tolower(cs)!='w', sgn=(tolowe…
207 + size_t const l=strlen(wDelL), s=strlen(wDelS), maxIt=ro…
208 + for (int it=0, on=0; state.m.c > 0 && it < maxIt; ++it)…
209 + // If an offset is to be performed in beginning or …
210 + if ((off || it) && historyMove(sgn, 0, 0)) brea…
211 + // Determine if the category of the current let…
212 + int n = 1<<(contains(cChar(),wDelS,s) ?(2-low) …
213 + found = (on|=n)^n && ((off ?on^n :n)!=1);
214 + // If a reverse offset is to be performed and t…
215 + if (found && off) historyMove(-sgn, 0, 0);
216 + // Terminate iteration: reset #it and old n val…
217 + if (found) it=-1, on=0, --state.m.c;
218 + }
219 + } else return failed;
220 + state.m.c = 0;
221 + return state.cmd.op == yank ? exitMotion : success;
222 +}
223 +
224 +ExitState kPressHist(char const *cs, size_t len, int ctrl, KeySym const…
225 + historyOpToggle(1, 1);
226 + int const prevYOff=IS_SET(MODE_ALTSCREEN)?0:histOff, search=sta…
227 + prevAltToggle=altToggle, prevOverlay=overlay;
228 + int const noOp=!state.cmd.op&&!state.cmd.infix, num=len==1&&BET…
229 + esc=kSym&&*kSym==XK_Escape, ret=(kSym&&*kSym==XK_Retu…
230 + quantifier=num&&(cs[0]!='0'||state.m.c), ins=!search …
231 + exited = 0;
232 + ExitState result = success;
233 + if (esc || ret || ins) { result = exitMotion, len = 0;
234 + } else if (kSym && *kSym == XK_BackSpace) {
235 + if ((search || state.m.c) && size(&cCmd)) pop(&cCmd);
236 + if (search) {
237 + if (size(&searchStr)) pop(&searchStr);
238 + else result = exitMotion;
239 + if (!size(&searchStr)) tfulldirt();
240 + applyPos(state.m.searchPos);
241 + findString(state.m.search==fw ? 1 : -1, 1);
242 + } else if (state.m.c) state.m.c /= 10;
243 + len = 0;
244 + } else if (search) {
245 + if (len >= 1) decodeTo(cs, len, &searchStr);
246 + applyPos(state.m.searchPos);
247 + findString(state.m.search==fw ? 1 : -1, 1);
248 + } else if (len == 0) { result = failed;
249 + } else if (quantifier) { state.m.c = min(SHRT_MAX, (int)state.m…
250 + } else if (state.cmd.infix && state.cmd.op && (result = expandE…
251 + } else if (cs[0] == 'd') { state = defaultNormalMode; result = exit…
252 + } else if (cs[0] == '.') {
253 + if (size(&cCmd)) assign(&lCmd, &cCmd);
254 + empty(&cCmd);
255 + executeCommand((uint32_t*) lCmd.content, size(&lCmd));
256 + empty(&cCmd);
257 + len = 0;
258 + } else if (cs[0] == 'r') { tfulldirt();
259 + } else if (cs[0] == 'c') {
260 + empty(&lCmd);
261 + empty(&cCmd);
262 + empty(&searchStr);
263 + tfulldirt();
264 + len = 0;
265 + } else if (cs[0] == fw || cs[0] == bw) {
266 + empty(&searchStr);
267 + state.m.search = (Search) cs[0];
268 + state.m.searchPos = (Pos){.p={term.c.x, term.c.y, prevY…
269 + state.m.active = 1;
270 + } else if (cs[0]==infix_i || cs[0]==infix_a) { state.cmd.infix=…
271 + } else if (cs[0] == 'y') {
272 + if (state.cmd.op) {
273 + result = (state.cmd.op == yank || state.cmd.op …
274 + if (state.cmd.op == yank) selstart(0, term.c.y,…
275 + } else selstart(term.c.x, term.c.y, 0);
276 + state.cmd.op = yank;
277 + } else if (cs[0] == visual || cs[0] == visualLine) {
278 + if (state.cmd.op != (Op) cs[0]) {
279 + state.cmd = defaultNormalMode.cmd;
280 + state.cmd.op = (Op) cs[0];
281 + selstart(cs[0] == visualLine ?0 :term.c.x, term…
282 + } else result = exitOp;
283 + } else if (!(result =executeMotion((char) (len?cs[0]:0), ctrl?k…
284 + result=failed;
285 + for (size_t i = 0; !ctrl && i < amountNmKeys; ++i)
286 + if (cs[0]==nmKeys[i][0] &&
287 + failed!=(result=pressKeys(&nmKeys[i][1], str…
288 + } // Operation/Motion finished if valid: update cmd string, ext…
289 + if (result != failed) {
290 + if (len == 1 && !ctrl) decodeTo(cs, len, &cCmd);
291 + if ((state.cmd.op == visualLine) || ((state.cmd.op == y…
292 + int const off = term.c.y + (IS_SET(MODE_ALTSCRE…
293 + sel.ob.x = off ? term.col - 1 : 0;
294 + selextend(off ? 0 : term.col-1, term.c.y, sel.t…
295 + } else if (sel.oe.x != -1) {
296 + selextend(term.c.x, term.c.y, sel.type, 0);
297 + }
298 + } // Set repaint for motion or status bar
299 + if (!IS_SET(MODE_ALTSCREEN) && prevYOff != histOff) tfulldirt();
300 + // Terminate Motion / operation if thus indicated
301 + if (result == exitMotion) {
302 + if (!state.m.active) result = (exited=noOp) ? finish : …
303 + state.m.active = (int) (state.m.c = 0u);
304 + }
305 + if (result == exitOp || result == finish) {
306 + if (state.cmd.op == yank) {
307 + xsetsel(getsel());
308 + xclipcopy();
309 + }
310 + state = defaultNormalMode;
311 + selclear();
312 + if (!esc) assign(&lCmd, &cCmd);
313 + empty(&cCmd);
314 + } // Update the content displayed in the history overlay
315 + styleCmd = style[state.cmd.op==yank ? 1 : (state.cmd.op==visual…
316 + (state.cmd.op==visualLine ? 3 :0))];
317 + int const posLin = !IS_SET(MODE_ALTSCREEN) ? rangeY(insertOff-h…
318 + if (!posLin || posLin==h || !h) strcpy(posBuffer, posLin ? " [B…
319 + else sprintf(posBuffer, " % 3d%c ", min(100, max(0, (int)(.5 +…
320 + if ((overlay || overlay!=prevOverlay) && term.col>9 && term.row…
321 + if (!term.dirty[term.row-1]) xdrawline(term.line[term.r…
322 + if (!term.dirty[term.row-2]) xdrawline(term.line[term.r…
323 + }
324 + if (result==finish) altToggle = 0;
325 + if (altToggle != prevAltToggle) tswapscreen();
326 +end:
327 + historyOpToggle(-1, 1);
328 + return result;
329 +}
330 +
331 +void historyOverlay(int x, int y, Glyph* g) {
332 + if (!histMode) return;
333 + TCursor const *cHist = histOp ? &term.c : &c[0];
334 + if(overlay && term.col > 9 && term.row > 4 && (x > (2*term.col/…
335 + *g = (y == term.row - 2) ? styleSearch : styleCmd;
336 + if (y == term.row-2) getChar(&searchStr, g, term.row-2,…
337 + else if (x > term.col - 7) g->u = (Rune)(posBuffer[x - …
338 + else getChar(size(&cCmd) ?&cCmd :&lCmd, g, term.row-1, …
339 + } else if (highlighted(x, y)) g->bg = highlightBg, g->fg = high…
340 + else if ((x==cHist->x) ^ (y==cHist->y)) g->bg = currentBg;
341 + else if (x==cHist->x) g->mode^=ATTR_REVERSE;
342 +}
343 +void historyPreDraw() {
344 + static Pos op = {.p={0, 0, 0}};
345 + historyOpToggle(1, 0);
346 + // Draw the cursor cross if changed
347 + if (term.c.y >= term.row || op.p[1] >= term.row) tfulldirt();
348 + else if (exited || (op.p[1] != term.c.y)) term.dirty[term.c.y] …
349 + for (int i=0; (exited || term.c.x != op.p[0]) && i<term.row; ++…
350 + xdrawline(term.line[i], term.c.x, i, term.c.x + 1);
351 + xdrawline(term.line[i], op.p[0], i, op.p[0] + 1);
352 + }
353 + // Update search results either only for lines with new content…
354 + markSearchMatches(exited);
355 + op = (Pos){.p = {term.c.x, term.c.y, 0}};
356 + historyOpToggle(-1, 0);
357 +}
358 diff --git a/normalMode.h b/normalMode.h
359 new file mode 100644
360 index 0000000..eb77484
361 --- /dev/null
362 +++ b/normalMode.h
363 @@ -0,0 +1,8 @@
364 +void normalMode();
365 +void historyPreDraw();
366 +void historyOverlay(int x, int y, Glyph* g);
367 +void historyModeToggle(int start);
368 +void historyOpToggle(int, int);
369 +typedef enum {failed=0, success=1, exitMotion=2, exitOp=3, finish=4} Ex…
370 +ExitState kPressHist(char const *txt, size_t len, int ctrl, KeySym cons…
371 +ExitState pressKeys(char const* s, size_t e);
372 diff --git a/st.c b/st.c
373 index 0ce6ac2..4e9005f 100644
374 --- a/st.c
375 +++ b/st.c
376 @@ -1,4 +1,5 @@
377 /* See LICENSE for license details. */
378 +#include <assert.h>
379 #include <ctype.h>
380 #include <errno.h>
381 #include <fcntl.h>
382 @@ -42,6 +43,8 @@
383 #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
384 #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
385 #define ISDELIM(u) (u && wcschr(worddelimiters, u))
386 +static inline int max(int a, int b) { return a > b ? a : b; }
387 +static inline int min(int a, int b) { return a < b ? a : b; }
388
389 enum term_mode {
390 MODE_WRAP = 1 << 0,
391 @@ -97,6 +100,7 @@ typedef struct {
392 int mode;
393 int type;
394 int snap;
395 + int swap;
396 /*
397 * Selection variables:
398 * nb – normalized coordinates of the beginning of the select…
399 @@ -179,7 +183,6 @@ static void tdeleteline(int);
400 static void tinsertblank(int);
401 static void tinsertblankline(int);
402 static int tlinelen(int);
403 -static void tmoveto(int, int);
404 static void tmoveato(int, int);
405 static void tnewline(int);
406 static void tputtab(int);
407 @@ -206,7 +209,6 @@ static void drawregion(int, int, int, int);
408
409 static void selnormalize(void);
410 static void selscroll(int, int);
411 -static void selsnap(int *, int *, int);
412
413 static size_t utf8decode(const char *, Rune *, size_t);
414 static Rune utf8decodebyte(char, size_t *);
415 @@ -232,6 +234,14 @@ static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0x…
416 static Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10…
417 static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10F…
418
419 +int buffCols;
420 +extern int const buffSize;
421 +int histOp, histMode, histOff, insertOff, altToggle, *mark;
422 +Line *buf = NULL;
423 +static TCursor c[3];
424 +static inline int rows() { return IS_SET(MODE_ALTSCREEN) ? term.row : b…
425 +static inline int rangeY(int i) { while (i < 0) i += rows(); return i %…
426 +
427 ssize_t
428 xwrite(int fd, const char *s, size_t len)
429 {
430 @@ -424,6 +434,118 @@ tlinelen(int y)
431 return i;
432 }
433
434 +void historyOpToggle(int start, int paint) {
435 + if ((!histOp == !(histOp + start)) && ((histOp += start) || 1))…
436 + if (histMode && paint && (!IS_SET(MODE_ALTSCREEN) || altToggle)…
437 + tcursor(CURSOR_SAVE);
438 + histOp += start;
439 + if (histMode && altToggle) {
440 + tswapscreen();
441 + memset(term.dirty,0,sizeof(*term.dirty)*term.row);
442 + }
443 + tcursor(CURSOR_LOAD);
444 + *(!IS_SET(MODE_ALTSCREEN)?&term.line:&term.alt)=&buf[histOp?his…
445 +}
446 +
447 +void historyModeToggle(int start) {
448 + if (!(histMode = (histOp = !!start))) {
449 + selnormalize();
450 + tfulldirt();
451 + } else {
452 + tcursor(CURSOR_SAVE);
453 + histOp = 0;
454 + histOff = insertOff;
455 + }
456 +}
457 +
458 +int historyBufferScroll(int n) {
459 + if (IS_SET(MODE_ALTSCREEN) || !n) return histOp;
460 + int p=abs(n=(n<0) ? max(n,-term.row) : min(n,term.row)), r=term…
461 + s=sizeof(*term.dirty), *ptr=histOp?&histOff:&insertOf…
462 + if (!histMode || histOp) tfulldirt(); else {
463 + memmove(&term.dirty[-min(n,0)], &term.dirty[max(n,0)], …
464 + memset(&term.dirty[n>0 ? r : 0], 0, s * p);
465 + }
466 + term.line = &buf[*ptr = (buffSize+*ptr+n) % buffSize];
467 + // Cut part of selection removed from buffer, and update sel.ne…
468 + int const prevOffBuf = sel.alt ? 0 : insertOff + term.row;
469 + if (sel.ob.x != -1 && !histOp && n) {
470 + int const offBuf = sel.alt ? 0 : insertOff + term.row,
471 + pb = rangeY(sel.ob.y - prevOffBuf),
472 + pe = rangeY(sel.oe.y - prevOffBuf);
473 + int const b = rangeY(sel.ob.y - offBuf), nln = n < 0,
474 + e = rangeY(sel.oe.y - offBuf), last = offBuf …
475 + if (pb != b && ((pb < b) != nln)) sel.ob.y = last;
476 + if (pe != e && ((pe < e) != nln)) sel.oe.y = last;
477 + if (sel.oe.y == last && sel.ob.y == last) selclear();
478 + }
479 + selnormalize();
480 + // Clear the new region exposed by the shift.
481 + if (!histOp) tclearregion(0, n>0?r+1:0, buffCols-1, n>0?term.ro…
482 + return 1;
483 +}
484 +
485 +int historyMove(int x, int y, int ly) {
486 + historyOpToggle(1, 1);
487 + y += ((term.c.x += x) < 0 ?term.c.x-term.col :term.c.x) / term.…
488 + if ((term.c.x %= term.col) < 0) term.c.x += term.col;
489 + if ((term.c.y += y) >= term.row) ly += term.c.y - term.row + 1;…
490 + else if (term.c.y < 0) ly += term.c.y;
491 + term.c.y = MIN(MAX(term.c.y, 0), term.row - 1);
492 + // Check if scroll is necessary / arrived at top / bottom of te…
493 + int t = 0, b = 0, finTop = ly < 0, finBot = ly > 0;
494 + if (!IS_SET(MODE_ALTSCREEN)) {
495 + b=rangeY(insertOff-histOff), t=-rangeY(-term.row-(inser…
496 + finBot = ly > b, finTop=histMode&&((-ly>-t));
497 + }
498 + if ((finTop || finBot) && (x||y)) term.c.x = finBot ? term.col-…
499 + historyBufferScroll(finBot ? b : (finTop ? t : ly));
500 + historyOpToggle(-1, 1);
501 + return finTop || finBot;
502 +}
503 +
504 +#include "normalMode.c"
505 +
506 +void selnormalize(void) {
507 + historyOpToggle(1, 1);
508 +
509 + int const oldb = sel.nb.y, olde = sel.ne.y;
510 + if (sel.ob.x == -1) {
511 + sel.ne.y = sel.nb.y = -1;
512 + } else {
513 + int const offsetBuffer = sel.alt ? 0 : insertOff + term…
514 + int const off = sel.alt ? 0 : (histMode ? histOff : ins…
515 + int const nby = rangeY(sel.ob.y - off),
516 + ney = rangeY(sel.oe.y - off);
517 + sel.swap = rangeY(sel.ob.y - offsetBuffer)
518 + > rangeY(sel.oe.y - offsetBuffer);
519 + sel.nb.y = sel.swap ? ney : nby;
520 + sel.ne.y = !sel.swap ? ney : nby;
521 + int const cnb = sel.nb.y < term.row, cne = sel.ne.y < t…
522 + if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {
523 + if (cnb) sel.nb.x = (!sel.swap) ? sel.ob.x : se…
524 + if (cne) sel.ne.x = (!sel.swap) ? sel.oe.x : se…
525 + } else {
526 + if (cnb) sel.nb.x = MIN(sel.ob.x, sel.oe.x);
527 + if (cne) sel.ne.x = MAX(sel.ob.x, sel.oe.x);
528 + }
529 + }
530 + int const nBet=sel.nb.y<=sel.ne.y, oBet=oldb<=olde;
531 + for (int i = 0; i < term.row; ++i) {
532 + int const n = nBet ? BETWEEN(i, sel.nb.y, sel.ne.y)
533 + : OUT(i, sel.nb.y, sel.ne.y);
534 + term.dirty[i] |= (sel.type == SEL_RECTANGULAR && n) ||
535 + (n != (oBet ? BETWEEN(i,oldb,olde) : OUT(i,oldb…
536 +
537 + }
538 + if (BETWEEN(oldb, 0, term.row - 1)) term.dirty[oldb] = 1;
539 + if (BETWEEN(olde, 0, term.row - 1)) term.dirty[olde] = 1;
540 + if (BETWEEN(sel.nb.y, 0, term.row - 1)) term.dirty[sel.nb.y] = …
541 + if (BETWEEN(sel.ne.y, 0, term.row - 1)) term.dirty[sel.ne.y] = …
542 +
543 + historyOpToggle(-1, 1);
544 +}
545 +
546 void
547 selstart(int col, int row, int snap)
548 {
549 @@ -433,19 +555,14 @@ selstart(int col, int row, int snap)
550 sel.alt = IS_SET(MODE_ALTSCREEN);
551 sel.snap = snap;
552 sel.oe.x = sel.ob.x = col;
553 - sel.oe.y = sel.ob.y = row;
554 + sel.oe.y = sel.ob.y = row + !sel.alt * (histMode ? histOff : in…
555 + if (sel.snap != 0) sel.mode = SEL_READY;
556 selnormalize();
557 -
558 - if (sel.snap != 0)
559 - sel.mode = SEL_READY;
560 - tsetdirt(sel.nb.y, sel.ne.y);
561 }
562
563 void
564 selextend(int col, int row, int type, int done)
565 {
566 - int oldey, oldex, oldsby, oldsey, oldtype;
567 -
568 if (sel.mode == SEL_IDLE)
569 return;
570 if (done && sel.mode == SEL_EMPTY) {
571 @@ -453,51 +570,13 @@ selextend(int col, int row, int type, int done)
572 return;
573 }
574
575 - oldey = sel.oe.y;
576 - oldex = sel.oe.x;
577 - oldsby = sel.nb.y;
578 - oldsey = sel.ne.y;
579 - oldtype = sel.type;
580 -
581 sel.oe.x = col;
582 - sel.oe.y = row;
583 + sel.oe.y = row + (sel.alt ? 0 : (histMode ? histOff : insertOff…
584 selnormalize();
585 sel.type = type;
586 -
587 - if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.ty…
588 - tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
589 -
590 sel.mode = done ? SEL_IDLE : SEL_READY;
591 }
592
593 -void
594 -selnormalize(void)
595 -{
596 - int i;
597 -
598 - if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {
599 - sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
600 - sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
601 - } else {
602 - sel.nb.x = MIN(sel.ob.x, sel.oe.x);
603 - sel.ne.x = MAX(sel.ob.x, sel.oe.x);
604 - }
605 - sel.nb.y = MIN(sel.ob.y, sel.oe.y);
606 - sel.ne.y = MAX(sel.ob.y, sel.oe.y);
607 -
608 - selsnap(&sel.nb.x, &sel.nb.y, -1);
609 - selsnap(&sel.ne.x, &sel.ne.y, +1);
610 -
611 - /* expand selection over line breaks */
612 - if (sel.type == SEL_RECTANGULAR)
613 - return;
614 - i = tlinelen(sel.nb.y);
615 - if (i < sel.nb.x)
616 - sel.nb.x = i;
617 - if (tlinelen(sel.ne.y) <= sel.ne.x)
618 - sel.ne.x = term.col - 1;
619 -}
620 -
621 int
622 selected(int x, int y)
623 {
624 @@ -509,119 +588,47 @@ selected(int x, int y)
625 return BETWEEN(y, sel.nb.y, sel.ne.y)
626 && BETWEEN(x, sel.nb.x, sel.ne.x);
627
628 - return BETWEEN(y, sel.nb.y, sel.ne.y)
629 - && (y != sel.nb.y || x >= sel.nb.x)
630 - && (y != sel.ne.y || x <= sel.ne.x);
631 -}
632 -
633 -void
634 -selsnap(int *x, int *y, int direction)
635 -{
636 - int newx, newy, xt, yt;
637 - int delim, prevdelim;
638 - Glyph *gp, *prevgp;
639 -
640 - switch (sel.snap) {
641 - case SNAP_WORD:
642 - /*
643 - * Snap around if the word wraps around at the end or
644 - * beginning of a line.
645 - */
646 - prevgp = &term.line[*y][*x];
647 - prevdelim = ISDELIM(prevgp->u);
648 - for (;;) {
649 - newx = *x + direction;
650 - newy = *y;
651 - if (!BETWEEN(newx, 0, term.col - 1)) {
652 - newy += direction;
653 - newx = (newx + term.col) % term.col;
654 - if (!BETWEEN(newy, 0, term.row - 1))
655 - break;
656 -
657 - if (direction > 0)
658 - yt = *y, xt = *x;
659 - else
660 - yt = newy, xt = newx;
661 - if (!(term.line[yt][xt].mode & ATTR_WRA…
662 - break;
663 - }
664 -
665 - if (newx >= tlinelen(newy))
666 - break;
667 -
668 - gp = &term.line[newy][newx];
669 - delim = ISDELIM(gp->u);
670 - if (!(gp->mode & ATTR_WDUMMY) && (delim != prev…
671 - || (delim && gp->u != prevgp->u…
672 - break;
673 -
674 - *x = newx;
675 - *y = newy;
676 - prevgp = gp;
677 - prevdelim = delim;
678 - }
679 - break;
680 - case SNAP_LINE:
681 - /*
682 - * Snap around if the the previous line or the current …
683 - * has set ATTR_WRAP at its end. Then the whole next or
684 - * previous line will be selected.
685 - */
686 - *x = (direction < 0) ? 0 : term.col - 1;
687 - if (direction < 0) {
688 - for (; *y > 0; *y += direction) {
689 - if (!(term.line[*y-1][term.col-1].mode
690 - & ATTR_WRAP)) {
691 - break;
692 - }
693 - }
694 - } else if (direction > 0) {
695 - for (; *y < term.row-1; *y += direction) {
696 - if (!(term.line[*y][term.col-1].mode
697 - & ATTR_WRAP)) {
698 - break;
699 - }
700 - }
701 - }
702 - break;
703 - }
704 + return ((sel.nb.y > sel.ne.y) ? OUT(y, sel.nb.y, sel.ne.y)
705 + : BETWEEN(y, sel.nb.y, sel.ne.y))…
706 + (y != sel.nb.y || x >= sel.nb.x) &&
707 + (y != sel.ne.y || x <= sel.ne.x);
708 }
709
710 char *
711 getsel(void)
712 {
713 char *str, *ptr;
714 - int y, bufsize, lastx, linelen;
715 + int y, yy, bufsize, lastx;
716 Glyph *gp, *last;
717
718 if (sel.ob.x == -1)
719 return NULL;
720
721 - bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ;
722 + int const start = sel.swap ? sel.oe.y : sel.ob.y, h = rows();
723 + int endy = (sel.swap ? sel.ob.y : sel.oe.y);
724 + for (; endy < start; endy += h);
725 + Line * const cbuf = IS_SET(MODE_ALTSCREEN) ? term.line : buf;
726 + bufsize = (term.col+1) * (endy-start+1 ) * UTF_SIZ;
727 + assert(bufsize > 0);
728 ptr = str = xmalloc(bufsize);
729
730 /* append every set & selected glyph to the selection */
731 - for (y = sel.nb.y; y <= sel.ne.y; y++) {
732 - if ((linelen = tlinelen(y)) == 0) {
733 - *ptr++ = '\n';
734 - continue;
735 - }
736 + for (y = start; y <= endy; y++) {
737 + yy = y % h;
738
739 if (sel.type == SEL_RECTANGULAR) {
740 - gp = &term.line[y][sel.nb.x];
741 + gp = &cbuf[yy][sel.nb.x];
742 lastx = sel.ne.x;
743 } else {
744 - gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0…
745 - lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
746 + gp = &cbuf[yy][start == y ? sel.nb.x : 0];
747 + lastx = (endy == y) ? sel.ne.x : term.col-1;
748 }
749 - last = &term.line[y][MIN(lastx, linelen-1)];
750 - while (last >= gp && last->u == ' ')
751 - --last;
752 + last = &cbuf[yy][lastx];
753 + if (!(cbuf[yy][term.col - 1].mode & ATTR_WRAP))
754 + while (last > gp && last->u == ' ') --last;
755
756 for ( ; gp <= last; ++gp) {
757 - if (gp->mode & ATTR_WDUMMY)
758 - continue;
759 -
760 + if (gp->mode & ATTR_WDUMMY) continue;
761 ptr += utf8encode(gp->u, ptr);
762 }
763
764 @@ -634,7 +641,7 @@ getsel(void)
765 * st.
766 * FIXME: Fix the computer world.
767 */
768 - if ((y < sel.ne.y || lastx >= linelen) && !(last->mode …
769 + if ((y < endy || lastx == term.col - 1) && !(last->mode…
770 *ptr++ = '\n';
771 }
772 *ptr = 0;
773 @@ -648,7 +655,7 @@ selclear(void)
774 return;
775 sel.mode = SEL_IDLE;
776 sel.ob.x = -1;
777 - tsetdirt(sel.nb.y, sel.ne.y);
778 + selnormalize();
779 }
780
781 void
782 @@ -1001,8 +1008,7 @@ tfulldirt(void)
783 void
784 tcursor(int mode)
785 {
786 - static TCursor c[2];
787 - int alt = IS_SET(MODE_ALTSCREEN);
788 + int alt = (histOp) ? 0 : (IS_SET(MODE_ALTSCREEN) + 1);
789
790 if (mode == CURSOR_SAVE) {
791 c[alt] = term.c;
792 @@ -1062,6 +1068,7 @@ tswapscreen(void)
793 void
794 tscrolldown(int orig, int n)
795 {
796 + if (!orig && historyBufferScroll(-n)) return;
797 int i;
798 Line temp;
799
800 @@ -1082,6 +1089,7 @@ tscrolldown(int orig, int n)
801 void
802 tscrollup(int orig, int n)
803 {
804 + if (!orig && historyBufferScroll(n)) return;
805 int i;
806 Line temp;
807
808 @@ -1243,8 +1251,8 @@ tclearregion(int x1, int y1, int x2, int y2)
809 if (y1 > y2)
810 temp = y1, y1 = y2, y2 = temp;
811
812 - LIMIT(x1, 0, term.col-1);
813 - LIMIT(x2, 0, term.col-1);
814 + LIMIT(x1, 0, buffCols-1);
815 + LIMIT(x2, 0, buffCols-1);
816 LIMIT(y1, 0, term.row-1);
817 LIMIT(y2, 0, term.row-1);
818
819 @@ -2413,8 +2421,6 @@ check_control_code:
820 */
821 return;
822 }
823 - if (sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.y))
824 - selclear();
825
826 gp = &term.line[term.c.y][term.c.x];
827 if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
828 @@ -2483,8 +2489,10 @@ void
829 tresize(int col, int row)
830 {
831 int i;
832 - int minrow = MIN(row, term.row);
833 - int mincol = MIN(col, term.col);
834 + int const colSet = col, alt = IS_SET(MODE_ALTSCREEN), ini = buf…
835 + col = MAX(col, buffCols);
836 + row = MIN(row, buffSize);
837 + int const minrow = MIN(row, term.row), mincol = MIN(col, buffCo…
838 int *bp;
839 TCursor c;
840
841 @@ -2493,6 +2501,7 @@ tresize(int col, int row)
842 "tresize: error resizing to %dx%d\n", col, row);
843 return;
844 }
845 + if (alt) tswapscreen();
846
847 /*
848 * slide screen to keep cursor where we expect it -
849 @@ -2500,48 +2509,54 @@ tresize(int col, int row)
850 * memmove because we're freeing the earlier lines
851 */
852 for (i = 0; i <= term.c.y - row; i++) {
853 - free(term.line[i]);
854 free(term.alt[i]);
855 }
856 /* ensure that both src and dst are not NULL */
857 if (i > 0) {
858 - memmove(term.line, term.line + i, row * sizeof(Line));
859 memmove(term.alt, term.alt + i, row * sizeof(Line));
860 }
861 for (i += row; i < term.row; i++) {
862 - free(term.line[i]);
863 free(term.alt[i]);
864 }
865
866 /* resize to new height */
867 - term.line = xrealloc(term.line, row * sizeof(Line));
868 + buf = xrealloc(buf, (buffSize + row) * sizeof(Line));
869 term.alt = xrealloc(term.alt, row * sizeof(Line));
870 term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
871 + mark = xrealloc(mark, col * row * sizeof(*mark));
872 term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
873
874 /* resize each row to new width, zero-pad if needed */
875 for (i = 0; i < minrow; i++) {
876 - term.line[i] = xrealloc(term.line[i], col * sizeof(Glyp…
877 term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyp…
878 }
879
880 /* allocate any new rows */
881 for (/* i = minrow */; i < row; i++) {
882 - term.line[i] = xmalloc(col * sizeof(Glyph));
883 term.alt[i] = xmalloc(col * sizeof(Glyph));
884 }
885 - if (col > term.col) {
886 - bp = term.tabs + term.col;
887 + if (col > buffCols) {
888 + bp = term.tabs + buffCols;
889
890 - memset(bp, 0, sizeof(*term.tabs) * (col - term.col));
891 + memset(bp, 0, sizeof(*term.tabs) * (col - buffCols));
892 while (--bp > term.tabs && !*bp)
893 /* nothing */ ;
894 for (bp += tabspaces; bp < term.tabs + col; bp += tabsp…
895 *bp = 1;
896 }
897 + Glyph g=(Glyph){.bg=term.c.attr.bg, .fg=term.c.attr.fg, .u=' ',…
898 + for (i = 0; i < buffSize; ++i) {
899 + buf[i] = xrealloc(ini ? NULL : buf[i], col*sizeof(Glyph…
900 + for (int j = ini ? 0 : buffCols; j < col; ++j) buf[i][j…
901 + }
902 + for (i = 0; i < row; ++i) buf[buffSize + i] = buf[i];
903 + term.line = &buf[*(histOp?&histOff:&insertOff) +=MAX(term.c.y-r…
904 + memset(mark, 0, col * row * sizeof(*mark));
905 /* update terminal size */
906 - term.col = col;
907 + term.col = colSet;
908 + buffCols = col;
909 term.row = row;
910 + if (alt) tswapscreen();
911 /* reset scrolling region */
912 tsetscroll(0, row-1);
913 /* make use of the LIMIT in tmoveto */
914 @@ -2570,15 +2585,17 @@ resettitle(void)
915 void
916 drawregion(int x1, int y1, int x2, int y2)
917 {
918 + if (altToggle && histMode && !histOp)
919 + memset(term.dirty, 0, sizeof(*term.dirty) * term.row);
920 + int const o = !IS_SET(MODE_ALTSCREEN) && histMode && !histOp, h…
921 int y;
922
923 for (y = y1; y < y2; y++) {
924 - if (!term.dirty[y])
925 - continue;
926 -
927 - term.dirty[y] = 0;
928 - xdrawline(term.line[y], x1, y, x2);
929 + int const oy = o ? (y + insertOff - histOff + h) % h : …
930 + if (!BETWEEN(oy, 0, term.row-1) || !term.dirty[y]) cont…
931 + xdrawline(term.line[y], x1, oy, x2);
932 }
933 + memset(&term.dirty[y1], 0, sizeof(*term.dirty) * (y2 - y1));
934 }
935
936 void
937 @@ -2597,7 +2614,9 @@ draw(void)
938 if (term.line[term.c.y][cx].mode & ATTR_WDUMMY)
939 cx--;
940
941 + if (histMode) historyPreDraw();
942 drawregion(0, 0, term.col, term.row);
943 + if (!histMode)
944 xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
945 term.ocx, term.ocy, term.line[term.ocy][term.oc…
946 term.ocx = cx;
947 diff --git a/st.h b/st.h
948 index d978458..d7c2d07 100644
949 --- a/st.h
950 +++ b/st.h
951 @@ -8,6 +8,7 @@
952 #define MAX(a, b) ((a) < (b) ? (b) : (a))
953 #define LEN(a) (sizeof(a) / sizeof(a)[0])
954 #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
955 +#define OUT(x, a, b) ((a) <= (x) || (x) <= (b))
956 #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
957 #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
958 #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b)…
959 @@ -89,6 +90,7 @@ void toggleprinter(const Arg *);
960 int tattrset(int);
961 void tnew(int, int);
962 void tresize(int, int);
963 +void tmoveto(int x, int y);
964 void tsetdirtattr(int);
965 void ttyhangup(void);
966 int ttynew(char *, char *, char *, char **);
967 diff --git a/utils.h b/utils.h
968 new file mode 100644
969 index 0000000..ca435e6
970 --- /dev/null
971 +++ b/utils.h
972 @@ -0,0 +1,23 @@
973 +/// Dynamic memory-chunk, with (1) datatype size, (2/3) initialized / a…
974 +typedef struct { uint8_t const elSize; uint32_t init, alloc; char* cont…
975 +#define UTF8_ARRAY {4, 0, 0, NULL}
976 +
977 +static inline int p_alloc(DynamicArray *s, uint32_t amount) {
978 + uint32_t const diff=s->init+s->elSize*amount-s->alloc, nas=s->a…
979 + if (s->alloc < s->init + s->elSize * amount) {
980 + char* tmp = realloc(s->content, nas);
981 + if (!tmp) return 0;
982 + s->alloc = nas, s->content = tmp;
983 + }
984 + return 1;
985 +}
986 +static inline char *view(DynamicArray * s, uint32_t i) { return s->cont…
987 +static inline char *end(DynamicArray *s, uint32_t i) { return s->conten…
988 +static inline uint32_t getU32(DynamicArray* s, uint32_t i, int b) { ret…
989 +static char *expand(DynamicArray *s) { if (!p_alloc(s, 1)) return NULL;…
990 +static inline void pop(DynamicArray* s) { s->init -= s->elSize; }
991 +static inline void empty(DynamicArray* s) { s->init = 0; }
992 +static inline int size(DynamicArray const * s) { return s->init / s->el…
993 +static inline void assign(DynamicArray* s, DynamicArray const *o) {
994 + if (p_alloc(s, size(o))) memcpy(s->content, o->content, (s->ini…
995 +}
996 diff --git a/win.h b/win.h
997 index a6ef1b9..cea19f3 100644
998 --- a/win.h
999 +++ b/win.h
1000 @@ -19,6 +19,7 @@ enum win_mode {
1001 MODE_MOUSEMANY = 1 << 15,
1002 MODE_BRCKTPASTE = 1 << 16,
1003 MODE_NUMLOCK = 1 << 17,
1004 + MODE_NORMAL = 1 << 18,
1005 MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
1006 |MODE_MOUSEMANY,
1007 };
1008 diff --git a/x.c b/x.c
1009 index e5f1737..f0024ab 100644
1010 --- a/x.c
1011 +++ b/x.c
1012 @@ -19,6 +19,7 @@ char *argv0;
1013 #include "arg.h"
1014 #include "st.h"
1015 #include "win.h"
1016 +#include "normalMode.h"
1017
1018 /* types used in config.h */
1019 typedef struct {
1020 @@ -261,6 +262,7 @@ clipcopy(const Arg *dummy)
1021
1022 free(xsel.clipboard);
1023 xsel.clipboard = NULL;
1024 + xsetsel(getsel());
1025
1026 if (xsel.primary != NULL) {
1027 xsel.clipboard = xstrdup(xsel.primary);
1028 @@ -460,7 +462,6 @@ void
1029 bpress(XEvent *e)
1030 {
1031 struct timespec now;
1032 - int snap;
1033
1034 if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
1035 mousereport(e);
1036 @@ -476,17 +477,34 @@ bpress(XEvent *e)
1037 * snapping behaviour is exposed.
1038 */
1039 clock_gettime(CLOCK_MONOTONIC, &now);
1040 - if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) {
1041 - snap = SNAP_LINE;
1042 - } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclickti…
1043 - snap = SNAP_WORD;
1044 + int const tripleClick = TIMEDIFF(now, xsel.tclick2) <= …
1045 + doubleClick = TIMEDIFF(now, xsel.tclick1) <= doubleclic…
1046 + if ((mouseYank || mouseSelect) && (tripleClick || doubl…
1047 + if (!IS_SET(MODE_NORMAL)) normalMode();
1048 + historyOpToggle(1, 1);
1049 + tmoveto(evcol(e), evrow(e));
1050 + if (tripleClick) {
1051 + if (mouseYank) pressKeys("dVy", 3);
1052 + if (mouseSelect) pressKeys("dV", 2);
1053 + } else if (doubleClick) {
1054 + if (mouseYank) pressKeys("dyiW", 4);
1055 + if (mouseSelect) {
1056 + tmoveto(evcol(e), evrow(e));
1057 + pressKeys("viW", 3);
1058 + }
1059 + }
1060 + historyOpToggle(-1, 1);
1061 } else {
1062 - snap = 0;
1063 + if (!IS_SET(MODE_NORMAL)) selstart(evcol(e), ev…
1064 + else {
1065 + historyOpToggle(1, 1);
1066 + tmoveto(evcol(e), evrow(e));
1067 + pressKeys("v", 1);
1068 + historyOpToggle(-1, 1);
1069 + }
1070 }
1071 xsel.tclick2 = xsel.tclick1;
1072 xsel.tclick1 = now;
1073 -
1074 - selstart(evcol(e), evrow(e), snap);
1075 }
1076 }
1077
1078 @@ -691,8 +709,7 @@ brelease(XEvent *e)
1079
1080 if (mouseaction(e, 1))
1081 return;
1082 - if (e->xbutton.button == Button1)
1083 - mousesel(e, 1);
1084 + if (e->xbutton.button == Button1 && !IS_SET(MODE_NORMAL)) mouse…
1085 }
1086
1087 void
1088 @@ -772,6 +789,8 @@ xloadcolor(int i, const char *name, Color *ncolor)
1089 return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor);
1090 }
1091
1092 +void normalMode() { historyModeToggle((win.mode ^=MODE_NORMAL) & MODE_N…
1093 +
1094 void
1095 xloadcols(void)
1096 {
1097 @@ -1225,8 +1244,10 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, cons…
1098
1099 for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
1100 /* Fetch rune and mode for current glyph. */
1101 - rune = glyphs[i].u;
1102 - mode = glyphs[i].mode;
1103 + Glyph g = glyphs[i];
1104 + historyOverlay(x+i, y, &g);
1105 + rune = g.u;
1106 + mode = g.mode;
1107
1108 /* Skip dummy wide-character spacing. */
1109 if (mode == ATTR_WDUMMY)
1110 @@ -1608,6 +1629,7 @@ xdrawline(Line line, int x1, int y1, int x2)
1111 i = ox = 0;
1112 for (x = x1; x < x2 && i < numspecs; x++) {
1113 new = line[x];
1114 + historyOverlay(x, y1, &new);
1115 if (new.mode == ATTR_WDUMMY)
1116 continue;
1117 if (selected(x, y1))
1118 @@ -1800,6 +1822,11 @@ kpress(XEvent *ev)
1119 len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &…
1120 else
1121 len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
1122 + if (IS_SET(MODE_NORMAL)) {
1123 + if (kPressHist(buf, len, match(ControlMask, e->state), …
1124 + == finish) normal…
1125 + return;
1126 + }
1127 /* 1. shortcuts */
1128 for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
1129 if (ksym == bp->keysym && match(bp->mod, e->state)) {
1130 --
1131 2.25.1
1132
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.