Adding reflow patch (#120)

This commit is contained in:
Stein Gunnar Bakkeby 2024-03-13 10:33:51 +01:00 committed by GitHub
parent 9a41526bfb
commit 3b87b07404
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 2347 additions and 82 deletions

View File

@ -15,6 +15,8 @@ Refer to [https://st.suckless.org/](https://st.suckless.org/) for details on the
### Changelog: ### Changelog:
2024-03-09 - Added the reflow patch
2024-03-07 - Improved sixel support, removed VIM browse patch 2024-03-07 - Improved sixel support, removed VIM browse patch
2022-10-24 - Added the fullscreen 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~ ~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~ [~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/) - [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 - allows you to specify a border that is relative in size to the width of a cell in the
terminal terminal

View File

@ -73,6 +73,12 @@ static float chscale = 1.0;
*/ */
wchar_t *worddelimiters = L" "; 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) */ /* selection timeouts (in milliseconds) */
static unsigned int doubleclicktimeout = 300; static unsigned int doubleclicktimeout = 300;
static unsigned int tripleclicktimeout = 600; 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 :) */ /* Else if 1 keep original foreground-color of each cell => more colors :) */
static int ignoreselfg = 1; static int ignoreselfg = 1;
#endif // SELECTION_COLORS_PATCH #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 #if BLINKING_CURSOR_PATCH
/* /*
@ -321,6 +332,10 @@ ResourcePref resources[] = {
#if ALPHA_FOCUS_HIGHLIGHT_PATCH #if ALPHA_FOCUS_HIGHLIGHT_PATCH
{ "alphaUnfocused",FLOAT, &alphaUnfocused }, { "alphaUnfocused",FLOAT, &alphaUnfocused },
#endif // ALPHA_FOCUS_HIGHLIGHT_PATCH #endif // ALPHA_FOCUS_HIGHLIGHT_PATCH
#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
{ "highlightfg", INTEGER, &highlightfg },
{ "highlightbg", INTEGER, &highlightbg },
#endif // KEYBOARDSELECT_PATCH
}; };
#endif // XRESOURCES_PATCH #endif // XRESOURCES_PATCH
@ -352,7 +367,7 @@ static MouseShortcut mshortcuts[] = {
{ ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} },
{ ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} }, { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} },
#endif // SCROLLBACK_MOUSE_PATCH #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, Button4, kscrollup, {.i = 1}, 0, S_PRI },
{ XK_NO_MOD, Button5, kscrolldown, {.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 }, { XK_ANY_MOD, Button4, ttysend, {.s = "\031"}, 0, S_ALT },
@ -432,6 +447,10 @@ static Shortcut shortcuts[] = {
#if KEYBOARDSELECT_PATCH #if KEYBOARDSELECT_PATCH
{ TERMMOD, XK_Escape, keyboard_select, { 0 } }, { TERMMOD, XK_Escape, keyboard_select, { 0 } },
#endif // KEYBOARDSELECT_PATCH #endif // KEYBOARDSELECT_PATCH
#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
{ TERMMOD, XK_F, searchforward, { 0 } },
{ TERMMOD, XK_B, searchbackward, { 0 } },
#endif // KEYBOARDSELECT_PATCH
#if ISO14755_PATCH #if ISO14755_PATCH
{ TERMMOD, XK_I, iso14755, {.i = 0} }, { TERMMOD, XK_I, iso14755, {.i = 0} },
#endif // ISO14755_PATCH #endif // ISO14755_PATCH

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -0,0 +1,3 @@
void keyboard_select(const Arg *);
void searchforward(const Arg *);
void searchbackward(const Arg *);

View File

@ -1,8 +1,10 @@
#if !REFLOW_PATCH
#if SCROLLBACK_PATCH #if SCROLLBACK_PATCH
#define TLINEURL(y) TLINE(y) #define TLINEURL(y) TLINE(y)
#else #else
#define TLINEURL(y) term.line[y] #define TLINEURL(y) term.line[y]
#endif // SCROLLBACK_PATCH #endif // SCROLLBACK_PATCH
#endif // REFLOW_PATCH
int url_x1, url_y1, url_x2, url_y2 = -1; int url_x1, url_y1, url_x2, url_y2 = -1;
int url_draw, url_click, url_maxcol; int url_draw, url_click, url_maxcol;
@ -20,6 +22,20 @@ isvalidurlchar(Rune u)
} }
/* find the end of the wrapped line */ /* 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 static int
findeowl(int row) findeowl(int row)
{ {
@ -35,6 +51,7 @@ findeowl(int row)
} while (TLINEURL(row)[col].u == ' ' && --col >= 0); } while (TLINEURL(row)[col].u == ' ' && --col >= 0);
return -1; return -1;
} }
#endif // REFLOW_PATCH
void void
clearurl(void) clearurl(void)
@ -44,6 +61,84 @@ clearurl(void)
url_y2 = -1; 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 * char *
detecturl(int col, int row, int draw) detecturl(int col, int row, int draw)
{ {
@ -52,6 +147,7 @@ detecturl(int col, int row, int draw)
int row_start = row; int row_start = row;
int col_start = col; int col_start = col;
int i = sizeof(url)/2+1, j = sizeof(url)/2; int i = sizeof(url)/2+1, j = sizeof(url)/2;
#if SCROLLBACK_PATCH #if SCROLLBACK_PATCH
int minrow = term.scr - term.histn, maxrow = term.scr + term.row - 1; 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 */ /* 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; minrow = 0, maxrow = term.row - 1;
#else #else
int minrow = 0, maxrow = term.row - 1; int minrow = 0, maxrow = term.row - 1;
#endif // scrollback_patch #endif // SCROLLBACK_PATCH
url_maxcol = 0; url_maxcol = 0;
/* clear previously underlined url */ /* clear previously underlined url */
@ -119,6 +215,7 @@ detecturl(int col, int row, int draw)
return &url[i]; return &url[i];
} }
#endif // REFLOW_PATCH
void void
openUrlOnClick(int col, int row, char* url_opener) openUrlOnClick(int col, int row, char* url_opener)

931
patch/reflow.c Normal file
View File

@ -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]));
}

44
patch/reflow.h Normal file
View File

@ -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[];

View File

@ -8,7 +8,9 @@
#if ISO14755_PATCH #if ISO14755_PATCH
#include "iso14755.c" #include "iso14755.c"
#endif #endif
#if KEYBOARDSELECT_PATCH #if REFLOW_PATCH && KEYBOARDSELECT_PATCH
#include "keyboardselect_reflow_st.c"
#elif KEYBOARDSELECT_PATCH
#include "keyboardselect_st.c" #include "keyboardselect_st.c"
#endif #endif
#if RIGHTCLICKTOPLUMB_PATCH #if RIGHTCLICKTOPLUMB_PATCH
@ -17,7 +19,9 @@
#if NEWTERM_PATCH #if NEWTERM_PATCH
#include "newterm.c" #include "newterm.c"
#endif #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" #include "scrollback.c"
#endif #endif
#if SYNC_PATCH #if SYNC_PATCH

View File

@ -8,7 +8,9 @@
#if ISO14755_PATCH #if ISO14755_PATCH
#include "iso14755.h" #include "iso14755.h"
#endif #endif
#if KEYBOARDSELECT_PATCH #if REFLOW_PATCH && KEYBOARDSELECT_PATCH
#include "keyboardselect_reflow_st.h"
#elif KEYBOARDSELECT_PATCH
#include "keyboardselect_st.h" #include "keyboardselect_st.h"
#endif #endif
#if OPENURLONCLICK_PATCH #if OPENURLONCLICK_PATCH
@ -20,7 +22,9 @@
#if NEWTERM_PATCH #if NEWTERM_PATCH
#include "newterm.h" #include "newterm.h"
#endif #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" #include "scrollback.h"
#endif #endif
#if SYNC_PATCH #if SYNC_PATCH

View File

@ -23,7 +23,9 @@
#if INVERT_PATCH #if INVERT_PATCH
#include "invert.c" #include "invert.c"
#endif #endif
#if KEYBOARDSELECT_PATCH #if REFLOW_PATCH && KEYBOARDSELECT_PATCH
#include "keyboardselect_reflow_x.c"
#elif KEYBOARDSELECT_PATCH
#include "keyboardselect_x.c" #include "keyboardselect_x.c"
#endif #endif
#if OPENURLONCLICK_PATCH #if OPENURLONCLICK_PATCH

View File

@ -20,7 +20,10 @@
#if INVERT_PATCH #if INVERT_PATCH
#include "invert.h" #include "invert.h"
#endif #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" #include "keyboardselect_x.h"
#endif #endif
#if NETWMICON_PATCH #if NETWMICON_PATCH

View File

@ -264,6 +264,13 @@
*/ */
#define OPENURLONCLICK_PATCH 0 #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 /* This patch allows you to specify a border that is relative in size to the width of a cell
* in the terminal. * in the terminal.
* https://st.suckless.org/patches/relativeborder/ * https://st.suckless.org/patches/relativeborder/

View File

@ -36,7 +36,7 @@ static sixel_color_t const sixel_default_color_table[] = {
void void
scroll_images(int n) { scroll_images(int n) {
ImageList *im, *next; ImageList *im, *next;
#if SCROLLBACK_PATCH #if SCROLLBACK_PATCH || REFLOW_PATCH
int top = tisaltscr() ? 0 : term.scr - HISTSIZE; int top = tisaltscr() ? 0 : term.scr - HISTSIZE;
#else #else
int top = 0; int top = 0;

267
st.c
View File

@ -73,6 +73,14 @@ enum term_mode {
#endif // SIXEL_PATCH #endif // SIXEL_PATCH
}; };
#if REFLOW_PATCH
enum scroll_mode {
SCROLL_RESIZE = -1,
SCROLL_NOSAVEHIST = 0,
SCROLL_SAVEHIST = 1
};
#endif // REFLOW_PATCH
enum cursor_movement { enum cursor_movement {
CURSOR_SAVE, CURSOR_SAVE,
CURSOR_LOAD CURSOR_LOAD
@ -177,27 +185,36 @@ static void tprinter(char *, size_t);
static void tdumpsel(void); static void tdumpsel(void);
static void tdumpline(int); static void tdumpline(int);
static void tdump(void); static void tdump(void);
#if !REFLOW_PATCH
static void tclearregion(int, int, int, int); static void tclearregion(int, int, int, int);
#endif // REFLOW_PATCH
static void tcursor(int); static void tcursor(int);
static void tresetcursor(void);
#if !REFLOW_PATCH
static void tdeletechar(int); static void tdeletechar(int);
#endif // REFLOW_PATCH
#if SIXEL_PATCH #if SIXEL_PATCH
static void tdeleteimages(void); static void tdeleteimages(void);
#endif // SIXEL_PATCH #endif // SIXEL_PATCH
static void tdeleteline(int); static void tdeleteline(int);
static void tinsertblank(int); static void tinsertblank(int);
static void tinsertblankline(int); static void tinsertblankline(int);
#if !REFLOW_PATCH
static int tlinelen(int); static int tlinelen(int);
#endif // REFLOW_PATCH
static void tmoveto(int, int); static void tmoveto(int, int);
static void tmoveato(int, int); static void tmoveato(int, int);
static void tnewline(int); static void tnewline(int);
static void tputtab(int); static void tputtab(int);
static void tputc(Rune); static void tputc(Rune);
static void treset(void); static void treset(void);
#if !REFLOW_PATCH
#if SCROLLBACK_PATCH #if SCROLLBACK_PATCH
static void tscrollup(int, int, int); static void tscrollup(int, int, int);
#else #else
static void tscrollup(int, int); static void tscrollup(int, int);
#endif // SCROLLBACK_PATCH #endif // SCROLLBACK_PATCH
#endif // REFLOW_PATCH
static void tscrolldown(int, int); static void tscrolldown(int, int);
static void tsetattr(const int *, int); static void tsetattr(const int *, int);
static void tsetchar(Rune, const Glyph *, 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 tdeftran(char);
static void tstrsequence(uchar); static void tstrsequence(uchar);
static void selnormalize(void); static void selnormalize(void);
#if !REFLOW_PATCH
static void selscroll(int, int); static void selscroll(int, int);
#endif // REFLOW_PATCH
static void selsnap(int *, int *, int); static void selsnap(int *, int *, int);
static size_t utf8decode(const char *, Rune *, size_t); static size_t utf8decode(const char *, Rune *, size_t);
@ -421,6 +440,7 @@ selinit(void)
sel.ob.x = -1; sel.ob.x = -1;
} }
#if !REFLOW_PATCH
int int
tlinelen(int y) tlinelen(int y)
{ {
@ -442,6 +462,7 @@ tlinelen(int y)
return i; return i;
} }
#endif // REFLOW_PATCH
void void
selstart(int col, int row, int snap) selstart(int col, int row, int snap)
@ -480,8 +501,8 @@ selextend(int col, int row, int type, int done)
sel.oe.x = col; sel.oe.x = col;
sel.oe.y = row; sel.oe.y = row;
selnormalize();
sel.type = type; sel.type = type;
selnormalize();
if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY)
tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
@ -510,13 +531,23 @@ selnormalize(void)
/* expand selection over line breaks */ /* expand selection over line breaks */
if (sel.type == SEL_RECTANGULAR) if (sel.type == SEL_RECTANGULAR)
return; 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); i = tlinelen(sel.nb.y);
if (i < sel.nb.x) if (i < sel.nb.x)
sel.nb.x = i; sel.nb.x = i;
if (tlinelen(sel.ne.y) <= sel.ne.x) if (tlinelen(sel.ne.y) <= sel.ne.x)
sel.ne.x = term.col - 1; sel.ne.x = term.col - 1;
#endif // REFLOW_PATCH
} }
#if !REFLOW_PATCH
int int
selected(int x, int y) selected(int x, int y)
{ {
@ -532,7 +563,9 @@ selected(int x, int y)
&& (y != sel.nb.y || x >= sel.nb.x) && (y != sel.nb.y || x >= sel.nb.x)
&& (y != sel.ne.y || x <= sel.ne.x); && (y != sel.ne.y || x <= sel.ne.x);
} }
#endif // REFLOW_PATCH
#if !REFLOW_PATCH
void void
selsnap(int *x, int *y, int direction) selsnap(int *x, int *y, int direction)
{ {
@ -625,7 +658,9 @@ selsnap(int *x, int *y, int direction)
break; break;
} }
} }
#endif // REFLOW_PATCH
#if !REFLOW_PATCH
char * char *
getsel(void) getsel(void)
{ {
@ -662,6 +697,7 @@ getsel(void)
#endif // SCROLLBACK_PATCH #endif // SCROLLBACK_PATCH
lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
} }
#if SCROLLBACK_PATCH #if SCROLLBACK_PATCH
last = &TLINE(y)[MIN(lastx, linelen-1)]; last = &TLINE(y)[MIN(lastx, linelen-1)];
#else #else
@ -693,15 +729,22 @@ getsel(void)
*ptr = 0; *ptr = 0;
return str; return str;
} }
#endif // REFLOW_PATCH
void void
selclear(void) selclear(void)
{ {
if (sel.ob.x == -1) if (sel.ob.x == -1)
return; return;
selremove();
tsetdirt(sel.nb.y, sel.ne.y);
}
void
selremove(void)
{
sel.mode = SEL_IDLE; sel.mode = SEL_IDLE;
sel.ob.x = -1; sel.ob.x = -1;
tsetdirt(sel.nb.y, sel.ne.y);
} }
void void
@ -950,10 +993,8 @@ void
ttywrite(const char *s, size_t n, int may_echo) ttywrite(const char *s, size_t n, int may_echo)
{ {
const char *next; const char *next;
#if SCROLLBACK_PATCH #if REFLOW_PATCH || SCROLLBACK_PATCH
Arg arg = (Arg) { .i = term.scr }; kscrolldown(&((Arg){ .i = term.scr }));
kscrolldown(&arg);
#endif // SCROLLBACK_PATCH #endif // SCROLLBACK_PATCH
if (may_echo && IS_SET(MODE_ECHO)) if (may_echo && IS_SET(MODE_ECHO))
@ -1097,7 +1138,11 @@ tsetdirtattr(int attr)
for (i = 0; i < term.row-1; i++) { for (i = 0; i < term.row-1; i++) {
for (j = 0; j < term.col-1; j++) { for (j = 0; j < term.col-1; j++) {
if (term.line[i][j].mode & attr) { if (term.line[i][j].mode & attr) {
#if REFLOW_PATCH
term.dirty[i] = 1;
#else
tsetdirt(i, i); tsetdirt(i, i);
#endif // REFLOW_PATCH
break; break;
} }
} }
@ -1110,7 +1155,12 @@ tfulldirt(void)
#if SYNC_PATCH #if SYNC_PATCH
tsync_end(); tsync_end();
#endif // SYNC_PATCH #endif // SYNC_PATCH
#if REFLOW_PATCH
for (int i = 0; i < term.row; i++)
term.dirty[i] = 1;
#else
tsetdirt(0, term.row-1); tsetdirt(0, term.row-1);
#endif // REFLOW_PATCH
} }
void 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 void
treset(void) treset(void)
{ {
uint i; uint i;
#if SIXEL_PATCH #if REFLOW_PATCH
ImageList *im; int x, y;
#endif // SIXEL_PATCH #endif // REFLOW_PATCH
term.c = (TCursor){{ tresetcursor();
.mode = ATTR_NULL,
.fg = defaultfg,
.bg = defaultbg
}, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
memset(term.tabs, 0, term.col * sizeof(*term.tabs)); memset(term.tabs, 0, term.col * sizeof(*term.tabs));
for (i = tabspaces; i < term.col; i += tabspaces) for (i = tabspaces; i < term.col; i += tabspaces)
@ -1149,8 +1202,20 @@ treset(void)
term.mode = MODE_WRAP|MODE_UTF8; term.mode = MODE_WRAP|MODE_UTF8;
memset(term.trantbl, CS_USA, sizeof(term.trantbl)); memset(term.trantbl, CS_USA, sizeof(term.trantbl));
term.charset = 0; 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++) { 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); tmoveto(0, 0);
tcursor(CURSOR_SAVE); tcursor(CURSOR_SAVE);
#if COLUMNS_PATCH #if COLUMNS_PATCH
@ -1158,13 +1223,18 @@ treset(void)
#else #else
tclearregion(0, 0, term.col-1, term.row-1); tclearregion(0, 0, term.col-1, term.row-1);
#endif // COLUMNS_PATCH #endif // COLUMNS_PATCH
#endif // REFLOW_PATCH
#if SIXEL_PATCH #if SIXEL_PATCH
tdeleteimages(); tdeleteimages();
#endif // SIXEL_PATCH #endif // SIXEL_PATCH
tswapscreen(); tswapscreen();
} }
#if REFLOW_PATCH
tfulldirt();
#endif // REFLOW_PATCH
} }
#if !REFLOW_PATCH
void void
tnew(int col, int row) tnew(int col, int row)
{ {
@ -1172,7 +1242,9 @@ tnew(int col, int row)
tresize(col, row); tresize(col, row);
treset(); treset();
} }
#endif // REFLOW_PATCH
#if !REFLOW_PATCH
void void
tswapscreen(void) tswapscreen(void)
{ {
@ -1190,7 +1262,9 @@ tswapscreen(void)
term.mode ^= MODE_ALTSCREEN; term.mode ^= MODE_ALTSCREEN;
tfulldirt(); tfulldirt();
} }
#endif // REFLOW_PATCH
#if !REFLOW_PATCH
void void
tscrolldown(int orig, int n) tscrolldown(int orig, int n)
{ {
@ -1245,7 +1319,9 @@ tscrolldown(int orig, int n)
selscroll(orig, n); selscroll(orig, n);
#endif // SCROLLBACK_PATCH #endif // SCROLLBACK_PATCH
} }
#endif // REFLOW_PATCH
#if !REFLOW_PATCH
void void
#if SCROLLBACK_PATCH #if SCROLLBACK_PATCH
tscrollup(int orig, int n, int copyhist) tscrollup(int orig, int n, int copyhist)
@ -1350,7 +1426,9 @@ tscrollup(int orig, int n)
selscroll(orig, -n); selscroll(orig, -n);
#endif // SCROLLBACK_PATCH #endif // SCROLLBACK_PATCH
} }
#endif // REFLOW_PATCH
#if !REFLOW_PATCH
void void
selscroll(int orig, int n) selscroll(int orig, int n)
{ {
@ -1370,6 +1448,7 @@ selscroll(int orig, int n)
} }
} }
} }
#endif // REFLOW_PATCH
void void
tnewline(int first_col) tnewline(int first_col)
@ -1377,7 +1456,9 @@ tnewline(int first_col)
int y = term.c.y; int y = term.c.y;
if (y == term.bot) { 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); tscrollup(term.top, 1, 1);
#else #else
tscrollup(term.top, 1); tscrollup(term.top, 1);
@ -1503,6 +1584,9 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
term.dirty[y] = 1; term.dirty[y] = 1;
term.line[y][x] = *attr; term.line[y][x] = *attr;
term.line[y][x].u = u; term.line[y][x].u = u;
#if REFLOW_PATCH
term.line[y][x].mode |= ATTR_SET;
#endif // REFLOW_PATCH
#if BOXDRAW_PATCH #if BOXDRAW_PATCH
if (isboxdraw(u)) if (isboxdraw(u))
@ -1510,6 +1594,7 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
#endif // BOXDRAW_PATCH #endif // BOXDRAW_PATCH
} }
#if !REFLOW_PATCH
void void
tclearregion(int x1, int y1, int x2, int y2) 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 void
tdeletechar(int n) tdeletechar(int n)
{ {
@ -1561,7 +1648,9 @@ tdeletechar(int n)
memmove(&line[dst], &line[src], size * sizeof(Glyph)); memmove(&line[dst], &line[src], size * sizeof(Glyph));
tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
} }
#endif // REFLOW_PATCH
#if !REFLOW_PATCH
void void
tinsertblank(int n) tinsertblank(int n)
{ {
@ -1578,6 +1667,7 @@ tinsertblank(int n)
memmove(&line[dst], &line[src], size * sizeof(Glyph)); memmove(&line[dst], &line[src], size * sizeof(Glyph));
tclearregion(src, term.c.y, dst - 1, term.c.y); tclearregion(src, term.c.y, dst - 1, term.c.y);
} }
#endif // REFLOW_PATCH
void void
tinsertblankline(int n) tinsertblankline(int n)
@ -1602,12 +1692,15 @@ tdeleteimages(void)
void void
tdeleteline(int n) tdeleteline(int n)
{ {
if (BETWEEN(term.c.y, term.top, term.bot)) if (BETWEEN(term.c.y, term.top, term.bot)) {
#if SCROLLBACK_PATCH #if REFLOW_PATCH
tscrollup(term.c.y, term.bot, n, SCROLL_NOSAVEHIST);
#elif SCROLLBACK_PATCH
tscrollup(term.c.y, n, 0); tscrollup(term.c.y, n, 0);
#else #else
tscrollup(term.c.y, n); tscrollup(term.c.y, n);
#endif // SCROLLBACK_PATCH #endif // SCROLLBACK_PATCH
}
} }
int32_t int32_t
@ -1906,6 +1999,13 @@ tsetmode(int priv, int set, const int *args, int narg)
case 1047: case 1047:
if (!allowaltscreen) if (!allowaltscreen)
break; break;
#if REFLOW_PATCH
if (set)
tloadaltscreen(*args != 47, *args == 1049);
else
tloaddefscreen(*args != 47, *args == 1049);
break;
#else
alt = IS_SET(MODE_ALTSCREEN); alt = IS_SET(MODE_ALTSCREEN);
if (alt) { if (alt) {
#if COLUMNS_PATCH #if COLUMNS_PATCH
@ -1919,7 +2019,12 @@ tsetmode(int priv, int set, const int *args, int narg)
if (*args != 1049) if (*args != 1049)
break; break;
/* FALLTHROUGH */ /* FALLTHROUGH */
#endif // REFLOW_PATCH
case 1048: case 1048:
#if REFLOW_PATCH
if (!allowaltscreen)
break;
#endif // REFLOW_PATCH
tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
break; break;
case 2004: /* 2004: bracketed paste mode */ case 2004: /* 2004: bracketed paste mode */
@ -1979,11 +2084,14 @@ void
csihandle(void) csihandle(void)
{ {
char buffer[40]; char buffer[40];
int len; int n = 0, len;
#if SIXEL_PATCH #if SIXEL_PATCH
ImageList *im, *next; ImageList *im, *next;
int n, pi, pa; int pi, pa;
#endif // SIXEL_PATCH #endif // SIXEL_PATCH
#if REFLOW_PATCH
int x;
#endif // REFLOW_PATCH
#if COLUMNS_PATCH #if COLUMNS_PATCH
int maxcol = term.maxcol; int maxcol = term.maxcol;
#else #else
@ -2086,18 +2194,52 @@ csihandle(void)
case 'J': /* ED -- Clear screen */ case 'J': /* ED -- Clear screen */
switch (csiescseq.arg[0]) { switch (csiescseq.arg[0]) {
case 0: /* below */ 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); tclearregion(term.c.x, term.c.y, maxcol-1, term.c.y);
if (term.c.y < term.row-1) { if (term.c.y < term.row-1)
tclearregion(0, term.c.y+1, maxcol-1, tclearregion(0, term.c.y+1, maxcol-1, term.row-1);
term.row-1); #endif // REFLOW_PATCH
}
break; break;
case 1: /* above */ 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) if (term.c.y > 1)
tclearregion(0, 0, maxcol-1, term.c.y-1); tclearregion(0, 0, maxcol-1, term.c.y-1);
tclearregion(0, term.c.y, term.c.x, term.c.y); tclearregion(0, term.c.y, term.c.x, term.c.y);
#endif // REFLOW_PATCH
break; break;
case 2: /* screen */ 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 SCROLLBACK_PATCH
if (!IS_SET(MODE_ALTSCREEN)) { if (!IS_SET(MODE_ALTSCREEN)) {
#if SCROLLBACK_PATCH #if SCROLLBACK_PATCH
@ -2127,8 +2269,25 @@ csihandle(void)
#if SIXEL_PATCH #if SIXEL_PATCH
tdeleteimages(); tdeleteimages();
#endif // SIXEL_PATCH #endif // SIXEL_PATCH
#endif // REFLOW_PTCH
break; break;
case 3: /* scrollback */ 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 SCROLLBACK_PATCH
if (!IS_SET(MODE_ALTSCREEN)) { if (!IS_SET(MODE_ALTSCREEN)) {
term.scr = 0; term.scr = 0;
@ -2149,6 +2308,7 @@ csihandle(void)
} }
#endif // SIXEL_PATCH #endif // SIXEL_PATCH
break; break;
#endif // REFLOW_PATCH
#if SIXEL_PATCH #if SIXEL_PATCH
case 6: /* sixels */ case 6: /* sixels */
tdeleteimages(); tdeleteimages();
@ -2161,9 +2321,20 @@ csihandle(void)
break; break;
case 'K': /* EL -- Clear line */ case 'K': /* EL -- Clear line */
switch (csiescseq.arg[0]) { switch (csiescseq.arg[0]) {
#if REFLOW_PATCH
case 0: /* right */ case 0: /* right */
tclearregion(term.c.x, term.c.y, maxcol-1, tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 1);
term.c.y); 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; break;
case 1: /* left */ case 1: /* left */
tclearregion(0, term.c.y, term.c.x, term.c.y); 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); tclearregion(0, term.c.y, maxcol-1, term.c.y);
break; break;
} }
#endif // REFLOW_PATCH
break; break;
case 'S': /* SU -- Scroll <n> line up ; XTSMGRAPHICS */ case 'S': /* SU -- Scroll <n> line up ; XTSMGRAPHICS */
if (csiescseq.priv) { if (csiescseq.priv) {
@ -2203,7 +2375,10 @@ csihandle(void)
goto unknown; goto unknown;
} }
DEFAULT(csiescseq.arg[0], 1); 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); tscrollup(term.top, csiescseq.arg[0], 1);
#elif SCROLLBACK_PATCH #elif SCROLLBACK_PATCH
tscrollup(term.top, csiescseq.arg[0], 0); tscrollup(term.top, csiescseq.arg[0], 0);
@ -2227,9 +2402,17 @@ csihandle(void)
tdeleteline(csiescseq.arg[0]); tdeleteline(csiescseq.arg[0]);
break; break;
case 'X': /* ECH -- Erase <n> char */ 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); DEFAULT(csiescseq.arg[0], 1);
tclearregion(term.c.x, term.c.y, tclearregion(term.c.x, term.c.y,
term.c.x + csiescseq.arg[0] - 1, term.c.y); term.c.x + csiescseq.arg[0] - 1, term.c.y);
#endif // REFLOW_PATCH
break; break;
case 'P': /* DCH -- Delete <n> char */ case 'P': /* DCH -- Delete <n> char */
DEFAULT(csiescseq.arg[0], 1); DEFAULT(csiescseq.arg[0], 1);
@ -2411,7 +2594,7 @@ strhandle(void)
int i, x, y, x1, y1, x2, y2, numimages; int i, x, y, x1, y1, x2, y2, numimages;
int cx, cy; int cx, cy;
Line line; Line line;
#if SCROLLBACK_PATCH #if SCROLLBACK_PATCH || REFLOW_PATCH
int scr = IS_SET(MODE_ALTSCREEN) ? 0 : term.scr; int scr = IS_SET(MODE_ALTSCREEN) ? 0 : term.scr;
#else #else
int scr = 0; int scr = 0;
@ -2551,7 +2734,7 @@ strhandle(void)
x2 = MIN(x2, term.col); x2 = MIN(x2, term.col);
for (i = 0, im = newimages; im; im = next, i++) { for (i = 0, im = newimages; im; im = next, i++) {
next = im->next; next = im->next;
#if SCROLLBACK_PATCH #if SCROLLBACK_PATCH || REFLOW_PATCH
scr = IS_SET(MODE_ALTSCREEN) ? 0 : term.scr; scr = IS_SET(MODE_ALTSCREEN) ? 0 : term.scr;
#endif // SCROLLBACK_PATCH #endif // SCROLLBACK_PATCH
if (IS_SET(MODE_SIXEL_SDM)) { if (IS_SET(MODE_SIXEL_SDM)) {
@ -2704,6 +2887,7 @@ tdumpsel(void)
} }
} }
#if !REFLOW_PATCH
void void
tdumpline(int n) tdumpline(int n)
{ {
@ -2718,6 +2902,7 @@ tdumpline(int n)
} }
tprinter("\n", 1); tprinter("\n", 1);
} }
#endif // REFLOW_PATCH
void void
tdump(void) tdump(void)
@ -2994,7 +3179,9 @@ eschandle(uchar ascii)
return 0; return 0;
case 'D': /* IND -- Linefeed */ case 'D': /* IND -- Linefeed */
if (term.c.y == term.bot) { 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); tscrollup(term.top, 1, 1);
#else #else
tscrollup(term.top, 1); tscrollup(term.top, 1);
@ -3027,7 +3214,7 @@ eschandle(uchar ascii)
resettitle(); resettitle();
xloadcols(); xloadcols();
xsetmode(0, MODE_HIDE); xsetmode(0, MODE_HIDE);
#if SCROLLBACK_PATCH #if SCROLLBACK_PATCH && !REFLOW_PATCH
if (!IS_SET(MODE_ALTSCREEN)) { if (!IS_SET(MODE_ALTSCREEN)) {
term.scr = 0; term.scr = 0;
term.histi = 0; term.histi = 0;
@ -3189,8 +3376,14 @@ check_control_code:
return; 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)) if (selected(term.c.x, term.c.y))
selclear(); selclear();
#endif // REFLOW_PATCH
gp = &term.line[term.c.y][term.c.x]; gp = &term.line[term.c.y][term.c.x];
if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
@ -3229,6 +3422,9 @@ check_control_code:
if (term.c.x+width < term.col) { if (term.c.x+width < term.col) {
tmoveto(term.c.x+width, term.c.y); tmoveto(term.c.x+width, term.c.y);
} else { } else {
#if REFLOW_PATCH
term.wrapcwidth[IS_SET(MODE_ALTSCREEN)] = width;
#endif // REFLOW_PATCH
term.c.state |= CURSOR_WRAPNEXT; term.c.state |= CURSOR_WRAPNEXT;
} }
} }
@ -3284,6 +3480,7 @@ twrite(const char *buf, int buflen, int show_ctrl)
return n; return n;
} }
#if !REFLOW_PATCH
void void
tresize(int col, int row) tresize(int col, int row)
{ {
@ -3446,6 +3643,7 @@ tresize(int col, int row)
} }
#endif // SIXEL_PATCH #endif // SIXEL_PATCH
} }
#endif // REFLOW_PATCH
void void
resettitle(void) resettitle(void)
@ -3467,7 +3665,7 @@ drawregion(int x1, int y1, int x2, int y2)
continue; continue;
term.dirty[y] = 0; term.dirty[y] = 0;
#if SCROLLBACK_PATCH #if SCROLLBACK_PATCH || REFLOW_PATCH
xdrawline(TLINE(y), x1, y, x2); xdrawline(TLINE(y), x1, y, x2);
#else #else
xdrawline(term.line[y], x1, y, x2); xdrawline(term.line[y], x1, y, x2);
@ -3495,6 +3693,10 @@ draw(void)
drawregion(0, 0, term.col, term.row); drawregion(0, 0, term.col, term.row);
#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
if (!kbds_drawcursor()) {
#endif // KEYBOARDSELECT_PATCH
#if SCROLLBACK_PATCH #if SCROLLBACK_PATCH
if (term.scr == 0) if (term.scr == 0)
#endif // SCROLLBACK_PATCH #endif // SCROLLBACK_PATCH
@ -3506,6 +3708,9 @@ draw(void)
xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
term.ocx, term.ocy, term.line[term.ocy][term.ocx]); term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
#endif // LIGATURES_PATCH #endif // LIGATURES_PATCH
#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
}
#endif // KEYBOARDSELECT_PATCH
term.ocx = cx; term.ocx = cx;
term.ocy = term.c.y; term.ocy = term.c.y;
xfinishdraw(); xfinishdraw();

68
st.h
View File

@ -33,39 +33,43 @@
#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
#define IS_TRUECOL(x) (1 << 24 & (x)) #define IS_TRUECOL(x) (1 << 24 & (x))
#if SCROLLBACK_PATCH #if SCROLLBACK_PATCH || REFLOW_PATCH
#define HISTSIZE 2000 #define HISTSIZE 2000
#endif // SCROLLBACK_PATCH #endif // SCROLLBACK_PATCH | REFLOW_PATCH
enum glyph_attribute { enum glyph_attribute {
ATTR_NULL = 0, ATTR_NULL = 0,
ATTR_BOLD = 1 << 0, ATTR_SET = 1 << 0,
ATTR_FAINT = 1 << 1, ATTR_BOLD = 1 << 1,
ATTR_ITALIC = 1 << 2, ATTR_FAINT = 1 << 2,
ATTR_UNDERLINE = 1 << 3, ATTR_ITALIC = 1 << 3,
ATTR_BLINK = 1 << 4, ATTR_UNDERLINE = 1 << 4,
ATTR_REVERSE = 1 << 5, ATTR_BLINK = 1 << 5,
ATTR_INVISIBLE = 1 << 6, ATTR_REVERSE = 1 << 6,
ATTR_STRUCK = 1 << 7, ATTR_INVISIBLE = 1 << 7,
ATTR_WRAP = 1 << 8, ATTR_STRUCK = 1 << 8,
ATTR_WIDE = 1 << 9, ATTR_WRAP = 1 << 9,
ATTR_WDUMMY = 1 << 10, 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 #if BOXDRAW_PATCH
ATTR_BOXDRAW = 1 << 11, ATTR_BOXDRAW = 1 << 13,
#endif // BOXDRAW_PATCH #endif // BOXDRAW_PATCH
#if UNDERCURL_PATCH
ATTR_DIRTYUNDERLINE = 1 << 14,
#endif // UNDERCURL_PATCH
#if LIGATURES_PATCH #if LIGATURES_PATCH
ATTR_LIGA = 1 << 12, ATTR_LIGA = 1 << 15,
#endif // LIGATURES_PATCH #endif // LIGATURES_PATCH
#if SIXEL_PATCH #if SIXEL_PATCH
ATTR_SIXEL = 1 << 13, ATTR_SIXEL = 1 << 16,
#endif // SIXEL_PATCH #endif // SIXEL_PATCH
#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
ATTR_HIGHLIGHT = 1 << 17,
#endif // KEYBOARDSELECT_PATCH
ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, 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 #if SIXEL_PATCH
@ -77,6 +81,9 @@ typedef struct _ImageList {
int height; int height;
int x; int x;
int y; int y;
#if REFLOW_PATCH
int reflow_y;
#endif // REFLOW_PATCH
int cols; int cols;
int cw; int cw;
int ch; int ch;
@ -128,7 +135,7 @@ typedef XftGlyphFontSpec GlyphFontSpec;
#define Glyph Glyph_ #define Glyph Glyph_
typedef struct { typedef struct {
Rune u; /* character code */ Rune u; /* character code */
ushort mode; /* attribute flags */ uint32_t mode; /* attribute flags */
uint32_t fg; /* foreground */ uint32_t fg; /* foreground */
uint32_t bg; /* background */ uint32_t bg; /* background */
#if UNDERCURL_PATCH #if UNDERCURL_PATCH
@ -164,12 +171,18 @@ typedef struct {
#endif // COLUMNS_PATCH #endif // COLUMNS_PATCH
Line *line; /* screen */ Line *line; /* screen */
Line *alt; /* alternate 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 */ Line hist[HISTSIZE]; /* history buffer */
int histi; /* history index */ int histi; /* history index */
int histn; /* number of history entries */ int histn; /* number of history entries */
int scr; /* scroll back */ int scr; /* scroll back */
#endif // SCROLLBACK_PATCH #endif // SCROLLBACK_PATCH | REFLOW_PATCH
int *dirty; /* dirtyness of lines */ int *dirty; /* dirtyness of lines */
TCursor c; /* cursor */ TCursor c; /* cursor */
int ocx; /* old cursor col */ int ocx; /* old cursor col */
@ -347,6 +360,7 @@ void resettitle(void);
void selclear(void); void selclear(void);
void selinit(void); void selinit(void);
void selremove(void);
void selstart(int, int, int); void selstart(int, int, int);
void selextend(int, int, int, int); void selextend(int, int, int, int);
int selected(int, int); int selected(int, int);
@ -376,6 +390,10 @@ extern char *scroll;
extern char *stty_args; extern char *stty_args;
extern char *vtiden; extern char *vtiden;
extern wchar_t *worddelimiters; 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 allowaltscreen;
extern int allowwindowops; extern int allowwindowops;
extern char *termname; extern char *termname;

3
win.h
View File

@ -53,3 +53,6 @@ void xsetsel(char *);
int xstartdraw(void); int xstartdraw(void);
void xximspot(int, int); void xximspot(int, int);
void xclearwin(void); void xclearwin(void);
#if REFLOW_PATCH && KEYBOARDSELECT_PATCH
void xdrawglyph(Glyph, int, int);
#endif // KEYBOARDSELECT_PATCH

109
x.c
View File

@ -90,7 +90,7 @@ static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
#if LIGATURES_PATCH #if LIGATURES_PATCH
static inline void xresetfontsettings(uint32_t mode, Font **font, int *frcflags); static inline void xresetfontsettings(uint32_t mode, Font **font, int *frcflags);
#endif // LIGATURES_PATCH #endif // LIGATURES_PATCH
static void xdrawglyph(Glyph, int, int); void xdrawglyph(Glyph, int, int);
static void xclear(int, int, int, int); static void xclear(int, int, int, int);
static int xgeommasktogravity(int); static int xgeommasktogravity(int);
static int ximopen(Display *); static int ximopen(Display *);
@ -259,6 +259,11 @@ clippaste(const Arg *dummy)
{ {
Atom clipboard; Atom clipboard;
#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
if (IS_SET(MODE_KBDSELECT) && !kbds_issearchmode())
return;
#endif // KEYBOARDSELECT_PATCH
clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard,
xw.win, CurrentTime); xw.win, CurrentTime);
@ -273,6 +278,11 @@ numlock(const Arg *dummy)
void void
selpaste(const Arg *dummy) 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, XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY,
xw.win, CurrentTime); xw.win, CurrentTime);
} }
@ -399,6 +409,11 @@ mousesel(XEvent *e, int done)
int type, seltype = SEL_REGULAR; int type, seltype = SEL_REGULAR;
uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); 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) { for (type = 1; type < LEN(selmasks); ++type) {
if (match(selmasks[type], state)) { if (match(selmasks[type], state)) {
seltype = type; seltype = type;
@ -517,6 +532,11 @@ bpress(XEvent *e)
xsel.tclick2 = xsel.tclick1; xsel.tclick2 = xsel.tclick1;
xsel.tclick1 = now; xsel.tclick1 = now;
#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
if (kbds_isselectmode())
return;
#endif // KEYBOARDSELECT_PATCH
selstart(evcol(e), evrow(e), snap); selstart(evcol(e), evrow(e), snap);
#if OPENURLONCLICK_PATCH #if OPENURLONCLICK_PATCH
@ -555,6 +575,9 @@ selnotify(XEvent *e)
int format; int format;
uchar *data, *last, *repl; uchar *data, *last, *repl;
Atom type, incratom, property = None; Atom type, incratom, property = None;
#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
int append = 0;
#endif // KEYBOARDSELECT_PATCH
incratom = XInternAtom(xw.dpy, "INCR", 0); incratom = XInternAtom(xw.dpy, "INCR", 0);
@ -616,6 +639,10 @@ selnotify(XEvent *e)
continue; 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: * As seen in getsel:
* Line endings are inconsistent in the terminal and GUI world * Line endings are inconsistent in the terminal and GUI world
@ -634,6 +661,27 @@ selnotify(XEvent *e)
ttywrite((char *)data, nitems * format / 8, 1); ttywrite((char *)data, nitems * format / 8, 1);
if (IS_SET(MODE_BRCKTPASTE) && rem == 0) if (IS_SET(MODE_BRCKTPASTE) && rem == 0)
ttywrite("\033[201~", 6, 0); ttywrite("\033[201~", 6, 0);
}
#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);
#endif // KEYBOARDSELECT_PATCH
XFree(data); XFree(data);
/* number of 32-bit chunks returned */ /* number of 32-bit chunks returned */
ofs += nitems * format / 32; ofs += nitems * format / 32;
@ -2064,6 +2112,13 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
} }
#endif // INVERT_PATCH #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 #if ALPHA_PATCH && ALPHA_GRADIENT_PATCH
// gradient // gradient
bg->color.alpha = grad_alpha * 0xffff * (win.h - y*win.ch) / win.h + stat_alpha * 0xffff; 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 #if DYNAMIC_CURSOR_COLOR_PATCH
|ATTR_REVERSE |ATTR_REVERSE
#endif // DYNAMIC_CURSOR_COLOR_PATCH #endif // DYNAMIC_CURSOR_COLOR_PATCH
#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
|ATTR_HIGHLIGHT
#endif // KEYBOARDSELECT_PATCH
; ;
if (IS_SET(MODE_REVERSE)) { 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]; drawcol = dc.col[defaultcs];
#else #else
if (selected(cx, cy)) { 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; g.mode &= ~ATTR_REVERSE;
#endif // DYNAMIC_CURSOR_COLOR_PATCH #endif // DYNAMIC_CURSOR_COLOR_PATCH
g.fg = defaultfg; g.fg = defaultfg;
@ -2669,6 +2729,11 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
#endif // SELECTION_COLORS_PATCH #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 */ /* draw the new one */
if (IS_SET(MODE_FOCUSED)) { if (IS_SET(MODE_FOCUSED)) {
switch (win.cursor) { 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); xdrawglyphfontspecs(specs, seq[i].base, seq[i].numspecs, seq[i].ox, y1, DRAW_FG, seq[i].charlen);
specs += seq[i].numspecs; specs += seq[i].numspecs;
} }
#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
kbds_drawstatusbar(y1);
#endif // KEYBOARDSELECT_PATCH
} }
#elif LIGATURES_PATCH #elif LIGATURES_PATCH
void void
@ -2957,6 +3026,10 @@ xdrawline(Line line, int x1, int y1, int x2)
numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1); numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1);
xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
} }
#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
kbds_drawstatusbar(y1);
#endif // KEYBOARDSELECT_PATCH
} }
#elif WIDE_GLYPHS_PATCH #elif WIDE_GLYPHS_PATCH
void void
@ -2999,6 +3072,10 @@ xdrawline(Line line, int x1, int y1, int x2)
if (i > 0) if (i > 0)
xdrawglyphfontspecs(specs, base, i, ox, y1, dmode); 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 #else // !WIDE_GLYPHS_PATCH and !LIGATURES_PATCH
void void
@ -3035,6 +3112,10 @@ xdrawline(Line line, int x1, int y1, int x2)
} }
if (i > 0) if (i > 0)
xdrawglyphfontspecs(specs, base, i, ox, y1); xdrawglyphfontspecs(specs, base, i, ox, y1);
#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
kbds_drawstatusbar(y1);
#endif // KEYBOARDSELECT_PATCH
} }
#endif // WIDE_GLYPHS_PATCH | LIGATURES_PATCH #endif // WIDE_GLYPHS_PATCH | LIGATURES_PATCH
@ -3130,11 +3211,11 @@ xfinishdraw(void)
width = MIN(width, (x2 - im->x) * win.cw); width = MIN(width, (x2 - im->x) * win.cw);
/* delete the image if the text cells behind it have been changed */ /* delete the image if the text cells behind it have been changed */
#if SCROLLBACK_PATCH #if SCROLLBACK_PATCH || REFLOW_PATCH
line = TLINE(im->y); line = TLINE(im->y);
#else #else
line = term.line[im->y]; line = term.line[im->y];
#endif // SCROLLBACK_PATCH #endif // SCROLLBACK_PATCH | REFLOW_PATCH
for (del = 0, x = im->x; x < x2; x++) { for (del = 0, x = im->x; x < x2; x++) {
if ((del = !(line[x].mode & ATTR_SIXEL))) if ((del = !(line[x].mode & ATTR_SIXEL)))
break; break;
@ -3431,7 +3512,25 @@ kpress(XEvent *ev)
} else { } else {
len = XLookupString(e, buf, sizeof buf, &ksym, NULL); 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 ( IS_SET(MODE_KBDSELECT) ) {
if ( match(XK_NO_MOD, e->state) || if ( match(XK_NO_MOD, e->state) ||
(XK_Shift_L | XK_Shift_R) & e->state ) (XK_Shift_L | XK_Shift_R) & e->state )