commit 3b87b07404bb39bba6f22a88d7fb31b8f0f587a0
parent 9a41526bfb03bcd48fdf4deaf41b032fcbae5447
Author: Stein Gunnar Bakkeby <bakkeby@gmail.com>
Date: Wed, 13 Mar 2024 10:33:51 +0100
Adding reflow patch (#120)
Diffstat:
21 files changed, 2348 insertions(+), 84 deletions(-)
diff --git a/README.md b/README.md
@@ -15,6 +15,8 @@ Refer to [https://st.suckless.org/](https://st.suckless.org/) for details on the
### Changelog:
+2024-03-09 - Added the reflow patch
+
2024-03-07 - Improved sixel support, removed VIM browse patch
2022-10-24 - Added the fullscreen patch
@@ -231,6 +233,10 @@ Refer to [https://st.suckless.org/](https://st.suckless.org/) for details on the
~and 07 in order to facilitate the use of theme setting scripts like~
[~theme.sh~](https://github.com/lemnos/theme.sh) ~which expect these colours to be distinct~
+ - reflow
+ - allows st to be resized without cutting off text when the terminal window is made larger again
+ - text wraps when the terminal window is made smaller
+
- [relativeborder](https://st.suckless.org/patches/relativeborder/)
- allows you to specify a border that is relative in size to the width of a cell in the
terminal
diff --git a/config.def.h b/config.def.h
@@ -73,6 +73,12 @@ static float chscale = 1.0;
*/
wchar_t *worddelimiters = L" ";
+#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+/* Word delimiters for short and long jumps in the keyboard select patch */
+wchar_t *kbds_sdelim = L"!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~ ";
+wchar_t *kbds_ldelim = L" ";
+#endif // KEYBOARDSELECT_PATCH
+
/* selection timeouts (in milliseconds) */
static unsigned int doubleclicktimeout = 300;
static unsigned int tripleclicktimeout = 600;
@@ -221,6 +227,11 @@ unsigned int selectionbg = 259;
/* Else if 1 keep original foreground-color of each cell => more colors :) */
static int ignoreselfg = 1;
#endif // SELECTION_COLORS_PATCH
+#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+/* Foreground and background color of search results */
+unsigned int highlightfg = 15;
+unsigned int highlightbg = 160;
+#endif // KEYBOARDSELECT_PATCH
#if BLINKING_CURSOR_PATCH
/*
@@ -321,6 +332,10 @@ ResourcePref resources[] = {
#if ALPHA_FOCUS_HIGHLIGHT_PATCH
{ "alphaUnfocused",FLOAT, &alphaUnfocused },
#endif // ALPHA_FOCUS_HIGHLIGHT_PATCH
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ { "highlightfg", INTEGER, &highlightfg },
+ { "highlightbg", INTEGER, &highlightbg },
+ #endif // KEYBOARDSELECT_PATCH
};
#endif // XRESOURCES_PATCH
@@ -352,7 +367,7 @@ static MouseShortcut mshortcuts[] = {
{ ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} },
{ ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} },
#endif // SCROLLBACK_MOUSE_PATCH
- #if SCROLLBACK_MOUSE_ALTSCREEN_PATCH
+ #if SCROLLBACK_MOUSE_ALTSCREEN_PATCH || REFLOW_PATCH
{ XK_NO_MOD, Button4, kscrollup, {.i = 1}, 0, S_PRI },
{ XK_NO_MOD, Button5, kscrolldown, {.i = 1}, 0, S_PRI },
{ XK_ANY_MOD, Button4, ttysend, {.s = "\031"}, 0, S_ALT },
@@ -432,6 +447,10 @@ static Shortcut shortcuts[] = {
#if KEYBOARDSELECT_PATCH
{ TERMMOD, XK_Escape, keyboard_select, { 0 } },
#endif // KEYBOARDSELECT_PATCH
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ { TERMMOD, XK_F, searchforward, { 0 } },
+ { TERMMOD, XK_B, searchbackward, { 0 } },
+ #endif // KEYBOARDSELECT_PATCH
#if ISO14755_PATCH
{ TERMMOD, XK_I, iso14755, {.i = 0} },
#endif // ISO14755_PATCH
diff --git a/patch/keyboardselect_reflow.txt b/patch/keyboardselect_reflow.txt
@@ -0,0 +1,29 @@
+Shortcuts in keyboard selection mode:
+
+h, j, k, l: move cursor left/down/up/right (also with arrow keys)
+H, M, L: move cursor to the top/middle/bottom of the screen
+Home, End: move cursor to the top/bottom of the screen
+Backspace or 0, $ or A: move cursor to the beginning/end of the line
+^ or I: move cursor to the beginning of the indented line
+!: move cursor to the middle of the row
+_: move cursor to the right edge of the screen
+*: move cursor to the center of the screen
+w, W jump forward to the start of a word
+e, E jump forward to the end of a word
+b, B jump backward to the start of a word
+g, G: go to the first/last line
+z: center the screen on the cursor
+PgUp or K, PgDown or J: scroll the page up/down
+/, ?: activate input mode and search up/down
+n, N: repeat last search and search forward/backward
+f, F: jump forward/backward to the given character
+t, T: jump forward/backward to before the given character
+; or r repeat previous f, t, F or T movement and move forward
+, or R repeat previous f, t, F or T movement and move backward
+v: toggle selection mode
+V: toggle line selection mode
+s: toggle regular/rectangular selection type
+y: yank (copy) selected text
+0 - 9: set the quantifier
+Return: quit keyboard_select, yank and keep the highlight of the selection
+Escape, q: quit keyboard_select/exit input mode/exit selection mode/reset quantifier
diff --git a/patch/keyboardselect_reflow_st.c b/patch/keyboardselect_reflow_st.c
@@ -0,0 +1,769 @@
+#include <wctype.h>
+
+enum keyboardselect_mode {
+ KBDS_MODE_MOVE = 0,
+ KBDS_MODE_SELECT = 1<<1,
+ KBDS_MODE_LSELECT = 1<<2,
+ KBDS_MODE_FIND = 1<<3,
+ KBDS_MODE_SEARCH = 1<<4,
+};
+
+enum cursor_wrap {
+ KBDS_WRAP_NONE = 0,
+ KBDS_WRAP_LINE = 1<<0,
+ KBDS_WRAP_EDGE = 1<<1,
+};
+
+typedef struct {
+ int x;
+ int y;
+ Line line;
+ int len;
+} KCursor;
+
+static int kbds_in_use, kbds_quant;
+static int kbds_seltype = SEL_REGULAR;
+static int kbds_mode, kbds_directsearch;
+static int kbds_searchlen, kbds_searchdir, kbds_searchcase;
+static int kbds_finddir, kbds_findtill;
+static Glyph *kbds_searchstr;
+static Rune kbds_findchar;
+static KCursor kbds_c, kbds_oc;
+
+void
+kbds_drawstatusbar(int y)
+{
+ static char *modes[] = { " MOVE ", "", " SELECT ", " RSELECT ", " LSELECT ",
+ " SEARCH FW ", " SEARCH BW ", " FIND FW ", " FIND BW " };
+ static char quant[20] = { ' ' };
+ static Glyph g;
+ int i, n, m;
+ int mlen, qlen;
+
+ if (!kbds_in_use)
+ return;
+
+ g.mode = ATTR_REVERSE;
+ g.fg = defaultfg;
+ g.bg = defaultbg;
+
+ if (y == 0) {
+ if (kbds_issearchmode())
+ m = 5 + (kbds_searchdir < 0 ? 1 : 0);
+ else if (kbds_mode & KBDS_MODE_FIND)
+ m = 7 + (kbds_finddir < 0 ? 1 : 0);
+ else if (kbds_mode & KBDS_MODE_SELECT)
+ m = 2 + (kbds_seltype == SEL_RECTANGULAR ? 1 : 0);
+ else
+ m = kbds_mode;
+ mlen = strlen(modes[m]);
+ qlen = kbds_quant ? snprintf(quant+1, sizeof quant-1, "%i", kbds_quant) + 1 : 0;
+ if (kbds_c.y != y || kbds_c.x < term.col - qlen - mlen) {
+ for (n = mlen, i = term.col-1; i >= 0 && n > 0; i--) {
+ g.u = modes[m][--n];
+ xdrawglyph(g, i, y);
+ }
+ for (n = qlen; i >= 0 && n > 0; i--) {
+ g.u = quant[--n];
+ xdrawglyph(g, i, y);
+ }
+ }
+ }
+
+ if (y == term.row-1 && kbds_issearchmode()) {
+ for (g.u = ' ', i = 0; i < term.col; i++)
+ xdrawglyph(g, i, y);
+ g.u = (kbds_searchdir > 0) ? '/' : '?';
+ xdrawglyph(g, 0, y);
+ for (i = 0; i < kbds_searchlen; i++) {
+ g.u = kbds_searchstr[i].u;
+ g.mode = kbds_searchstr[i].mode | ATTR_WIDE | ATTR_REVERSE;
+ if (g.u == ' ' || g.mode & ATTR_WDUMMY)
+ continue;
+ xdrawglyph(g, i + 1, y);
+ }
+ g.u = ' ';
+ g.mode = ATTR_NULL;
+ xdrawglyph(g, i + 1, y);
+ }
+}
+
+void
+kbds_pasteintosearch(const char *data, int len, int append)
+{
+ static char buf[BUFSIZ];
+ static int buflen;
+ Rune u;
+ int l, n, charsize;
+
+ if (!append)
+ buflen = 0;
+
+ for (; len > 0; len -= l, data += l) {
+ l = MIN(sizeof(buf) - buflen, len);
+ memmove(buf + buflen, data, l);
+ buflen += l;
+ for (n = 0; n < buflen; n += charsize) {
+ if (IS_SET(MODE_UTF8)) {
+ /* process a complete utf8 char */
+ charsize = utf8decode(buf + n, &u, buflen - n);
+ if (charsize == 0)
+ break;
+ } else {
+ u = buf[n] & 0xFF;
+ charsize = 1;
+ }
+ if (u > 0x1f && kbds_searchlen < term.col-2) {
+ kbds_searchstr[kbds_searchlen].u = u;
+ kbds_searchstr[kbds_searchlen++].mode = ATTR_NULL;
+ if (wcwidth(u) > 1) {
+ kbds_searchstr[kbds_searchlen-1].mode = ATTR_WIDE;
+ if (kbds_searchlen < term.col-2) {
+ kbds_searchstr[kbds_searchlen].u = 0;
+ kbds_searchstr[kbds_searchlen++].mode = ATTR_WDUMMY;
+ }
+ }
+ }
+ }
+ buflen -= n;
+ /* keep any incomplete UTF-8 byte sequence for the next call */
+ if (buflen > 0)
+ memmove(buf, buf + n, buflen);
+ }
+ term.dirty[term.row-1] = 1;
+}
+
+int
+kbds_top(void)
+{
+ return IS_SET(MODE_ALTSCREEN) ? 0 : -term.histf + term.scr;
+}
+
+int
+kbds_bot(void)
+{
+ return IS_SET(MODE_ALTSCREEN) ? term.row-1 : term.row-1 + term.scr;
+}
+
+int
+kbds_iswrapped(KCursor *c)
+{
+ return c->len > 0 && (c->line[c->len-1].mode & ATTR_WRAP);
+}
+
+int
+kbds_isselectmode(void)
+{
+ return kbds_in_use && (kbds_mode & (KBDS_MODE_SELECT | KBDS_MODE_LSELECT));
+}
+
+int
+kbds_issearchmode(void)
+{
+ return kbds_in_use && (kbds_mode & KBDS_MODE_SEARCH);
+}
+
+void
+kbds_setmode(int mode)
+{
+ kbds_mode = mode;
+ term.dirty[0] = 1;
+}
+
+void
+kbds_selecttext(void)
+{
+ if (kbds_isselectmode()) {
+ if (kbds_mode & KBDS_MODE_LSELECT)
+ selextend(term.col-1, kbds_c.y, SEL_RECTANGULAR, 0);
+ else
+ selextend(kbds_c.x, kbds_c.y, kbds_seltype, 0);
+ if (sel.mode == SEL_IDLE)
+ kbds_setmode(kbds_mode & ~(KBDS_MODE_SELECT | KBDS_MODE_LSELECT));
+ }
+}
+
+void
+kbds_copytoclipboard(void)
+{
+ if (kbds_mode & KBDS_MODE_LSELECT) {
+ selextend(term.col-1, kbds_c.y, SEL_RECTANGULAR, 1);
+ sel.type = SEL_REGULAR;
+ } else {
+ selextend(kbds_c.x, kbds_c.y, kbds_seltype, 1);
+ }
+ xsetsel(getsel());
+
+ #if !CLIPBOARD_PATCH
+ xclipcopy();
+ #endif // CLIPBOARD_PATCH
+}
+
+void
+kbds_clearhighlights(void)
+{
+ int x, y;
+ Line line;
+
+ for (y = (IS_SET(MODE_ALTSCREEN) ? 0 : -term.histf); y < term.row; y++) {
+ line = TLINEABS(y);
+ for (x = 0; x < term.col; x++)
+ line[x].mode &= ~ATTR_HIGHLIGHT;
+ }
+ tfulldirt();
+}
+
+int
+kbds_moveto(int x, int y)
+{
+ if (y < 0)
+ kscrollup(&((Arg){ .i = -y }));
+ else if (y >= term.row)
+ kscrolldown(&((Arg){ .i = y - term.row + 1 }));
+ kbds_c.x = (x < 0) ? 0 : (x > term.col-1) ? term.col-1 : x;
+ kbds_c.y = (y < 0) ? 0 : (y > term.row-1) ? term.row-1 : y;
+ kbds_c.line = TLINE(kbds_c.y);
+ kbds_c.len = tlinelen(kbds_c.line);
+ if (kbds_c.x > 0 && (kbds_c.line[kbds_c.x].mode & ATTR_WDUMMY))
+ kbds_c.x--;
+}
+
+int
+kbds_moveforward(KCursor *c, int dx, int wrap)
+{
+ KCursor n = *c;
+
+ n.x += dx;
+ if (n.x >= 0 && n.x < term.col && (n.line[n.x].mode & ATTR_WDUMMY))
+ n.x += dx;
+
+ if (n.x < 0) {
+ if (!wrap || --n.y < kbds_top())
+ return 0;
+ n.line = TLINE(n.y);
+ n.len = tlinelen(n.line);
+ if ((wrap & KBDS_WRAP_LINE) && kbds_iswrapped(&n))
+ n.x = n.len-1;
+ else if (wrap & KBDS_WRAP_EDGE)
+ n.x = term.col-1;
+ else
+ return 0;
+ n.x -= (n.x > 0 && (n.line[n.x].mode & ATTR_WDUMMY)) ? 1 : 0;
+ } else if (n.x >= term.col) {
+ if (((wrap & KBDS_WRAP_EDGE) ||
+ ((wrap & KBDS_WRAP_LINE) && kbds_iswrapped(&n))) && ++n.y <= kbds_bot()) {
+ n.line = TLINE(n.y);
+ n.len = tlinelen(n.line);
+ n.x = 0;
+ } else {
+ return 0;
+ }
+ } else if (n.x >= n.len && dx > 0 && (wrap & KBDS_WRAP_LINE)) {
+ if (n.x == n.len && kbds_iswrapped(&n) && n.y < kbds_bot()) {
+ ++n.y;
+ n.line = TLINE(n.y);
+ n.len = tlinelen(n.line);
+ n.x = 0;
+ } else if (!(wrap & KBDS_WRAP_EDGE)) {
+ return 0;
+ }
+ }
+ *c = n;
+ return 1;
+}
+
+int
+kbds_ismatch(KCursor c)
+{
+ KCursor m = c;
+ int i, next;
+
+ if (c.x + kbds_searchlen > c.len && (!kbds_iswrapped(&c) || c.y >= kbds_bot()))
+ return 0;
+
+ for (next = 0, i = 0; i < kbds_searchlen; i++) {
+ if (kbds_searchstr[i].mode & ATTR_WDUMMY)
+ continue;
+ if ((next++ && !kbds_moveforward(&c, 1, KBDS_WRAP_LINE)) ||
+ (kbds_searchcase && kbds_searchstr[i].u != c.line[c.x].u) ||
+ (!kbds_searchcase && kbds_searchstr[i].u != towlower(c.line[c.x].u)))
+ return 0;
+ }
+
+ for (i = 0; i < kbds_searchlen; i++) {
+ if (!(kbds_searchstr[i].mode & ATTR_WDUMMY)) {
+ m.line[m.x].mode |= ATTR_HIGHLIGHT;
+ kbds_moveforward(&m, 1, KBDS_WRAP_LINE);
+ }
+ }
+ return 1;
+}
+
+int
+kbds_searchall(void)
+{
+ KCursor c;
+ int count = 0;
+
+ if (!kbds_searchlen)
+ return 0;
+
+ for (c.y = kbds_top(); c.y <= kbds_bot(); c.y++) {
+ c.line = TLINE(c.y);
+ c.len = tlinelen(c.line);
+ for (c.x = 0; c.x < c.len; c.x++)
+ count += kbds_ismatch(c);
+ }
+ tfulldirt();
+
+ return count;
+}
+
+void
+kbds_searchnext(int dir)
+{
+ KCursor c = kbds_c, n = kbds_c;
+ int wrapped = 0;
+
+ if (!kbds_searchlen) {
+ kbds_quant = 0;
+ return;
+ }
+
+ if (dir < 0 && c.x > c.len)
+ c.x = c.len;
+
+ for (kbds_quant = MAX(kbds_quant, 1); kbds_quant > 0;) {
+ if (!kbds_moveforward(&c, dir, KBDS_WRAP_LINE)) {
+ c.y += dir;
+ if (c.y < kbds_top())
+ c.y = kbds_bot(), wrapped++;
+ else if (c.y > kbds_bot())
+ c.y = kbds_top(), wrapped++;
+ if (wrapped > 1)
+ break;;
+ c.line = TLINE(c.y);
+ c.len = tlinelen(c.line);
+ c.x = (dir < 0 && c.len > 0) ? c.len-1 : 0;
+ c.x -= (c.x > 0 && (c.line[c.x].mode & ATTR_WDUMMY)) ? 1 : 0;
+ }
+ if (kbds_ismatch(c)) {
+ n = c;
+ kbds_quant--;
+ }
+ }
+
+ kbds_moveto(n.x, n.y);
+ kbds_quant = 0;
+}
+
+void
+kbds_findnext(int dir, int repeat)
+{
+ KCursor prev, c = kbds_c, n = kbds_c;
+ int skipfirst, yoff = 0;
+
+ if (c.len <= 0 || kbds_findchar == 0) {
+ kbds_quant = 0;
+ return;
+ }
+
+ if (dir < 0 && c.x > c.len)
+ c.x = c.len;
+
+ kbds_quant = MAX(kbds_quant, 1);
+ skipfirst = (kbds_quant == 1 && repeat && kbds_findtill);
+
+ while (kbds_quant > 0) {
+ prev = c;
+ if (!kbds_moveforward(&c, dir, KBDS_WRAP_LINE))
+ break;
+ if (c.line[c.x].u == kbds_findchar) {
+ if (skipfirst && prev.x == kbds_c.x && prev.y == kbds_c.y) {
+ skipfirst = 0;
+ continue;
+ }
+ n.x = kbds_findtill ? prev.x : c.x;
+ n.y = c.y;
+ yoff = kbds_findtill ? prev.y - c.y : 0;
+ kbds_quant--;
+ }
+ }
+
+ kbds_moveto(n.x, n.y);
+ kbds_moveto(kbds_c.x, kbds_c.y + yoff);
+ kbds_quant = 0;
+}
+
+int
+kbds_isdelim(KCursor c, int xoff, wchar_t *delims)
+{
+ if (xoff && !kbds_moveforward(&c, xoff, KBDS_WRAP_LINE))
+ return 1;
+ return wcschr(delims, c.line[c.x].u) != NULL;
+}
+
+void
+kbds_nextword(int start, int dir, wchar_t *delims)
+{
+ KCursor c = kbds_c, n = kbds_c;
+ int xoff = start ? -1 : 1;
+
+ if (dir < 0 && c.x > c.len)
+ c.x = c.len;
+ else if (dir > 0 && c.x >= c.len && c.len > 0)
+ c.x = c.len-1;
+
+ for (kbds_quant = MAX(kbds_quant, 1); kbds_quant > 0;) {
+ if (!kbds_moveforward(&c, dir, KBDS_WRAP_LINE)) {
+ c.y += dir;
+ if (c.y < kbds_top() || c.y > kbds_bot())
+ break;
+ c.line = TLINE(c.y);
+ c.len = tlinelen(c.line);
+ c.x = (dir < 0 && c.len > 0) ? c.len-1 : 0;
+ c.x -= (c.x > 0 && (c.line[c.x].mode & ATTR_WDUMMY)) ? 1 : 0;
+ }
+ if (c.len > 0 &&
+ !kbds_isdelim(c, 0, delims) && kbds_isdelim(c, xoff, delims)) {
+ n = c;
+ kbds_quant--;
+ }
+ }
+
+ kbds_moveto(n.x, n.y);
+ kbds_quant = 0;
+}
+
+int
+kbds_drawcursor(void)
+{
+ if (kbds_in_use && (!kbds_issearchmode() || kbds_c.y != term.row-1)) {
+ #if LIGATURES_PATCH
+ xdrawcursor(kbds_c.x, kbds_c.y, TLINE(kbds_c.y)[kbds_c.x],
+ kbds_oc.x, kbds_oc.y, TLINE(kbds_oc.y)[kbds_oc.x],
+ TLINE(kbds_oc.y), term.col);
+ #else
+ xdrawcursor(kbds_c.x, kbds_c.y, TLINE(kbds_c.y)[kbds_c.x],
+ kbds_oc.x, kbds_oc.y, TLINE(kbds_oc.y)[kbds_oc.x]);
+ #endif // LIGATURES_PATCH
+ kbds_moveto(kbds_c.x, kbds_c.y);
+ kbds_oc = kbds_c;
+ }
+ return term.scr != 0 || kbds_in_use;
+}
+
+int
+kbds_keyboardhandler(KeySym ksym, char *buf, int len, int forcequit)
+{
+ int i, q, dy, eol, islast, prevscr, count, wrap;
+ int alt = IS_SET(MODE_ALTSCREEN);
+ Line line;
+ Rune u;
+
+ if (kbds_issearchmode() && !forcequit) {
+ switch (ksym) {
+ case XK_Escape:
+ kbds_searchlen = 0;
+ /* FALLTHROUGH */
+ case XK_Return:
+ for (kbds_searchcase = 0, i = 0; i < kbds_searchlen; i++) {
+ if (kbds_searchstr[i].u != towlower(kbds_searchstr[i].u)) {
+ kbds_searchcase = 1;
+ break;
+ }
+ }
+ count = kbds_searchall();
+ kbds_searchnext(kbds_searchdir);
+ kbds_selecttext();
+ kbds_setmode(kbds_mode & ~KBDS_MODE_SEARCH);
+ if (count == 0 && kbds_directsearch)
+ ksym = XK_Escape;
+ break;
+ case XK_BackSpace:
+ if (kbds_searchlen) {
+ kbds_searchlen--;
+ if (kbds_searchlen && (kbds_searchstr[kbds_searchlen].mode & ATTR_WDUMMY))
+ kbds_searchlen--;
+ }
+ break;
+ default:
+ if (len < 1 || kbds_searchlen >= term.col-2)
+ return 0;
+ utf8decode(buf, &u, len);
+ kbds_searchstr[kbds_searchlen].u = u;
+ kbds_searchstr[kbds_searchlen++].mode = ATTR_NULL;
+ if (wcwidth(u) > 1) {
+ kbds_searchstr[kbds_searchlen-1].mode = ATTR_WIDE;
+ if (kbds_searchlen < term.col-2) {
+ kbds_searchstr[kbds_searchlen].u = 0;
+ kbds_searchstr[kbds_searchlen++].mode = ATTR_WDUMMY;
+ }
+ }
+ break;
+ }
+ /* If the direct search is aborted, we just go to the next switch
+ * statement and exit the keyboard selection mode immediately */
+ if (!(ksym == XK_Escape && kbds_directsearch)) {
+ term.dirty[term.row-1] = 1;
+ return 0;
+ }
+ } else if ((kbds_mode & KBDS_MODE_FIND) && !forcequit) {
+ kbds_findchar = 0;
+ switch (ksym) {
+ case XK_Escape:
+ case XK_Return:
+ kbds_quant = 0;
+ break;
+ default:
+ if (len < 1)
+ return 0;
+ utf8decode(buf, &kbds_findchar, len);
+ kbds_findnext(kbds_finddir, 0);
+ kbds_selecttext();
+ break;
+ }
+ kbds_setmode(kbds_mode & ~KBDS_MODE_FIND);
+ return 0;
+ }
+
+ switch (ksym) {
+ case -1:
+ kbds_searchstr = xmalloc(term.col * sizeof(Glyph));
+ kbds_in_use = 1;
+ kbds_moveto(term.c.x, term.c.y);
+ kbds_oc = kbds_c;
+ kbds_setmode(KBDS_MODE_MOVE);
+ return MODE_KBDSELECT;
+ case XK_V:
+ if (kbds_mode & KBDS_MODE_LSELECT) {
+ selclear();
+ kbds_setmode(kbds_mode & ~(KBDS_MODE_SELECT | KBDS_MODE_LSELECT));
+ } else if (kbds_mode & KBDS_MODE_SELECT) {
+ selextend(term.col-1, kbds_c.y, SEL_RECTANGULAR, 0);
+ sel.ob.x = 0;
+ tfulldirt();
+ kbds_setmode((kbds_mode ^ KBDS_MODE_SELECT) | KBDS_MODE_LSELECT);
+ } else {
+ selstart(0, kbds_c.y, 0);
+ selextend(term.col-1, kbds_c.y, SEL_RECTANGULAR, 0);
+ kbds_setmode(kbds_mode | KBDS_MODE_LSELECT);
+ }
+ break;
+ case XK_v:
+ if (kbds_mode & KBDS_MODE_SELECT) {
+ selclear();
+ kbds_setmode(kbds_mode & ~(KBDS_MODE_SELECT | KBDS_MODE_LSELECT));
+ } else if (kbds_mode & KBDS_MODE_LSELECT) {
+ selextend(kbds_c.x, kbds_c.y, kbds_seltype, 0);
+ kbds_setmode((kbds_mode ^ KBDS_MODE_LSELECT) | KBDS_MODE_SELECT);
+ } else {
+ selstart(kbds_c.x, kbds_c.y, 0);
+ kbds_setmode(kbds_mode | KBDS_MODE_SELECT);
+ }
+ break;
+ case XK_s:
+ if (!(kbds_mode & KBDS_MODE_LSELECT)) {
+ kbds_seltype ^= (SEL_REGULAR | SEL_RECTANGULAR);
+ selextend(kbds_c.x, kbds_c.y, kbds_seltype, 0);
+ }
+ break;
+ case XK_y:
+ case XK_Y:
+ if (kbds_isselectmode()) {
+ kbds_copytoclipboard();
+ selclear();
+ kbds_setmode(kbds_mode & ~(KBDS_MODE_SELECT | KBDS_MODE_LSELECT));
+ }
+ break;
+ case -2:
+ case -3:
+ case XK_slash:
+ case XK_KP_Divide:
+ case XK_question:
+ kbds_directsearch = (ksym == -2 || ksym == -3);
+ kbds_searchdir = (ksym == XK_question || ksym == -3) ? -1 : 1;
+ kbds_searchlen = 0;
+ kbds_setmode(kbds_mode | KBDS_MODE_SEARCH);
+ kbds_clearhighlights();
+ return 0;
+ case XK_q:
+ case XK_Escape:
+ if (!kbds_in_use)
+ return 0;
+ if (kbds_quant && !forcequit) {
+ kbds_quant = 0;
+ break;
+ }
+ selclear();
+ if (kbds_isselectmode() && !forcequit) {
+ kbds_setmode(KBDS_MODE_MOVE);
+ break;
+ }
+ kbds_setmode(KBDS_MODE_MOVE);
+ /* FALLTHROUGH */
+ case XK_Return:
+ if (kbds_isselectmode())
+ kbds_copytoclipboard();
+ kbds_in_use = kbds_quant = 0;
+ free(kbds_searchstr);
+ kscrolldown(&((Arg){ .i = term.histf }));
+ kbds_clearhighlights();
+ return MODE_KBDSELECT;
+ case XK_n:
+ case XK_N:
+ kbds_searchnext(ksym == XK_n ? kbds_searchdir : -kbds_searchdir);
+ break;
+ case XK_BackSpace:
+ kbds_moveto(0, kbds_c.y);
+ break;
+ case XK_exclam:
+ kbds_moveto(term.col/2, kbds_c.y);
+ break;
+ case XK_underscore:
+ kbds_moveto(term.col-1, kbds_c.y);
+ break;
+ case XK_dollar:
+ case XK_A:
+ eol = kbds_c.len-1;
+ line = kbds_c.line;
+ islast = (kbds_c.x == eol || (kbds_c.x == eol-1 && (line[eol-1].mode & ATTR_WIDE)));
+ if (islast && kbds_iswrapped(&kbds_c) && kbds_c.y < kbds_bot())
+ kbds_moveto(tlinelen(TLINE(kbds_c.y+1))-1, kbds_c.y+1);
+ else
+ kbds_moveto(islast ? term.col-1 : eol, kbds_c.y);
+ break;
+ case XK_asciicircum:
+ case XK_I:
+ for (i = 0; i < kbds_c.len && kbds_c.line[i].u == ' '; i++)
+ ;
+ kbds_moveto((i < kbds_c.len) ? i : 0, kbds_c.y);
+ break;
+ case XK_End:
+ case XK_KP_End:
+ kbds_moveto(kbds_c.x, term.row-1);
+ break;
+ case XK_Home:
+ case XK_KP_Home:
+ case XK_H:
+ kbds_moveto(kbds_c.x, 0);
+ break;
+ case XK_M:
+ kbds_moveto(kbds_c.x, alt ? (term.row-1) / 2
+ : MIN(term.c.y + term.scr, term.row-1) / 2);
+ break;
+ case XK_L:
+ kbds_moveto(kbds_c.x, alt ? term.row-1
+ : MIN(term.c.y + term.scr, term.row-1));
+ break;
+ case XK_Page_Up:
+ case XK_KP_Page_Up:
+ case XK_K:
+ prevscr = term.scr;
+ kscrollup(&((Arg){ .i = term.row }));
+ kbds_moveto(kbds_c.x, alt ? 0
+ : MAX(0, kbds_c.y - term.row + term.scr - prevscr));
+ break;
+ case XK_Page_Down:
+ case XK_KP_Page_Down:
+ case XK_J:
+ prevscr = term.scr;
+ kscrolldown(&((Arg){ .i = term.row }));
+ kbds_moveto(kbds_c.x, alt ? term.row-1
+ : MIN(MIN(term.c.y + term.scr, term.row-1),
+ kbds_c.y + term.row + term.scr - prevscr));
+ break;
+ case XK_asterisk:
+ case XK_KP_Multiply:
+ kbds_moveto(term.col/2, (term.row-1) / 2);
+ break;
+ case XK_g:
+ kscrollup(&((Arg){ .i = term.histf }));
+ kbds_moveto(kbds_c.x, 0);
+ break;
+ case XK_G:
+ kscrolldown(&((Arg){ .i = term.histf }));
+ kbds_moveto(kbds_c.x, alt ? term.row-1 : term.c.y);
+ break;
+ case XK_b:
+ case XK_B:
+ kbds_nextword(1, -1, (ksym == XK_b) ? kbds_sdelim : kbds_ldelim);
+ break;
+ case XK_w:
+ case XK_W:
+ kbds_nextword(1, +1, (ksym == XK_w) ? kbds_sdelim : kbds_ldelim);
+ break;
+ case XK_e:
+ case XK_E:
+ kbds_nextword(0, +1, (ksym == XK_e) ? kbds_sdelim : kbds_ldelim);
+ break;
+ case XK_z:
+ prevscr = term.scr;
+ dy = kbds_c.y - (term.row-1) / 2;
+ if (dy <= 0)
+ kscrollup(&((Arg){ .i = -dy }));
+ else
+ kscrolldown(&((Arg){ .i = dy }));
+ kbds_moveto(kbds_c.x, kbds_c.y + term.scr - prevscr);
+ break;
+ case XK_f:
+ case XK_F:
+ case XK_t:
+ case XK_T:
+ kbds_finddir = (ksym == XK_f || ksym == XK_t) ? 1 : -1;
+ kbds_findtill = (ksym == XK_t || ksym == XK_T) ? 1 : 0;
+ kbds_setmode(kbds_mode | KBDS_MODE_FIND);
+ return 0;
+ case XK_semicolon:
+ case XK_r:
+ kbds_findnext(kbds_finddir, 1);
+ break;
+ case XK_comma:
+ case XK_R:
+ kbds_findnext(-kbds_finddir, 1);
+ break;
+ case XK_0:
+ case XK_KP_0:
+ if (!kbds_quant) {
+ kbds_moveto(0, kbds_c.y);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ if (ksym >= XK_0 && ksym <= XK_9) { /* 0-9 keyboard */
+ q = (kbds_quant * 10) + (ksym ^ XK_0);
+ kbds_quant = q <= 99999999 ? q : kbds_quant;
+ term.dirty[0] = 1;
+ return 0;
+ } else if (ksym >= XK_KP_0 && ksym <= XK_KP_9) { /* 0-9 numpad */
+ q = (kbds_quant * 10) + (ksym ^ XK_KP_0);
+ kbds_quant = q <= 99999999 ? q : kbds_quant;
+ term.dirty[0] = 1;
+ return 0;
+ } else if (ksym == XK_k || ksym == XK_h)
+ i = ksym & 1;
+ else if (ksym == XK_l || ksym == XK_j)
+ i = ((ksym & 6) | 4) >> 1;
+ else if (ksym >= XK_KP_Left && ksym <= XK_KP_Down)
+ i = ksym - XK_KP_Left;
+ else if ((XK_Home & ksym) != XK_Home || (i = (ksym ^ XK_Home) - 1) > 3)
+ return 0;
+
+ kbds_quant = (kbds_quant ? kbds_quant : 1);
+
+ if (i & 1) {
+ kbds_c.y += kbds_quant * (i & 2 ? 1 : -1);
+ } else {
+ for (;kbds_quant > 0; kbds_quant--) {
+ if (!kbds_moveforward(&kbds_c, (i & 2) ? 1 : -1,
+ KBDS_WRAP_LINE | KBDS_WRAP_EDGE))
+ break;
+ }
+ }
+ kbds_moveto(kbds_c.x, kbds_c.y);
+ }
+ kbds_selecttext();
+ kbds_quant = 0;
+ term.dirty[0] = 1;
+ return 0;
+}
diff --git a/patch/keyboardselect_reflow_st.h b/patch/keyboardselect_reflow_st.h
@@ -0,0 +1,6 @@
+void kbds_drawstatusbar(int y);
+void kbds_pasteintosearch(const char *, int, int);
+int kbds_isselectmode(void);
+int kbds_issearchmode(void);
+int kbds_drawcursor(void);
+int kbds_keyboardhandler(KeySym, char *, int, int);
diff --git a/patch/keyboardselect_reflow_x.c b/patch/keyboardselect_reflow_x.c
@@ -0,0 +1,16 @@
+void keyboard_select(const Arg *dummy)
+{
+ win.mode ^= kbds_keyboardhandler(-1, NULL, 0, 0);
+}
+
+void searchforward(const Arg *)
+{
+ win.mode ^= kbds_keyboardhandler(-1, NULL, 0, 0);
+ kbds_keyboardhandler(-2, NULL, 0, 0);
+}
+
+void searchbackward(const Arg *)
+{
+ win.mode ^= kbds_keyboardhandler(-1, NULL, 0, 0);
+ kbds_keyboardhandler(-3, NULL, 0, 0);
+}
diff --git a/patch/keyboardselect_reflow_x.h b/patch/keyboardselect_reflow_x.h
@@ -0,0 +1,3 @@
+void keyboard_select(const Arg *);
+void searchforward(const Arg *);
+void searchbackward(const Arg *);
diff --git a/patch/keyboardselect_x.c b/patch/keyboardselect_x.c
@@ -1,7 +1,7 @@
void toggle_winmode(int flag) {
- win.mode ^= flag;
+ win.mode ^= flag;
}
void keyboard_select(const Arg *dummy) {
win.mode ^= trt_kbdselect(-1, NULL, 0);
-}
-\ No newline at end of file
+}
diff --git a/patch/openurlonclick.c b/patch/openurlonclick.c
@@ -1,8 +1,10 @@
+#if !REFLOW_PATCH
#if SCROLLBACK_PATCH
#define TLINEURL(y) TLINE(y)
#else
#define TLINEURL(y) term.line[y]
#endif // SCROLLBACK_PATCH
+#endif // REFLOW_PATCH
int url_x1, url_y1, url_x2, url_y2 = -1;
int url_draw, url_click, url_maxcol;
@@ -20,6 +22,20 @@ isvalidurlchar(Rune u)
}
/* find the end of the wrapped line */
+#if REFLOW_PATCH
+static int
+findeowl(Line line)
+{
+ int i = term.col - 1;
+
+ do {
+ if (line[i].mode & ATTR_WRAP)
+ return i;
+ } while (!(line[i].mode & ATTR_SET) && --i >= 0);
+
+ return -1;
+}
+#else
static int
findeowl(int row)
{
@@ -35,6 +51,7 @@ findeowl(int row)
} while (TLINEURL(row)[col].u == ' ' && --col >= 0);
return -1;
}
+#endif // REFLOW_PATCH
void
clearurl(void)
@@ -44,6 +61,84 @@ clearurl(void)
url_y2 = -1;
}
+#if REFLOW_PATCH
+char *
+detecturl(int col, int row, int draw)
+{
+ static char url[2048];
+ Line line;
+ int x1, y1, x2, y2;
+ int i = sizeof(url)/2+1, j = sizeof(url)/2;
+ int row_start = row, col_start = col;
+ int minrow = tisaltscr() ? 0 : term.scr - term.histf;
+ int maxrow = tisaltscr() ? term.row - 1 : term.scr + term.row - 1;
+
+ /* clear previously underlined url */
+ if (draw)
+ clearurl();
+
+ url_maxcol = 0;
+ line = TLINE(row);
+
+ if (!isvalidurlchar(line[col].u))
+ return NULL;
+
+ /* find the first character of url */
+ do {
+ x1 = col_start, y1 = row_start;
+ url_maxcol = MAX(url_maxcol, x1);
+ url[--i] = line[col_start].u;
+ if (--col_start < 0) {
+ if (--row_start < minrow || (col_start = findeowl(TLINE(row_start))) < 0)
+ break;
+ line = TLINE(row_start);
+ }
+ } while (isvalidurlchar(line[col_start].u) && i > 0);
+
+ /* early detection */
+ if (url[i] != 'h')
+ return NULL;
+
+ /* find the last character of url */
+ line = TLINE(row);
+ do {
+ x2 = col, y2 = row;
+ url_maxcol = MAX(url_maxcol, x2);
+ url[j++] = line[col].u;
+ if (line[col++].mode & ATTR_WRAP) {
+ if (++row > maxrow)
+ break;
+ col = 0;
+ line = TLINE(row);
+ }
+ } while (col < term.col && isvalidurlchar(line[col].u) && j < sizeof(url)-1);
+
+ url[j] = 0;
+
+ if (strncmp("https://", &url[i], 8) && strncmp("http://", &url[i], 7))
+ return NULL;
+
+ /* Ignore some trailing characters to improve detection. */
+ /* Alacritty and many other terminals also ignore these. */
+ if (strchr(",.;:?!", (int)(url[j-1])) != NULL) {
+ x2 = MAX(x2-1, 0);
+ url[j-1] = 0;
+ }
+
+ /* underline url (see xdrawglyphfontspecs() in x.c) */
+ if (draw) {
+ url_x1 = (y1 >= 0) ? x1 : 0;
+ url_x2 = (y2 < term.row) ? x2 : url_maxcol;
+ url_y1 = MAX(y1, 0);
+ url_y2 = MIN(y2, term.row-1);
+ url_draw = 1;
+ for (y1 = url_y1; y1 <= url_y2; y1++)
+ term.dirty[y1] = 1;
+ }
+
+ return &url[i];
+}
+#else
char *
detecturl(int col, int row, int draw)
{
@@ -52,6 +147,7 @@ detecturl(int col, int row, int draw)
int row_start = row;
int col_start = col;
int i = sizeof(url)/2+1, j = sizeof(url)/2;
+
#if SCROLLBACK_PATCH
int minrow = term.scr - term.histn, maxrow = term.scr + term.row - 1;
/* Fixme: MODE_ALTSCREEN is not defined here, I had to use the magic number 1<<2 */
@@ -59,7 +155,7 @@ detecturl(int col, int row, int draw)
minrow = 0, maxrow = term.row - 1;
#else
int minrow = 0, maxrow = term.row - 1;
- #endif // scrollback_patch
+ #endif // SCROLLBACK_PATCH
url_maxcol = 0;
/* clear previously underlined url */
@@ -119,6 +215,7 @@ detecturl(int col, int row, int draw)
return &url[i];
}
+#endif // REFLOW_PATCH
void
openUrlOnClick(int col, int row, char* url_opener)
diff --git a/patch/reflow.c b/patch/reflow.c
@@ -0,0 +1,931 @@
+void
+tloaddefscreen(int clear, int loadcursor)
+{
+ int col, row, alt = IS_SET(MODE_ALTSCREEN);
+
+ if (alt) {
+ if (clear) {
+ tclearregion(0, 0, term.col-1, term.row-1, 1);
+ #if SIXEL_PATCH
+ tdeleteimages();
+ #endif // SIXEL_PATCH
+ }
+ col = term.col, row = term.row;
+ tswapscreen();
+ }
+ if (loadcursor)
+ tcursor(CURSOR_LOAD);
+ if (alt)
+ tresizedef(col, row);
+}
+
+void
+tloadaltscreen(int clear, int savecursor)
+{
+ int col, row, def = !IS_SET(MODE_ALTSCREEN);
+
+ if (savecursor)
+ tcursor(CURSOR_SAVE);
+ if (def) {
+ col = term.col, row = term.row;
+ kscrolldown(&((Arg){ .i = term.scr }));
+ tswapscreen();
+ tresizealt(col, row);
+ }
+ if (clear) {
+ tclearregion(0, 0, term.col-1, term.row-1, 1);
+ #if SIXEL_PATCH
+ tdeleteimages();
+ #endif // SIXEL_PATCH
+ }
+}
+
+void
+selmove(int n)
+{
+ sel.ob.y += n, sel.nb.y += n;
+ sel.oe.y += n, sel.ne.y += n;
+}
+
+void
+tclearglyph(Glyph *gp, int usecurattr)
+{
+ if (usecurattr) {
+ gp->fg = term.c.attr.fg;
+ gp->bg = term.c.attr.bg;
+ } else {
+ gp->fg = defaultfg;
+ gp->bg = defaultbg;
+ }
+ gp->mode = ATTR_NULL;
+ gp->u = ' ';
+}
+
+#if SIXEL_PATCH
+void
+treflow_moveimages(int oldy, int newy)
+{
+ ImageList *im;
+
+ for (im = term.images; im; im = im->next) {
+ if (im->y == oldy)
+ im->reflow_y = newy;
+ }
+}
+#endif // SIXEL_PATCH
+
+void
+treflow(int col, int row)
+{
+ int i, j, x, x2;
+ int oce, nce, bot, scr;
+ int ox = 0, oy = -term.histf, nx = 0, ny = -1, len;
+ int cy = -1; /* proxy for new y coordinate of cursor */
+ int buflen, nlines, del;
+ Line *buf, bufline, line;
+ #if SIXEL_PATCH
+ ImageList *im, *next;
+
+ for (im = term.images; im; im = im->next)
+ im->reflow_y = INT_MIN; /* unset reflow_y */
+ #endif // SIXEL_PATCH
+
+ /* y coordinate of cursor line end */
+ for (oce = term.c.y; oce < term.row - 1 &&
+ tiswrapped(term.line[oce]); oce++);
+
+ nlines = HISTSIZE + row;
+ buf = xmalloc(nlines * sizeof(Line));
+ do {
+ if (!nx && ++ny < nlines)
+ buf[ny] = xmalloc(col * sizeof(Glyph));
+ if (!ox) {
+ line = TLINEABS(oy);
+ len = tlinelen(line);
+ }
+ if (oy == term.c.y) {
+ if (!ox)
+ len = MAX(len, term.c.x + 1);
+ /* update cursor */
+ if (cy < 0 && term.c.x - ox < col - nx) {
+ term.c.x = nx + term.c.x - ox, cy = ny;
+ UPDATEWRAPNEXT(0, col);
+ }
+ }
+ /* get reflowed lines in buf */
+ bufline = buf[ny % nlines];
+ if (col - nx > len - ox) {
+ memcpy(&bufline[nx], &line[ox], (len-ox) * sizeof(Glyph));
+ nx += len - ox;
+ if (len == 0 || !(line[len - 1].mode & ATTR_WRAP)) {
+ for (j = nx; j < col; j++)
+ tclearglyph(&bufline[j], 0);
+ #if SIXEL_PATCH
+ treflow_moveimages(oy+term.scr, ny);
+ #endif // SIXEL_PATCH
+ nx = 0;
+ } else if (nx > 0) {
+ bufline[nx - 1].mode &= ~ATTR_WRAP;
+ }
+ ox = 0, oy++;
+ } else if (col - nx == len - ox) {
+ memcpy(&bufline[nx], &line[ox], (col-nx) * sizeof(Glyph));
+ #if SIXEL_PATCH
+ treflow_moveimages(oy+term.scr, ny);
+ #endif // SIXEL_PATCH
+ ox = 0, oy++, nx = 0;
+ } else/* if (col - nx < len - ox) */ {
+ memcpy(&bufline[nx], &line[ox], (col-nx) * sizeof(Glyph));
+ if (bufline[col - 1].mode & ATTR_WIDE) {
+ bufline[col - 2].mode |= ATTR_WRAP;
+ tclearglyph(&bufline[col - 1], 0);
+ ox--;
+ } else {
+ bufline[col - 1].mode |= ATTR_WRAP;
+ }
+ #if SIXEL_PATCH
+ treflow_moveimages(oy+term.scr, ny);
+ #endif // SIXEL_PATCH
+ ox += col - nx;
+ nx = 0;
+ }
+ } while (oy <= oce);
+ if (nx)
+ for (j = nx; j < col; j++)
+ tclearglyph(&bufline[j], 0);
+
+ /* free extra lines */
+ for (i = row; i < term.row; i++)
+ free(term.line[i]);
+ /* resize to new height */
+ term.line = xrealloc(term.line, row * sizeof(Line));
+
+ buflen = MIN(ny + 1, nlines);
+ bot = MIN(ny, row - 1);
+ scr = MAX(row - term.row, 0);
+ /* update y coordinate of cursor line end */
+ nce = MIN(oce + scr, bot);
+ /* update cursor y coordinate */
+ term.c.y = nce - (ny - cy);
+ if (term.c.y < 0) {
+ j = nce, nce = MIN(nce + -term.c.y, bot);
+ term.c.y += nce - j;
+ while (term.c.y < 0) {
+ free(buf[ny-- % nlines]);
+ buflen--;
+ term.c.y++;
+ }
+ }
+ /* allocate new rows */
+ for (i = row - 1; i > nce; i--) {
+ if (i >= term.row)
+ term.line[i] = xmalloc(col * sizeof(Glyph));
+ else
+ term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
+ for (j = 0; j < col; j++)
+ tclearglyph(&term.line[i][j], 0);
+ }
+ /* fill visible area */
+ for (/*i = nce */; i >= term.row; i--, ny--, buflen--)
+ term.line[i] = buf[ny % nlines];
+ for (/*i = term.row - 1 */; i >= 0; i--, ny--, buflen--) {
+ free(term.line[i]);
+ term.line[i] = buf[ny % nlines];
+ }
+ /* fill lines in history buffer and update term.histf */
+ for (/*i = -1 */; buflen > 0 && i >= -HISTSIZE; i--, ny--, buflen--) {
+ j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE;
+ free(term.hist[j]);
+ term.hist[j] = buf[ny % nlines];
+ }
+ term.histf = -i - 1;
+ term.scr = MIN(term.scr, term.histf);
+ /* resize rest of the history lines */
+ for (/*i = -term.histf - 1 */; i >= -HISTSIZE; i--) {
+ j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE;
+ term.hist[j] = xrealloc(term.hist[j], col * sizeof(Glyph));
+ }
+
+ #if SIXEL_PATCH
+ /* move images to the final position */
+ for (im = term.images; im; im = next) {
+ next = im->next;
+ if (im->reflow_y == INT_MIN) {
+ delete_image(im);
+ } else {
+ im->y = im->reflow_y - term.histf + term.scr - (ny + 1);
+ if (im->y - term.scr < -HISTSIZE || im->y - term.scr >= row)
+ delete_image(im);
+ }
+ }
+
+ /* expand images into new text cells or
+ * delete images if there is text behind them */
+ for (im = term.images; im; im = next) {
+ next = im->next;
+ if (im->x < col) {
+ line = TLINE(im->y);
+ x2 = MIN(im->x + im->cols, col);
+ for (del = 0, x = im->x; x < x2; x++) {
+ if ((del = line[x].mode & ATTR_SET))
+ break;
+ line[x].u = ' ';
+ line[x].mode = ATTR_SIXEL;
+ }
+ if (del)
+ delete_image(im);
+ }
+ }
+ #endif // SIXEL_PATCH
+
+ for (; buflen > 0; ny--, buflen--)
+ free(buf[ny % nlines]);
+ free(buf);
+}
+
+void
+rscrolldown(int n)
+{
+ int i;
+ Line temp;
+
+ /* can never be true as of now
+ if (IS_SET(MODE_ALTSCREEN))
+ return; */
+
+ if ((n = MIN(n, term.histf)) <= 0)
+ return;
+
+ for (i = term.c.y + n; i >= n; i--) {
+ temp = term.line[i];
+ term.line[i] = term.line[i-n];
+ term.line[i-n] = temp;
+ }
+ for (/*i = n - 1 */; i >= 0; i--) {
+ temp = term.line[i];
+ term.line[i] = term.hist[term.histi];
+ term.hist[term.histi] = temp;
+ term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE;
+ }
+ term.c.y += n;
+ term.histf -= n;
+ if ((i = term.scr - n) >= 0) {
+ term.scr = i;
+ } else {
+ #if SIXEL_PATCH
+ scroll_images(n - term.scr);
+ #endif // SIXEL_PATCH
+ term.scr = 0;
+ if (sel.ob.x != -1 && !sel.alt)
+ selmove(-i);
+ }
+}
+
+void
+tresizedef(int col, int row)
+{
+ int i, j;
+
+ /* return if dimensions haven't changed */
+ if (term.col == col && term.row == row) {
+ tfulldirt();
+ return;
+ }
+ if (col != term.col) {
+ if (!sel.alt)
+ selremove();
+ treflow(col, row);
+ } else {
+ /* slide screen up if otherwise cursor would get out of the screen */
+ if (term.c.y >= row) {
+ tscrollup(0, term.row - 1, term.c.y - row + 1, SCROLL_RESIZE);
+ term.c.y = row - 1;
+ }
+ for (i = row; i < term.row; i++)
+ free(term.line[i]);
+
+ /* resize to new height */
+ term.line = xrealloc(term.line, row * sizeof(Line));
+ /* allocate any new rows */
+ for (i = term.row; i < row; i++) {
+ term.line[i] = xmalloc(col * sizeof(Glyph));
+ for (j = 0; j < col; j++)
+ tclearglyph(&term.line[i][j], 0);
+ }
+ /* scroll down as much as height has increased */
+ rscrolldown(row - term.row);
+ }
+ /* update terminal size */
+ term.col = col, term.row = row;
+ /* reset scrolling region */
+ term.top = 0, term.bot = row - 1;
+ /* dirty all lines */
+ tfulldirt();
+}
+
+void
+tresizealt(int col, int row)
+{
+ int i, j;
+ #if SIXEL_PATCH
+ ImageList *im, *next;
+ #endif // SIXEL_PATCH
+
+ /* return if dimensions haven't changed */
+ if (term.col == col && term.row == row) {
+ tfulldirt();
+ return;
+ }
+ if (sel.alt)
+ selremove();
+ /* slide screen up if otherwise cursor would get out of the screen */
+ for (i = 0; i <= term.c.y - row; i++)
+ free(term.line[i]);
+ if (i > 0) {
+ /* ensure that both src and dst are not NULL */
+ memmove(term.line, term.line + i, row * sizeof(Line));
+ #if SIXEL_PATCH
+ scroll_images(-i);
+ #endif // SIXEL_PATCH
+ term.c.y = row - 1;
+ }
+ for (i += row; i < term.row; i++)
+ free(term.line[i]);
+ /* resize to new height */
+ term.line = xrealloc(term.line, row * sizeof(Line));
+ /* resize to new width */
+ for (i = 0; i < MIN(row, term.row); i++) {
+ term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
+ for (j = term.col; j < col; j++)
+ tclearglyph(&term.line[i][j], 0);
+ }
+ /* allocate any new rows */
+ for (/*i = MIN(row, term.row) */; i < row; i++) {
+ term.line[i] = xmalloc(col * sizeof(Glyph));
+ for (j = 0; j < col; j++)
+ tclearglyph(&term.line[i][j], 0);
+ }
+ /* update cursor */
+ if (term.c.x >= col) {
+ term.c.state &= ~CURSOR_WRAPNEXT;
+ term.c.x = col - 1;
+ } else {
+ UPDATEWRAPNEXT(1, col);
+ }
+ /* update terminal size */
+ term.col = col, term.row = row;
+ /* reset scrolling region */
+ term.top = 0, term.bot = row - 1;
+
+ #if SIXEL_PATCH
+ /* delete or clip images if they are not inside the screen */
+ for (im = term.images; im; im = next) {
+ next = im->next;
+ if (im->x >= term.col || im->y >= term.row || im->y < 0) {
+ delete_image(im);
+ } else {
+ if ((im->cols = MIN(im->x + im->cols, term.col) - im->x) <= 0)
+ delete_image(im);
+ }
+ }
+ #endif // SIXEL_PATCH
+
+ /* dirty all lines */
+ tfulldirt();
+}
+
+void
+kscrolldown(const Arg* a)
+{
+ int n = a->i;
+
+ if (!term.scr || IS_SET(MODE_ALTSCREEN))
+ return;
+
+ if (n < 0)
+ n = MAX(term.row / -n, 1);
+
+ if (n <= term.scr) {
+ term.scr -= n;
+ } else {
+ n = term.scr;
+ term.scr = 0;
+ }
+
+ if (sel.ob.x != -1 && !sel.alt)
+ selmove(-n); /* negate change in term.scr */
+ tfulldirt();
+
+ #if SIXEL_PATCH
+ scroll_images(-1*n);
+ #endif // SIXEL_PATCH
+
+ #if OPENURLONCLICK_PATCH
+ if (n > 0)
+ restoremousecursor();
+ #endif // OPENURLONCLICK_PATCH
+}
+
+void
+kscrollup(const Arg* a)
+{
+ int n = a->i;
+
+ if (!term.histf || IS_SET(MODE_ALTSCREEN))
+ return;
+
+ if (n < 0)
+ n = MAX(term.row / -n, 1);
+
+ if (term.scr + n <= term.histf) {
+ term.scr += n;
+ } else {
+ n = term.histf - term.scr;
+ term.scr = term.histf;
+ }
+
+ if (sel.ob.x != -1 && !sel.alt)
+ selmove(n); /* negate change in term.scr */
+ tfulldirt();
+
+ #if SIXEL_PATCH
+ scroll_images(n);
+ #endif // SIXEL_PATCH
+
+ #if OPENURLONCLICK_PATCH
+ if (n > 0)
+ restoremousecursor();
+ #endif // OPENURLONCLICK_PATCH
+}
+
+void
+tscrollup(int top, int bot, int n, int mode)
+{
+ #if OPENURLONCLICK_PATCH
+ restoremousecursor();
+ #endif //OPENURLONCLICK_PATCH
+
+ int i, j, s;
+ Line temp;
+ int alt = IS_SET(MODE_ALTSCREEN);
+ int savehist = !alt && top == 0 && mode != SCROLL_NOSAVEHIST;
+ int scr = alt ? 0 : term.scr;
+ #if SIXEL_PATCH
+ int itop = top + scr, ibot = bot + scr;
+ ImageList *im, *next;
+ #endif // SIXEL_PATCH
+
+ if (n <= 0)
+ return;
+ n = MIN(n, bot-top+1);
+
+ if (savehist) {
+ for (i = 0; i < n; i++) {
+ term.histi = (term.histi + 1) % HISTSIZE;
+ temp = term.hist[term.histi];
+ for (j = 0; j < term.col; j++)
+ tclearglyph(&temp[j], 1);
+ term.hist[term.histi] = term.line[i];
+ term.line[i] = temp;
+ }
+ term.histf = MIN(term.histf + n, HISTSIZE);
+ s = n;
+ if (term.scr) {
+ j = term.scr;
+ term.scr = MIN(j + n, HISTSIZE);
+ s = j + n - term.scr;
+ }
+ if (mode != SCROLL_RESIZE)
+ tfulldirt();
+ } else {
+ tclearregion(0, top, term.col-1, top+n-1, 1);
+ tsetdirt(top + scr, bot + scr);
+ }
+
+ for (i = top; i <= bot-n; i++) {
+ temp = term.line[i];
+ term.line[i] = term.line[i+n];
+ term.line[i+n] = temp;
+ }
+
+ #if SIXEL_PATCH
+ if (alt || !savehist) {
+ /* move images, if they are inside the scrolling region */
+ for (im = term.images; im; im = next) {
+ next = im->next;
+ if (im->y >= itop && im->y <= ibot) {
+ im->y -= n;
+ if (im->y < itop)
+ delete_image(im);
+ }
+ }
+ } else {
+ /* move images, if they are inside the scrolling region or scrollback */
+ for (im = term.images; im; im = next) {
+ next = im->next;
+ im->y -= scr;
+ if (im->y < 0) {
+ im->y -= n;
+ } else if (im->y >= top && im->y <= bot) {
+ im->y -= n;
+ if (im->y < top)
+ im->y -= top; // move to scrollback
+ }
+ if (im->y < -HISTSIZE)
+ delete_image(im);
+ else
+ im->y += term.scr;
+ }
+ }
+ #endif // SIXEL_PATCH
+
+ if (sel.ob.x != -1 && sel.alt == alt) {
+ if (!savehist) {
+ selscroll(top, bot, -n);
+ } else if (s > 0) {
+ selmove(-s);
+ if (-term.scr + sel.nb.y < -term.histf)
+ selremove();
+ }
+ }
+}
+
+void
+tscrolldown(int top, int n)
+{
+ #if OPENURLONCLICK_PATCH
+ restoremousecursor();
+ #endif //OPENURLONCLICK_PATCH
+
+ int i, bot = term.bot;
+ int scr = IS_SET(MODE_ALTSCREEN) ? 0 : term.scr;
+ int itop = top + scr, ibot = bot + scr;
+ Line temp;
+ #if SIXEL_PATCH
+ ImageList *im, *next;
+ #endif // SIXEL_PATCH
+
+ if (n <= 0)
+ return;
+ n = MIN(n, bot-top+1);
+
+ tsetdirt(top + scr, bot + scr);
+ tclearregion(0, bot-n+1, term.col-1, bot, 1);
+
+ for (i = bot; i >= top+n; i--) {
+ temp = term.line[i];
+ term.line[i] = term.line[i-n];
+ term.line[i-n] = temp;
+ }
+
+ #if SIXEL_PATCH
+ /* move images, if they are inside the scrolling region */
+ for (im = term.images; im; im = next) {
+ next = im->next;
+ if (im->y >= itop && im->y <= ibot) {
+ im->y += n;
+ if (im->y > ibot)
+ delete_image(im);
+ }
+ }
+ #endif // SIXEL_PATCH
+
+ if (sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN))
+ selscroll(top, bot, n);
+}
+
+void
+tresize(int col, int row)
+{
+ int *bp;
+
+ #if KEYBOARDSELECT_PATCH
+ if (row != term.row || col != term.col)
+ win.mode ^= kbds_keyboardhandler(XK_Escape, NULL, 0, 1);
+ #endif // KEYBOARDSELECT_PATCH
+
+ term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
+ term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
+ if (col > term.col) {
+ bp = term.tabs + term.col;
+ memset(bp, 0, sizeof(*term.tabs) * (col - term.col));
+ while (--bp > term.tabs && !*bp)
+ /* nothing */ ;
+ for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
+ *bp = 1;
+ }
+
+ if (IS_SET(MODE_ALTSCREEN))
+ tresizealt(col, row);
+ else
+ tresizedef(col, row);
+}
+
+void
+tclearregion(int x1, int y1, int x2, int y2, int usecurattr)
+{
+ int x, y;
+
+ /* regionselected() takes relative coordinates */
+ if (regionselected(x1+term.scr, y1+term.scr, x2+term.scr, y2+term.scr))
+ selremove();
+
+ for (y = y1; y <= y2; y++) {
+ term.dirty[y] = 1;
+ for (x = x1; x <= x2; x++)
+ tclearglyph(&term.line[y][x], usecurattr);
+ }
+}
+
+void
+tnew(int col, int row)
+{
+ int i, j;
+ for (i = 0; i < 2; i++) {
+ term.line = xmalloc(row * sizeof(Line));
+ for (j = 0; j < row; j++)
+ term.line[j] = xmalloc(col * sizeof(Glyph));
+ term.col = col, term.row = row;
+ tswapscreen();
+ }
+ term.dirty = xmalloc(row * sizeof(*term.dirty));
+ term.tabs = xmalloc(col * sizeof(*term.tabs));
+ for (i = 0; i < HISTSIZE; i++)
+ term.hist[i] = xmalloc(col * sizeof(Glyph));
+ treset();
+}
+
+void
+tdeletechar(int n)
+{
+ int src, dst, size;
+ Line line;
+
+ if (n <= 0)
+ return;
+ dst = term.c.x;
+ src = MIN(term.c.x + n, term.col);
+ size = term.col - src;
+ if (size > 0) { /* otherwise src would point beyond the array
+ https://stackoverflow.com/questions/29844298 */
+ line = term.line[term.c.y];
+ memmove(&line[dst], &line[src], size * sizeof(Glyph));
+ }
+ tclearregion(dst + size, term.c.y, term.col - 1, term.c.y, 1);
+}
+
+void
+tinsertblank(int n)
+{
+ int src, dst, size;
+ Line line;
+
+ if (n <= 0)
+ return;
+ dst = MIN(term.c.x + n, term.col);
+ src = term.c.x;
+ size = term.col - dst;
+
+ if (size > 0) { /* otherwise dst would point beyond the array */
+ line = term.line[term.c.y];
+ memmove(&line[dst], &line[src], size * sizeof(Glyph));
+ }
+ tclearregion(src, term.c.y, dst - 1, term.c.y, 1);
+}
+
+int
+tlinelen(Line line)
+{
+ int i = term.col - 1;
+
+ /* We are using a different algorithm on the alt screen because an
+ * application might use spaces to clear the screen and in that case it is
+ * impossible to find the end of the line when every cell has the ATTR_SET
+ * attribute. The second algorithm is more accurate on the main screen and
+ * and we can use it there. */
+ if (IS_SET(MODE_ALTSCREEN))
+ for (; i >= 0 && !(line[i].mode & ATTR_WRAP) && line[i].u == ' '; i--);
+ else
+ for (; i >= 0 && !(line[i].mode & (ATTR_SET | ATTR_WRAP)); i--);
+
+ return i + 1;
+}
+
+int
+tiswrapped(Line line)
+{
+ int len = tlinelen(line);
+
+ return len > 0 && (line[len - 1].mode & ATTR_WRAP);
+}
+
+char *
+tgetglyphs(char *buf, const Glyph *gp, const Glyph *lgp)
+{
+ while (gp <= lgp)
+ if (gp->mode & ATTR_WDUMMY) {
+ gp++;
+ } else {
+ buf += utf8encode((gp++)->u, buf);
+ }
+ return buf;
+}
+
+size_t
+tgetline(char *buf, const Glyph *fgp)
+{
+ char *ptr;
+ const Glyph *lgp = &fgp[term.col - 1];
+
+ while (lgp > fgp && !(lgp->mode & (ATTR_SET | ATTR_WRAP)))
+ lgp--;
+ ptr = tgetglyphs(buf, fgp, lgp);
+ if (!(lgp->mode & ATTR_WRAP))
+ *(ptr++) = '\n';
+ return ptr - buf;
+}
+
+int
+regionselected(int x1, int y1, int x2, int y2)
+{
+ if (sel.ob.x == -1 || sel.mode == SEL_EMPTY ||
+ sel.alt != IS_SET(MODE_ALTSCREEN) || sel.nb.y > y2 || sel.ne.y < y1)
+ return 0;
+
+ return (sel.type == SEL_RECTANGULAR) ? sel.nb.x <= x2 && sel.ne.x >= x1
+ : (sel.nb.y != y2 || sel.nb.x <= x2) &&
+ (sel.ne.y != y1 || sel.ne.x >= x1);
+}
+
+int
+selected(int x, int y)
+{
+ return regionselected(x, y, x, y);
+}
+
+void
+selsnap(int *x, int *y, int direction)
+{
+ int newx, newy;
+ int rtop = 0, rbot = term.row - 1;
+ int delim, prevdelim, maxlen;
+ const Glyph *gp, *prevgp;
+
+ if (!IS_SET(MODE_ALTSCREEN))
+ rtop += -term.histf + term.scr, rbot += term.scr;
+
+ switch (sel.snap) {
+ case SNAP_WORD:
+ /*
+ * Snap around if the word wraps around at the end or
+ * beginning of a line.
+ */
+ maxlen = (TLINE(*y)[term.col-2].mode & ATTR_WRAP) ? term.col-1 : term.col;
+ LIMIT(*x, 0, maxlen - 1);
+ prevgp = &TLINE(*y)[*x];
+ prevdelim = ISDELIM(prevgp->u);
+ for (;;) {
+ newx = *x + direction;
+ newy = *y;
+ if (!BETWEEN(newx, 0, maxlen - 1)) {
+ newy += direction;
+ if (!BETWEEN(newy, rtop, rbot))
+ break;
+
+ if (!tiswrapped(TLINE(direction > 0 ? *y : newy)))
+ break;
+
+ maxlen = (TLINE(newy)[term.col-2].mode & ATTR_WRAP) ? term.col-1 : term.col;
+ newx = direction > 0 ? 0 : maxlen - 1;
+ }
+
+ gp = &TLINE(newy)[newx];
+ delim = ISDELIM(gp->u);
+ if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
+ || (delim && gp->u != prevgp->u)))
+ break;
+
+ *x = newx;
+ *y = newy;
+ if (!(gp->mode & ATTR_WDUMMY)) {
+ prevgp = gp;
+ prevdelim = delim;
+ }
+ }
+ break;
+ case SNAP_LINE:
+ /*
+ * Snap around if the the previous line or the current one
+ * has set ATTR_WRAP at its end. Then the whole next or
+ * previous line will be selected.
+ */
+ *x = (direction < 0) ? 0 : term.col - 1;
+ if (direction < 0) {
+ for (; *y > rtop; *y -= 1) {
+ if (!tiswrapped(TLINE(*y-1)))
+ break;
+ }
+ } else if (direction > 0) {
+ for (; *y < rbot; *y += 1) {
+ if (!tiswrapped(TLINE(*y)))
+ break;
+ }
+ }
+ break;
+ }
+}
+
+void
+selscroll(int top, int bot, int n)
+{
+ /* turn absolute coordinates into relative */
+ top += term.scr, bot += term.scr;
+
+ if (BETWEEN(sel.nb.y, top, bot) != BETWEEN(sel.ne.y, top, bot)) {
+ selclear();
+ } else if (BETWEEN(sel.nb.y, top, bot)) {
+ selmove(n);
+ if (sel.nb.y < top || sel.ne.y > bot)
+ selclear();
+ }
+}
+
+void
+tswapscreen(void)
+{
+ static Line *altline;
+ static int altcol, altrow;
+ Line *tmpline = term.line;
+ int tmpcol = term.col, tmprow = term.row;
+ #if SIXEL_PATCH
+ ImageList *im = term.images;
+ #endif // SIXEL_PATCH
+
+ term.line = altline;
+ term.col = altcol, term.row = altrow;
+ altline = tmpline;
+ altcol = tmpcol, altrow = tmprow;
+ term.mode ^= MODE_ALTSCREEN;
+
+ #if SIXEL_PATCH
+ term.images = term.images_alt;
+ term.images_alt = im;
+ #endif // SIXEL_PATCH
+}
+
+char *
+getsel(void)
+{
+ char *str, *ptr;
+ int y, lastx, linelen;
+ const Glyph *gp, *lgp;
+
+ if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN))
+ return NULL;
+
+ str = xmalloc((term.col + 1) * (sel.ne.y - sel.nb.y + 1) * UTF_SIZ);
+ ptr = str;
+
+ /* append every set & selected glyph to the selection */
+ for (y = sel.nb.y; y <= sel.ne.y; y++) {
+ Line line = TLINE(y);
+
+ if ((linelen = tlinelen(line)) == 0) {
+ *ptr++ = '\n';
+ continue;
+ }
+
+ if (sel.type == SEL_RECTANGULAR) {
+ gp = &line[sel.nb.x];
+ lastx = sel.ne.x;
+ } else {
+ gp = &line[sel.nb.y == y ? sel.nb.x : 0];
+ lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
+ }
+ lgp = &line[MIN(lastx, linelen-1)];
+
+ ptr = tgetglyphs(ptr, gp, lgp);
+ /*
+ * Copy and pasting of line endings is inconsistent
+ * in the inconsistent terminal and GUI world.
+ * The best solution seems like to produce '\n' when
+ * something is copied from st and convert '\n' to
+ * '\r', when something to be pasted is received by
+ * st.
+ * FIXME: Fix the computer world.
+ */
+ if ((y < sel.ne.y || lastx >= linelen) &&
+ (!(lgp->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
+ *ptr++ = '\n';
+ }
+ *ptr = '\0';
+ return str;
+}
+
+void
+tdumpline(int n)
+{
+ char str[(term.col + 1) * UTF_SIZ];
+
+ tprinter(str, tgetline(str, &term.line[n][0]));
+}
diff --git a/patch/reflow.h b/patch/reflow.h
@@ -0,0 +1,44 @@
+#define TLINE(y) ( \
+ (y) < term.scr ? term.hist[(term.histi + (y) - term.scr + 1 + HISTSIZE) % HISTSIZE] \
+ : term.line[(y) - term.scr] \
+)
+
+#define TLINEABS(y) ( \
+ (y) < 0 ? term.hist[(term.histi + (y) + 1 + HISTSIZE) % HISTSIZE] : term.line[(y)] \
+)
+
+#define UPDATEWRAPNEXT(alt, col) do { \
+ if ((term.c.state & CURSOR_WRAPNEXT) && term.c.x + term.wrapcwidth[alt] < col) { \
+ term.c.x += term.wrapcwidth[alt]; \
+ term.c.state &= ~CURSOR_WRAPNEXT; \
+ } \
+} while (0);
+
+static int tiswrapped(Line line);
+static size_t tgetline(char *, const Glyph *);
+static inline int regionselected(int, int, int, int);
+static void tloaddefscreen(int, int);
+static void tloadaltscreen(int, int);
+static void selmove(int);
+static inline void tclearglyph(Glyph *, int);
+static void treflow(int, int);
+static void rscrolldown(int);
+static void tresizedef(int, int);
+static void tresizealt(int, int);
+void kscrolldown(const Arg *);
+void kscrollup(const Arg *);
+static void tscrollup(int, int, int, int);
+static void tclearregion(int, int, int, int, int);
+static void tdeletechar(int);
+static int tlinelen(Line len);
+static char * tgetglyphs(char *buf, const Glyph *gp, const Glyph *lgp);
+static void selscroll(int, int, int);
+
+typedef struct {
+ uint b;
+ uint mask;
+ void (*func)(const Arg *);
+ const Arg arg;
+} MouseKey;
+
+extern MouseKey mkeys[];
diff --git a/patch/st_include.c b/patch/st_include.c
@@ -8,7 +8,9 @@
#if ISO14755_PATCH
#include "iso14755.c"
#endif
-#if KEYBOARDSELECT_PATCH
+#if REFLOW_PATCH && KEYBOARDSELECT_PATCH
+#include "keyboardselect_reflow_st.c"
+#elif KEYBOARDSELECT_PATCH
#include "keyboardselect_st.c"
#endif
#if RIGHTCLICKTOPLUMB_PATCH
@@ -17,7 +19,9 @@
#if NEWTERM_PATCH
#include "newterm.c"
#endif
-#if SCROLLBACK_PATCH || SCROLLBACK_MOUSE_PATCH || SCROLLBACK_MOUSE_ALTSCREEN_PATCH
+#if REFLOW_PATCH
+#include "reflow.c"
+#elif SCROLLBACK_PATCH || SCROLLBACK_MOUSE_PATCH || SCROLLBACK_MOUSE_ALTSCREEN_PATCH
#include "scrollback.c"
#endif
#if SYNC_PATCH
diff --git a/patch/st_include.h b/patch/st_include.h
@@ -8,7 +8,9 @@
#if ISO14755_PATCH
#include "iso14755.h"
#endif
-#if KEYBOARDSELECT_PATCH
+#if REFLOW_PATCH && KEYBOARDSELECT_PATCH
+#include "keyboardselect_reflow_st.h"
+#elif KEYBOARDSELECT_PATCH
#include "keyboardselect_st.h"
#endif
#if OPENURLONCLICK_PATCH
@@ -20,7 +22,9 @@
#if NEWTERM_PATCH
#include "newterm.h"
#endif
-#if SCROLLBACK_PATCH || SCROLLBACK_MOUSE_PATCH || SCROLLBACK_MOUSE_ALTSCREEN_PATCH
+#if REFLOW_PATCH
+#include "reflow.h"
+#elif SCROLLBACK_PATCH || SCROLLBACK_MOUSE_PATCH || SCROLLBACK_MOUSE_ALTSCREEN_PATCH
#include "scrollback.h"
#endif
#if SYNC_PATCH
diff --git a/patch/x_include.c b/patch/x_include.c
@@ -23,7 +23,9 @@
#if INVERT_PATCH
#include "invert.c"
#endif
-#if KEYBOARDSELECT_PATCH
+#if REFLOW_PATCH && KEYBOARDSELECT_PATCH
+#include "keyboardselect_reflow_x.c"
+#elif KEYBOARDSELECT_PATCH
#include "keyboardselect_x.c"
#endif
#if OPENURLONCLICK_PATCH
diff --git a/patch/x_include.h b/patch/x_include.h
@@ -20,7 +20,10 @@
#if INVERT_PATCH
#include "invert.h"
#endif
-#if KEYBOARDSELECT_PATCH
+#if REFLOW_PATCH && KEYBOARDSELECT_PATCH
+#include "keyboardselect_reflow_st.h"
+#include "keyboardselect_reflow_x.h"
+#elif KEYBOARDSELECT_PATCH
#include "keyboardselect_x.h"
#endif
#if NETWMICON_PATCH
diff --git a/patches.def.h b/patches.def.h
@@ -264,6 +264,13 @@
*/
#define OPENURLONCLICK_PATCH 0
+/* Reflow.
+ * Allows st to be resized without cutting off text when the terminal window is made larger again.
+ * Text wraps when the terminal window is made smaller.
+ * Comes with scrollback.
+ */
+#define REFLOW_PATCH 0
+
/* This patch allows you to specify a border that is relative in size to the width of a cell
* in the terminal.
* https://st.suckless.org/patches/relativeborder/
diff --git a/sixel.c b/sixel.c
@@ -36,7 +36,7 @@ static sixel_color_t const sixel_default_color_table[] = {
void
scroll_images(int n) {
ImageList *im, *next;
- #if SCROLLBACK_PATCH
+ #if SCROLLBACK_PATCH || REFLOW_PATCH
int top = tisaltscr() ? 0 : term.scr - HISTSIZE;
#else
int top = 0;
diff --git a/st.c b/st.c
@@ -73,6 +73,14 @@ enum term_mode {
#endif // SIXEL_PATCH
};
+#if REFLOW_PATCH
+enum scroll_mode {
+ SCROLL_RESIZE = -1,
+ SCROLL_NOSAVEHIST = 0,
+ SCROLL_SAVEHIST = 1
+};
+#endif // REFLOW_PATCH
+
enum cursor_movement {
CURSOR_SAVE,
CURSOR_LOAD
@@ -177,27 +185,36 @@ static void tprinter(char *, size_t);
static void tdumpsel(void);
static void tdumpline(int);
static void tdump(void);
+#if !REFLOW_PATCH
static void tclearregion(int, int, int, int);
+#endif // REFLOW_PATCH
static void tcursor(int);
+static void tresetcursor(void);
+#if !REFLOW_PATCH
static void tdeletechar(int);
+#endif // REFLOW_PATCH
#if SIXEL_PATCH
static void tdeleteimages(void);
#endif // SIXEL_PATCH
static void tdeleteline(int);
static void tinsertblank(int);
static void tinsertblankline(int);
+#if !REFLOW_PATCH
static int tlinelen(int);
+#endif // REFLOW_PATCH
static void tmoveto(int, int);
static void tmoveato(int, int);
static void tnewline(int);
static void tputtab(int);
static void tputc(Rune);
static void treset(void);
+#if !REFLOW_PATCH
#if SCROLLBACK_PATCH
static void tscrollup(int, int, int);
#else
static void tscrollup(int, int);
#endif // SCROLLBACK_PATCH
+#endif // REFLOW_PATCH
static void tscrolldown(int, int);
static void tsetattr(const int *, int);
static void tsetchar(Rune, const Glyph *, int, int);
@@ -213,7 +230,9 @@ static int32_t tdefcolor(const int *, int *, int);
static void tdeftran(char);
static void tstrsequence(uchar);
static void selnormalize(void);
+#if !REFLOW_PATCH
static void selscroll(int, int);
+#endif // REFLOW_PATCH
static void selsnap(int *, int *, int);
static size_t utf8decode(const char *, Rune *, size_t);
@@ -421,6 +440,7 @@ selinit(void)
sel.ob.x = -1;
}
+#if !REFLOW_PATCH
int
tlinelen(int y)
{
@@ -442,6 +462,7 @@ tlinelen(int y)
return i;
}
+#endif // REFLOW_PATCH
void
selstart(int col, int row, int snap)
@@ -455,9 +476,9 @@ selstart(int col, int row, int snap)
sel.oe.y = sel.ob.y = row;
selnormalize();
- if (sel.snap != 0)
- sel.mode = SEL_READY;
- tsetdirt(sel.nb.y, sel.ne.y);
+ if (sel.snap != 0)
+ sel.mode = SEL_READY;
+ tsetdirt(sel.nb.y, sel.ne.y);
}
void
@@ -480,8 +501,8 @@ selextend(int col, int row, int type, int done)
sel.oe.x = col;
sel.oe.y = row;
- selnormalize();
sel.type = type;
+ selnormalize();
if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY)
tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
@@ -510,13 +531,23 @@ selnormalize(void)
/* expand selection over line breaks */
if (sel.type == SEL_RECTANGULAR)
return;
+
+ #if REFLOW_PATCH
+ i = tlinelen(TLINE(sel.nb.y));
+ if (sel.nb.x > i)
+ sel.nb.x = i;
+ if (sel.ne.x >= tlinelen(TLINE(sel.ne.y)))
+ sel.ne.x = term.col - 1;
+ #else
i = tlinelen(sel.nb.y);
if (i < sel.nb.x)
sel.nb.x = i;
if (tlinelen(sel.ne.y) <= sel.ne.x)
sel.ne.x = term.col - 1;
+ #endif // REFLOW_PATCH
}
+#if !REFLOW_PATCH
int
selected(int x, int y)
{
@@ -532,7 +563,9 @@ selected(int x, int y)
&& (y != sel.nb.y || x >= sel.nb.x)
&& (y != sel.ne.y || x <= sel.ne.x);
}
+#endif // REFLOW_PATCH
+#if !REFLOW_PATCH
void
selsnap(int *x, int *y, int direction)
{
@@ -625,7 +658,9 @@ selsnap(int *x, int *y, int direction)
break;
}
}
+#endif // REFLOW_PATCH
+#if !REFLOW_PATCH
char *
getsel(void)
{
@@ -662,6 +697,7 @@ getsel(void)
#endif // SCROLLBACK_PATCH
lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
}
+
#if SCROLLBACK_PATCH
last = &TLINE(y)[MIN(lastx, linelen-1)];
#else
@@ -693,15 +729,22 @@ getsel(void)
*ptr = 0;
return str;
}
+#endif // REFLOW_PATCH
void
selclear(void)
{
if (sel.ob.x == -1)
return;
+ selremove();
+ tsetdirt(sel.nb.y, sel.ne.y);
+}
+
+void
+selremove(void)
+{
sel.mode = SEL_IDLE;
sel.ob.x = -1;
- tsetdirt(sel.nb.y, sel.ne.y);
}
void
@@ -950,10 +993,8 @@ void
ttywrite(const char *s, size_t n, int may_echo)
{
const char *next;
- #if SCROLLBACK_PATCH
- Arg arg = (Arg) { .i = term.scr };
-
- kscrolldown(&arg);
+ #if REFLOW_PATCH || SCROLLBACK_PATCH
+ kscrolldown(&((Arg){ .i = term.scr }));
#endif // SCROLLBACK_PATCH
if (may_echo && IS_SET(MODE_ECHO))
@@ -1097,7 +1138,11 @@ tsetdirtattr(int attr)
for (i = 0; i < term.row-1; i++) {
for (j = 0; j < term.col-1; j++) {
if (term.line[i][j].mode & attr) {
+ #if REFLOW_PATCH
+ term.dirty[i] = 1;
+ #else
tsetdirt(i, i);
+ #endif // REFLOW_PATCH
break;
}
}
@@ -1110,7 +1155,12 @@ tfulldirt(void)
#if SYNC_PATCH
tsync_end();
#endif // SYNC_PATCH
+ #if REFLOW_PATCH
+ for (int i = 0; i < term.row; i++)
+ term.dirty[i] = 1;
+ #else
tsetdirt(0, term.row-1);
+ #endif // REFLOW_PATCH
}
void
@@ -1128,18 +1178,21 @@ tcursor(int mode)
}
void
+tresetcursor(void)
+{
+ term.c = (TCursor){ { .mode = ATTR_NULL, .fg = defaultfg, .bg = defaultbg },
+ .x = 0, .y = 0, .state = CURSOR_DEFAULT };
+}
+
+void
treset(void)
{
uint i;
- #if SIXEL_PATCH
- ImageList *im;
- #endif // SIXEL_PATCH
+ #if REFLOW_PATCH
+ int x, y;
+ #endif // REFLOW_PATCH
- term.c = (TCursor){{
- .mode = ATTR_NULL,
- .fg = defaultfg,
- .bg = defaultbg
- }, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
+ tresetcursor();
memset(term.tabs, 0, term.col * sizeof(*term.tabs));
for (i = tabspaces; i < term.col; i += tabspaces)
@@ -1149,8 +1202,20 @@ treset(void)
term.mode = MODE_WRAP|MODE_UTF8;
memset(term.trantbl, CS_USA, sizeof(term.trantbl));
term.charset = 0;
+ #if REFLOW_PATCH
+ term.histf = 0;
+ term.histi = 0;
+ term.scr = 0;
+ selremove();
+ #endif // REFLOW_PATCH
for (i = 0; i < 2; i++) {
+ #if REFLOW_PATCH
+ tcursor(CURSOR_SAVE); /* reset saved cursor */
+ for (y = 0; y < term.row; y++)
+ for (x = 0; x < term.col; x++)
+ tclearglyph(&term.line[y][x], 0);
+ #else
tmoveto(0, 0);
tcursor(CURSOR_SAVE);
#if COLUMNS_PATCH
@@ -1158,13 +1223,18 @@ treset(void)
#else
tclearregion(0, 0, term.col-1, term.row-1);
#endif // COLUMNS_PATCH
+ #endif // REFLOW_PATCH
#if SIXEL_PATCH
tdeleteimages();
#endif // SIXEL_PATCH
tswapscreen();
}
+ #if REFLOW_PATCH
+ tfulldirt();
+ #endif // REFLOW_PATCH
}
+#if !REFLOW_PATCH
void
tnew(int col, int row)
{
@@ -1172,7 +1242,9 @@ tnew(int col, int row)
tresize(col, row);
treset();
}
+#endif // REFLOW_PATCH
+#if !REFLOW_PATCH
void
tswapscreen(void)
{
@@ -1190,7 +1262,9 @@ tswapscreen(void)
term.mode ^= MODE_ALTSCREEN;
tfulldirt();
}
+#endif // REFLOW_PATCH
+#if !REFLOW_PATCH
void
tscrolldown(int orig, int n)
{
@@ -1245,7 +1319,9 @@ tscrolldown(int orig, int n)
selscroll(orig, n);
#endif // SCROLLBACK_PATCH
}
+#endif // REFLOW_PATCH
+#if !REFLOW_PATCH
void
#if SCROLLBACK_PATCH
tscrollup(int orig, int n, int copyhist)
@@ -1350,7 +1426,9 @@ tscrollup(int orig, int n)
selscroll(orig, -n);
#endif // SCROLLBACK_PATCH
}
+#endif // REFLOW_PATCH
+#if !REFLOW_PATCH
void
selscroll(int orig, int n)
{
@@ -1370,6 +1448,7 @@ selscroll(int orig, int n)
}
}
}
+#endif // REFLOW_PATCH
void
tnewline(int first_col)
@@ -1377,7 +1456,9 @@ tnewline(int first_col)
int y = term.c.y;
if (y == term.bot) {
- #if SCROLLBACK_PATCH
+ #if REFLOW_PATCH
+ tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST);
+ #elif SCROLLBACK_PATCH
tscrollup(term.top, 1, 1);
#else
tscrollup(term.top, 1);
@@ -1503,6 +1584,9 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
term.dirty[y] = 1;
term.line[y][x] = *attr;
term.line[y][x].u = u;
+ #if REFLOW_PATCH
+ term.line[y][x].mode |= ATTR_SET;
+ #endif // REFLOW_PATCH
#if BOXDRAW_PATCH
if (isboxdraw(u))
@@ -1510,6 +1594,7 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
#endif // BOXDRAW_PATCH
}
+#if !REFLOW_PATCH
void
tclearregion(int x1, int y1, int x2, int y2)
{
@@ -1544,7 +1629,9 @@ tclearregion(int x1, int y1, int x2, int y2)
}
}
}
+#endif // REFLOW_PATCH
+#if !REFLOW_PATCH
void
tdeletechar(int n)
{
@@ -1561,7 +1648,9 @@ tdeletechar(int n)
memmove(&line[dst], &line[src], size * sizeof(Glyph));
tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
}
+#endif // REFLOW_PATCH
+#if !REFLOW_PATCH
void
tinsertblank(int n)
{
@@ -1578,6 +1667,7 @@ tinsertblank(int n)
memmove(&line[dst], &line[src], size * sizeof(Glyph));
tclearregion(src, term.c.y, dst - 1, term.c.y);
}
+#endif // REFLOW_PATCH
void
tinsertblankline(int n)
@@ -1602,12 +1692,15 @@ tdeleteimages(void)
void
tdeleteline(int n)
{
- if (BETWEEN(term.c.y, term.top, term.bot))
- #if SCROLLBACK_PATCH
+ if (BETWEEN(term.c.y, term.top, term.bot)) {
+ #if REFLOW_PATCH
+ tscrollup(term.c.y, term.bot, n, SCROLL_NOSAVEHIST);
+ #elif SCROLLBACK_PATCH
tscrollup(term.c.y, n, 0);
#else
tscrollup(term.c.y, n);
#endif // SCROLLBACK_PATCH
+ }
}
int32_t
@@ -1906,6 +1999,13 @@ tsetmode(int priv, int set, const int *args, int narg)
case 1047:
if (!allowaltscreen)
break;
+ #if REFLOW_PATCH
+ if (set)
+ tloadaltscreen(*args != 47, *args == 1049);
+ else
+ tloaddefscreen(*args != 47, *args == 1049);
+ break;
+ #else
alt = IS_SET(MODE_ALTSCREEN);
if (alt) {
#if COLUMNS_PATCH
@@ -1919,7 +2019,12 @@ tsetmode(int priv, int set, const int *args, int narg)
if (*args != 1049)
break;
/* FALLTHROUGH */
+ #endif // REFLOW_PATCH
case 1048:
+ #if REFLOW_PATCH
+ if (!allowaltscreen)
+ break;
+ #endif // REFLOW_PATCH
tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
break;
case 2004: /* 2004: bracketed paste mode */
@@ -1979,11 +2084,14 @@ void
csihandle(void)
{
char buffer[40];
- int len;
+ int n = 0, len;
#if SIXEL_PATCH
ImageList *im, *next;
- int n, pi, pa;
+ int pi, pa;
#endif // SIXEL_PATCH
+ #if REFLOW_PATCH
+ int x;
+ #endif // REFLOW_PATCH
#if COLUMNS_PATCH
int maxcol = term.maxcol;
#else
@@ -2086,18 +2194,52 @@ csihandle(void)
case 'J': /* ED -- Clear screen */
switch (csiescseq.arg[0]) {
case 0: /* below */
+ #if REFLOW_PATCH
+ tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 1);
+ if (term.c.y < term.row-1)
+ tclearregion(0, term.c.y+1, term.col-1, term.row-1, 1);
+ #else
tclearregion(term.c.x, term.c.y, maxcol-1, term.c.y);
- if (term.c.y < term.row-1) {
- tclearregion(0, term.c.y+1, maxcol-1,
- term.row-1);
- }
+ if (term.c.y < term.row-1)
+ tclearregion(0, term.c.y+1, maxcol-1, term.row-1);
+ #endif // REFLOW_PATCH
break;
case 1: /* above */
+ #if REFLOW_PATCH
+ if (term.c.y >= 1)
+ tclearregion(0, 0, term.col-1, term.c.y-1, 1);
+ tclearregion(0, term.c.y, term.c.x, term.c.y, 1);
+ #else
if (term.c.y > 1)
tclearregion(0, 0, maxcol-1, term.c.y-1);
tclearregion(0, term.c.y, term.c.x, term.c.y);
+ #endif // REFLOW_PATCH
break;
case 2: /* screen */
+ #if REFLOW_PATCH
+ if (IS_SET(MODE_ALTSCREEN)) {
+ tclearregion(0, 0, term.col-1, term.row-1, 1);
+ #if SIXEL_PATCH
+ tdeleteimages();
+ #endif // SIXEL_PATCH
+ break;
+ }
+ /* vte does this:
+ tscrollup(0, term.row-1, term.row, SCROLL_SAVEHIST); */
+ /* alacritty does this: */
+ #if KEYBOARDSELECT_PATCH
+ for (n = term.row-1; n >= 0 && tlinelen(term.line[n]) == 0; n--)
+ ;
+ #endif // KEYBOARDSELECT_PATCH
+ #if SIXEL_PATCH
+ for (im = term.images; im; im = im->next)
+ n = MAX(im->y - term.scr, n);
+ #endif // SIXEL_PATCH
+ if (n >= 0)
+ tscrollup(0, term.row-1, n+1, SCROLL_SAVEHIST);
+ tscrollup(0, term.row-1, term.row-n-1, SCROLL_NOSAVEHIST);
+ break;
+ #else // !REFLOW_PATCH
#if SCROLLBACK_PATCH
if (!IS_SET(MODE_ALTSCREEN)) {
#if SCROLLBACK_PATCH
@@ -2127,8 +2269,25 @@ csihandle(void)
#if SIXEL_PATCH
tdeleteimages();
#endif // SIXEL_PATCH
+ #endif // REFLOW_PTCH
break;
case 3: /* scrollback */
+ #if REFLOW_PATCH
+ if (IS_SET(MODE_ALTSCREEN))
+ break;
+ kscrolldown(&((Arg){ .i = term.scr }));
+ term.scr = 0;
+ term.histi = 0;
+ term.histf = 0;
+ #if SIXEL_PATCH
+ for (im = term.images; im; im = next) {
+ next = im->next;
+ if (im->y < 0)
+ delete_image(im);
+ }
+ #endif // SIXEL_PATCH
+ break;
+ #else // !REFLOW_PATCH
#if SCROLLBACK_PATCH
if (!IS_SET(MODE_ALTSCREEN)) {
term.scr = 0;
@@ -2149,6 +2308,7 @@ csihandle(void)
}
#endif // SIXEL_PATCH
break;
+ #endif // REFLOW_PATCH
#if SIXEL_PATCH
case 6: /* sixels */
tdeleteimages();
@@ -2161,9 +2321,20 @@ csihandle(void)
break;
case 'K': /* EL -- Clear line */
switch (csiescseq.arg[0]) {
+ #if REFLOW_PATCH
+ case 0: /* right */
+ tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 1);
+ break;
+ case 1: /* left */
+ tclearregion(0, term.c.y, term.c.x, term.c.y, 1);
+ break;
+ case 2: /* all */
+ tclearregion(0, term.c.y, term.col-1, term.c.y, 1);
+ break;
+ }
+ #else
case 0: /* right */
- tclearregion(term.c.x, term.c.y, maxcol-1,
- term.c.y);
+ tclearregion(term.c.x, term.c.y, maxcol-1, term.c.y);
break;
case 1: /* left */
tclearregion(0, term.c.y, term.c.x, term.c.y);
@@ -2172,6 +2343,7 @@ csihandle(void)
tclearregion(0, term.c.y, maxcol-1, term.c.y);
break;
}
+ #endif // REFLOW_PATCH
break;
case 'S': /* SU -- Scroll <n> line up ; XTSMGRAPHICS */
if (csiescseq.priv) {
@@ -2203,7 +2375,10 @@ csihandle(void)
goto unknown;
}
DEFAULT(csiescseq.arg[0], 1);
- #if SIXEL_PATCH && SCROLLBACK_PATCH
+ #if REFLOW_PATCH
+ /* xterm, urxvt, alacritty save this in history */
+ tscrollup(term.top, term.bot, csiescseq.arg[0], SCROLL_SAVEHIST);
+ #elif SIXEL_PATCH && SCROLLBACK_PATCH
tscrollup(term.top, csiescseq.arg[0], 1);
#elif SCROLLBACK_PATCH
tscrollup(term.top, csiescseq.arg[0], 0);
@@ -2227,9 +2402,17 @@ csihandle(void)
tdeleteline(csiescseq.arg[0]);
break;
case 'X': /* ECH -- Erase <n> char */
+ #if REFLOW_PATCH
+ if (csiescseq.arg[0] < 0)
+ return;
+ DEFAULT(csiescseq.arg[0], 1);
+ x = MIN(term.c.x + csiescseq.arg[0], term.col) - 1;
+ tclearregion(term.c.x, term.c.y, x, term.c.y, 1);
+ #else
DEFAULT(csiescseq.arg[0], 1);
tclearregion(term.c.x, term.c.y,
term.c.x + csiescseq.arg[0] - 1, term.c.y);
+ #endif // REFLOW_PATCH
break;
case 'P': /* DCH -- Delete <n> char */
DEFAULT(csiescseq.arg[0], 1);
@@ -2411,7 +2594,7 @@ strhandle(void)
int i, x, y, x1, y1, x2, y2, numimages;
int cx, cy;
Line line;
- #if SCROLLBACK_PATCH
+ #if SCROLLBACK_PATCH || REFLOW_PATCH
int scr = IS_SET(MODE_ALTSCREEN) ? 0 : term.scr;
#else
int scr = 0;
@@ -2551,7 +2734,7 @@ strhandle(void)
x2 = MIN(x2, term.col);
for (i = 0, im = newimages; im; im = next, i++) {
next = im->next;
- #if SCROLLBACK_PATCH
+ #if SCROLLBACK_PATCH || REFLOW_PATCH
scr = IS_SET(MODE_ALTSCREEN) ? 0 : term.scr;
#endif // SCROLLBACK_PATCH
if (IS_SET(MODE_SIXEL_SDM)) {
@@ -2704,6 +2887,7 @@ tdumpsel(void)
}
}
+#if !REFLOW_PATCH
void
tdumpline(int n)
{
@@ -2718,6 +2902,7 @@ tdumpline(int n)
}
tprinter("\n", 1);
}
+#endif // REFLOW_PATCH
void
tdump(void)
@@ -2994,7 +3179,9 @@ eschandle(uchar ascii)
return 0;
case 'D': /* IND -- Linefeed */
if (term.c.y == term.bot) {
- #if SCROLLBACK_PATCH
+ #if REFLOW_PATCH
+ tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST);
+ #elif SCROLLBACK_PATCH
tscrollup(term.top, 1, 1);
#else
tscrollup(term.top, 1);
@@ -3027,7 +3214,7 @@ eschandle(uchar ascii)
resettitle();
xloadcols();
xsetmode(0, MODE_HIDE);
- #if SCROLLBACK_PATCH
+ #if SCROLLBACK_PATCH && !REFLOW_PATCH
if (!IS_SET(MODE_ALTSCREEN)) {
term.scr = 0;
term.histi = 0;
@@ -3189,8 +3376,14 @@ check_control_code:
return;
}
+ #if REFLOW_PATCH
+ /* selected() takes relative coordinates */
+ if (selected(term.c.x + term.scr, term.c.y + term.scr))
+ selclear();
+ #else
if (selected(term.c.x, term.c.y))
selclear();
+ #endif // REFLOW_PATCH
gp = &term.line[term.c.y][term.c.x];
if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
@@ -3229,6 +3422,9 @@ check_control_code:
if (term.c.x+width < term.col) {
tmoveto(term.c.x+width, term.c.y);
} else {
+ #if REFLOW_PATCH
+ term.wrapcwidth[IS_SET(MODE_ALTSCREEN)] = width;
+ #endif // REFLOW_PATCH
term.c.state |= CURSOR_WRAPNEXT;
}
}
@@ -3284,6 +3480,7 @@ twrite(const char *buf, int buflen, int show_ctrl)
return n;
}
+#if !REFLOW_PATCH
void
tresize(int col, int row)
{
@@ -3446,6 +3643,7 @@ tresize(int col, int row)
}
#endif // SIXEL_PATCH
}
+#endif // REFLOW_PATCH
void
resettitle(void)
@@ -3467,7 +3665,7 @@ drawregion(int x1, int y1, int x2, int y2)
continue;
term.dirty[y] = 0;
- #if SCROLLBACK_PATCH
+ #if SCROLLBACK_PATCH || REFLOW_PATCH
xdrawline(TLINE(y), x1, y, x2);
#else
xdrawline(term.line[y], x1, y, x2);
@@ -3495,6 +3693,10 @@ draw(void)
drawregion(0, 0, term.col, term.row);
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ if (!kbds_drawcursor()) {
+ #endif // KEYBOARDSELECT_PATCH
+
#if SCROLLBACK_PATCH
if (term.scr == 0)
#endif // SCROLLBACK_PATCH
@@ -3506,6 +3708,9 @@ draw(void)
xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
#endif // LIGATURES_PATCH
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ }
+ #endif // KEYBOARDSELECT_PATCH
term.ocx = cx;
term.ocy = term.c.y;
xfinishdraw();
diff --git a/st.h b/st.h
@@ -33,39 +33,43 @@
#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
#define IS_TRUECOL(x) (1 << 24 & (x))
-#if SCROLLBACK_PATCH
+#if SCROLLBACK_PATCH || REFLOW_PATCH
#define HISTSIZE 2000
-#endif // SCROLLBACK_PATCH
+#endif // SCROLLBACK_PATCH | REFLOW_PATCH
enum glyph_attribute {
- ATTR_NULL = 0,
- ATTR_BOLD = 1 << 0,
- ATTR_FAINT = 1 << 1,
- ATTR_ITALIC = 1 << 2,
- ATTR_UNDERLINE = 1 << 3,
- ATTR_BLINK = 1 << 4,
- ATTR_REVERSE = 1 << 5,
- ATTR_INVISIBLE = 1 << 6,
- ATTR_STRUCK = 1 << 7,
- ATTR_WRAP = 1 << 8,
- ATTR_WIDE = 1 << 9,
- ATTR_WDUMMY = 1 << 10,
+ ATTR_NULL = 0,
+ ATTR_SET = 1 << 0,
+ ATTR_BOLD = 1 << 1,
+ ATTR_FAINT = 1 << 2,
+ ATTR_ITALIC = 1 << 3,
+ ATTR_UNDERLINE = 1 << 4,
+ ATTR_BLINK = 1 << 5,
+ ATTR_REVERSE = 1 << 6,
+ ATTR_INVISIBLE = 1 << 7,
+ ATTR_STRUCK = 1 << 8,
+ ATTR_WRAP = 1 << 9,
+ ATTR_WIDE = 1 << 10,
+ ATTR_WDUMMY = 1 << 11,
+ #if SELECTION_COLORS_PATCH
+ ATTR_SELECTED = 1 << 12,
+ #endif // SELECTION_COLORS_PATCH | REFLOW_PATCH
#if BOXDRAW_PATCH
- ATTR_BOXDRAW = 1 << 11,
+ ATTR_BOXDRAW = 1 << 13,
#endif // BOXDRAW_PATCH
+ #if UNDERCURL_PATCH
+ ATTR_DIRTYUNDERLINE = 1 << 14,
+ #endif // UNDERCURL_PATCH
#if LIGATURES_PATCH
- ATTR_LIGA = 1 << 12,
+ ATTR_LIGA = 1 << 15,
#endif // LIGATURES_PATCH
#if SIXEL_PATCH
- ATTR_SIXEL = 1 << 13,
+ ATTR_SIXEL = 1 << 16,
#endif // SIXEL_PATCH
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ ATTR_HIGHLIGHT = 1 << 17,
+ #endif // KEYBOARDSELECT_PATCH
ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
- #if SELECTION_COLORS_PATCH
- ATTR_SELECTED = 1 << 14,
- #endif // SELECTION_COLORS_PATCH
- #if UNDERCURL_PATCH
- ATTR_DIRTYUNDERLINE = 1 << 15,
- #endif // UNDERCURL_PATCH
};
#if SIXEL_PATCH
@@ -77,6 +81,9 @@ typedef struct _ImageList {
int height;
int x;
int y;
+ #if REFLOW_PATCH
+ int reflow_y;
+ #endif // REFLOW_PATCH
int cols;
int cw;
int ch;
@@ -128,7 +135,7 @@ typedef XftGlyphFontSpec GlyphFontSpec;
#define Glyph Glyph_
typedef struct {
Rune u; /* character code */
- ushort mode; /* attribute flags */
+ uint32_t mode; /* attribute flags */
uint32_t fg; /* foreground */
uint32_t bg; /* background */
#if UNDERCURL_PATCH
@@ -164,12 +171,18 @@ typedef struct {
#endif // COLUMNS_PATCH
Line *line; /* screen */
Line *alt; /* alternate screen */
- #if SCROLLBACK_PATCH
+ #if REFLOW_PATCH
+ Line hist[HISTSIZE]; /* history buffer */
+ int histi; /* history index */
+ int histf; /* nb history available */
+ int scr; /* scroll back */
+ int wrapcwidth[2]; /* used in updating WRAPNEXT when resizing */
+ #elif SCROLLBACK_PATCH
Line hist[HISTSIZE]; /* history buffer */
int histi; /* history index */
int histn; /* number of history entries */
int scr; /* scroll back */
- #endif // SCROLLBACK_PATCH
+ #endif // SCROLLBACK_PATCH | REFLOW_PATCH
int *dirty; /* dirtyness of lines */
TCursor c; /* cursor */
int ocx; /* old cursor col */
@@ -347,6 +360,7 @@ void resettitle(void);
void selclear(void);
void selinit(void);
+void selremove(void);
void selstart(int, int, int);
void selextend(int, int, int, int);
int selected(int, int);
@@ -376,6 +390,10 @@ extern char *scroll;
extern char *stty_args;
extern char *vtiden;
extern wchar_t *worddelimiters;
+#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+extern wchar_t *kbds_sdelim;
+extern wchar_t *kbds_ldelim;
+#endif // KEYBOARDSELECT_PATCH
extern int allowaltscreen;
extern int allowwindowops;
extern char *termname;
diff --git a/win.h b/win.h
@@ -52,4 +52,7 @@ void xsetpointermotion(int);
void xsetsel(char *);
int xstartdraw(void);
void xximspot(int, int);
-void xclearwin(void);
-\ No newline at end of file
+void xclearwin(void);
+#if REFLOW_PATCH && KEYBOARDSELECT_PATCH
+void xdrawglyph(Glyph, int, int);
+#endif // KEYBOARDSELECT_PATCH
+\ No newline at end of file
diff --git a/x.c b/x.c
@@ -90,7 +90,7 @@ static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
#if LIGATURES_PATCH
static inline void xresetfontsettings(uint32_t mode, Font **font, int *frcflags);
#endif // LIGATURES_PATCH
-static void xdrawglyph(Glyph, int, int);
+void xdrawglyph(Glyph, int, int);
static void xclear(int, int, int, int);
static int xgeommasktogravity(int);
static int ximopen(Display *);
@@ -259,6 +259,11 @@ clippaste(const Arg *dummy)
{
Atom clipboard;
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ if (IS_SET(MODE_KBDSELECT) && !kbds_issearchmode())
+ return;
+ #endif // KEYBOARDSELECT_PATCH
+
clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard,
xw.win, CurrentTime);
@@ -273,6 +278,11 @@ numlock(const Arg *dummy)
void
selpaste(const Arg *dummy)
{
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ if (IS_SET(MODE_KBDSELECT) && !kbds_issearchmode())
+ return;
+ #endif // KEYBOARDSELECT_PATCH
+
XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY,
xw.win, CurrentTime);
}
@@ -399,6 +409,11 @@ mousesel(XEvent *e, int done)
int type, seltype = SEL_REGULAR;
uint state = e->xbutton.state & ~(Button1Mask | forcemousemod);
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ if (kbds_isselectmode())
+ return;
+ #endif // KEYBOARDSELECT_PATCH
+
for (type = 1; type < LEN(selmasks); ++type) {
if (match(selmasks[type], state)) {
seltype = type;
@@ -517,6 +532,11 @@ bpress(XEvent *e)
xsel.tclick2 = xsel.tclick1;
xsel.tclick1 = now;
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ if (kbds_isselectmode())
+ return;
+ #endif // KEYBOARDSELECT_PATCH
+
selstart(evcol(e), evrow(e), snap);
#if OPENURLONCLICK_PATCH
@@ -555,6 +575,9 @@ selnotify(XEvent *e)
int format;
uchar *data, *last, *repl;
Atom type, incratom, property = None;
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ int append = 0;
+ #endif // KEYBOARDSELECT_PATCH
incratom = XInternAtom(xw.dpy, "INCR", 0);
@@ -616,6 +639,30 @@ selnotify(XEvent *e)
continue;
}
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ if (IS_SET(MODE_KBDSELECT) && kbds_issearchmode()) {
+ kbds_pasteintosearch(data, nitems * format / 8, append++);
+ } else {
+ /*
+ * As seen in getsel:
+ * Line endings are inconsistent in the terminal and GUI world
+ * copy and pasting. When receiving some selection data,
+ * replace all '\n' with '\r'.
+ * FIXME: Fix the computer world.
+ */
+ repl = data;
+ last = data + nitems * format / 8;
+ while ((repl = memchr(repl, '\n', last - repl))) {
+ *repl++ = '\r';
+ }
+
+ if (IS_SET(MODE_BRCKTPASTE) && ofs == 0)
+ ttywrite("\033[200~", 6, 0);
+ ttywrite((char *)data, nitems * format / 8, 1);
+ if (IS_SET(MODE_BRCKTPASTE) && rem == 0)
+ ttywrite("\033[201~", 6, 0);
+ }
+ #else
/*
* As seen in getsel:
* Line endings are inconsistent in the terminal and GUI world
@@ -634,6 +681,7 @@ selnotify(XEvent *e)
ttywrite((char *)data, nitems * format / 8, 1);
if (IS_SET(MODE_BRCKTPASTE) && rem == 0)
ttywrite("\033[201~", 6, 0);
+ #endif // KEYBOARDSELECT_PATCH
XFree(data);
/* number of 32-bit chunks returned */
ofs += nitems * format / 32;
@@ -854,11 +902,11 @@ xresize(int col, int row)
#if !SINGLE_DRAWABLE_BUFFER_PATCH
XFreePixmap(xw.dpy, xw.buf);
xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
- #if ALPHA_PATCH
- xw.depth
- #else
- DefaultDepth(xw.dpy, xw.scr)
- #endif // ALPHA_PATCH
+ #if ALPHA_PATCH
+ xw.depth
+ #else
+ DefaultDepth(xw.dpy, xw.scr)
+ #endif // ALPHA_PATCH
);
XftDrawChange(xw.draw, xw.buf);
#endif // SINGLE_DRAWABLE_BUFFER_PATCH
@@ -2064,6 +2112,13 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
}
#endif // INVERT_PATCH
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ if (base.mode & ATTR_HIGHLIGHT) {
+ fg = &dc.col[(base.mode & ATTR_REVERSE) ? highlightbg : highlightfg];
+ bg = &dc.col[(base.mode & ATTR_REVERSE) ? highlightfg : highlightbg];
+ }
+ #endif // KEYBOARDSELECT_PATCH
+
#if ALPHA_PATCH && ALPHA_GRADIENT_PATCH
// gradient
bg->color.alpha = grad_alpha * 0xffff * (win.h - y*win.ch) / win.h + stat_alpha * 0xffff;
@@ -2614,6 +2669,9 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
#if DYNAMIC_CURSOR_COLOR_PATCH
|ATTR_REVERSE
#endif // DYNAMIC_CURSOR_COLOR_PATCH
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ |ATTR_HIGHLIGHT
+ #endif // KEYBOARDSELECT_PATCH
;
if (IS_SET(MODE_REVERSE)) {
@@ -2638,7 +2696,9 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
drawcol = dc.col[defaultcs];
#else
if (selected(cx, cy)) {
- #if DYNAMIC_CURSOR_COLOR_PATCH
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ g.mode &= ~(ATTR_REVERSE | ATTR_HIGHLIGHT);
+ #elif DYNAMIC_CURSOR_COLOR_PATCH
g.mode &= ~ATTR_REVERSE;
#endif // DYNAMIC_CURSOR_COLOR_PATCH
g.fg = defaultfg;
@@ -2669,6 +2729,11 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
#endif // SELECTION_COLORS_PATCH
}
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ if (g.mode & ATTR_HIGHLIGHT)
+ g.mode ^= ATTR_REVERSE;
+ #endif // KEYBOARDSELECT_PATCH
+
/* draw the new one */
if (IS_SET(MODE_FOCUSED)) {
switch (win.cursor) {
@@ -2921,6 +2986,10 @@ xdrawline(Line line, int x1, int y1, int x2)
xdrawglyphfontspecs(specs, seq[i].base, seq[i].numspecs, seq[i].ox, y1, DRAW_FG, seq[i].charlen);
specs += seq[i].numspecs;
}
+
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ kbds_drawstatusbar(y1);
+ #endif // KEYBOARDSELECT_PATCH
}
#elif LIGATURES_PATCH
void
@@ -2957,6 +3026,10 @@ xdrawline(Line line, int x1, int y1, int x2)
numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1);
xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
}
+
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ kbds_drawstatusbar(y1);
+ #endif // KEYBOARDSELECT_PATCH
}
#elif WIDE_GLYPHS_PATCH
void
@@ -2999,6 +3072,10 @@ xdrawline(Line line, int x1, int y1, int x2)
if (i > 0)
xdrawglyphfontspecs(specs, base, i, ox, y1, dmode);
}
+
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ kbds_drawstatusbar(y1);
+ #endif // KEYBOARDSELECT_PATCH
}
#else // !WIDE_GLYPHS_PATCH and !LIGATURES_PATCH
void
@@ -3035,6 +3112,10 @@ xdrawline(Line line, int x1, int y1, int x2)
}
if (i > 0)
xdrawglyphfontspecs(specs, base, i, ox, y1);
+
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ kbds_drawstatusbar(y1);
+ #endif // KEYBOARDSELECT_PATCH
}
#endif // WIDE_GLYPHS_PATCH | LIGATURES_PATCH
@@ -3130,11 +3211,11 @@ xfinishdraw(void)
width = MIN(width, (x2 - im->x) * win.cw);
/* delete the image if the text cells behind it have been changed */
- #if SCROLLBACK_PATCH
+ #if SCROLLBACK_PATCH || REFLOW_PATCH
line = TLINE(im->y);
#else
line = term.line[im->y];
- #endif // SCROLLBACK_PATCH
+ #endif // SCROLLBACK_PATCH | REFLOW_PATCH
for (del = 0, x = im->x; x < x2; x++) {
if ((del = !(line[x].mode & ATTR_SIXEL)))
break;
@@ -3431,7 +3512,25 @@ kpress(XEvent *ev)
} else {
len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
}
- #if KEYBOARDSELECT_PATCH
+
+ #if KEYBOARDSELECT_PATCH && REFLOW_PATCH
+ if (IS_SET(MODE_KBDSELECT) ) {
+ if (kbds_issearchmode()) {
+ for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
+ if (ksym == bp->keysym && match(bp->mod, e->state) &&
+ (!bp->screen || bp->screen == screen) &&
+ (bp->func == clippaste || bp->func == selpaste)) {
+ bp->func(&(bp->arg));
+ return;
+ }
+ }
+ }
+ if (match(XK_NO_MOD, e->state) ||
+ (XK_Shift_L | XK_Shift_R) & e->state )
+ win.mode ^= kbds_keyboardhandler(ksym, buf, len, 0);
+ return;
+ }
+ #elif KEYBOARDSELECT_PATCH
if ( IS_SET(MODE_KBDSELECT) ) {
if ( match(XK_NO_MOD, e->state) ||
(XK_Shift_L | XK_Shift_R) & e->state )