From 3b87b07404bb39bba6f22a88d7fb31b8f0f587a0 Mon Sep 17 00:00:00 2001 From: Stein Gunnar Bakkeby Date: Wed, 13 Mar 2024 10:33:51 +0100 Subject: [PATCH] Adding reflow patch (#120) --- README.md | 6 + config.def.h | 21 +- patch/keyboardselect_reflow.txt | 29 + patch/keyboardselect_reflow_st.c | 769 +++++++++++++++++++++++++ patch/keyboardselect_reflow_st.h | 6 + patch/keyboardselect_reflow_x.c | 16 + patch/keyboardselect_reflow_x.h | 3 + patch/keyboardselect_x.c | 4 +- patch/openurlonclick.c | 99 +++- patch/reflow.c | 931 +++++++++++++++++++++++++++++++ patch/reflow.h | 44 ++ patch/st_include.c | 8 +- patch/st_include.h | 8 +- patch/x_include.c | 4 +- patch/x_include.h | 5 +- patches.def.h | 7 + sixel.c | 2 +- st.c | 273 +++++++-- st.h | 70 ++- win.h | 5 +- x.c | 119 +++- 21 files changed, 2347 insertions(+), 82 deletions(-) create mode 100644 patch/keyboardselect_reflow.txt create mode 100644 patch/keyboardselect_reflow_st.c create mode 100644 patch/keyboardselect_reflow_st.h create mode 100644 patch/keyboardselect_reflow_x.c create mode 100644 patch/keyboardselect_reflow_x.h create mode 100644 patch/reflow.c create mode 100644 patch/reflow.h diff --git a/README.md b/README.md index 6a30338..d6a27a5 100644 --- 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 index b8fe86e..8fda770 100644 --- 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 new file mode 100644 index 0000000..8dd6c49 --- /dev/null +++ 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 new file mode 100644 index 0000000..b532557 --- /dev/null +++ b/patch/keyboardselect_reflow_st.c @@ -0,0 +1,769 @@ +#include + +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 new file mode 100644 index 0000000..c840af8 --- /dev/null +++ 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 new file mode 100644 index 0000000..c8a39a2 --- /dev/null +++ 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 new file mode 100644 index 0000000..6893013 --- /dev/null +++ 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 index 16af3e1..44cbe64 100644 --- 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 index 078550a..54b1a02 100644 --- 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 new file mode 100644 index 0000000..92b67f1 --- /dev/null +++ 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 new file mode 100644 index 0000000..ec28375 --- /dev/null +++ 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 index 3a8c3c1..20c2386 100644 --- 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 index 2572987..acc2975 100644 --- 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 index 0395eb5..f891756 100644 --- 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 index cbfd6d4..7c4af1c 100644 --- 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 index 98a271c..45526c1 100644 --- 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 index b773883..de526c5 100644 --- 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 index f01287c..bc1abf7 100644 --- 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 @@ -1127,19 +1177,22 @@ 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, maxcol-1, - term.c.y); + 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); 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 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 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 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 index 2b8095f..2bc959c 100644 --- 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 index 709e4e1..c7449ea 100644 --- 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 index 2c43e81..639f8d5 100644 --- 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 )