Compare commits

...

10 Commits

Author SHA1 Message Date
Ryan 5a8134756d
setup externalpipe-based commands
Adds a commands with hotkeys:
- Copy output from a command
- Copy a URL from the buffer
- Open a URL from the buffer
- Edit the screen in emacs (primarily for copying)
2023-09-17 01:25:35 -04:00
Ryan 0b6da82ccb
remove unused bindings 2023-09-17 01:25:34 -04:00
Ryan 5b0f5cf38d
make zooming more sane
This commit makes the zoom rate configurable and way higher.
2023-09-17 01:25:34 -04:00
Ryan 32787b3e6d
apply external-pipe-eternal patch
Makes it work with scrollback.
2023-09-17 01:25:33 -04:00
Ryan 312b7a9db6
apply externalpipe patch 2023-09-17 01:25:25 -04:00
Ryan fb3bc3271d
apply blinking_cursor patch
Tweak the Xresources loading to use the new variable.
2023-09-17 01:25:10 -04:00
Ryan ac8e2737df
apply xclearwin patch 2023-09-17 01:24:51 -04:00
Ryan 65232bdda3
apply scrollback-mouse-increment patch
Tweak the config to be a bit better organized.
2023-09-17 01:24:42 -04:00
Ryan 2246322ba6
apply scrollback-mouse-altscreen patch
This makes the mouse wheel work for both normal scrolling and
altscreens such as less or vim.
2023-09-17 01:24:33 -04:00
Ryan e620ca6bb3
apply scrollback-mouse patch
Just updates the config, kinda unnecessary.
2023-09-17 01:24:24 -04:00
10 changed files with 243 additions and 33 deletions

View File

@ -43,7 +43,13 @@ dist: clean
install: st
mkdir -p $(DESTDIR)$(PREFIX)/bin
cp -f st $(DESTDIR)$(PREFIX)/bin
cp -f st-urlhandler $(DESTDIR)$(PREFIX)/bin
cp -f st-copyout $(DESTDIR)$(PREFIX)/bin
cp -f st-editscreen $(DESTDIR)$(PREFIX)/bin
chmod 755 $(DESTDIR)$(PREFIX)/bin/st
chmod 755 $(DESTDIR)$(PREFIX)/bin/st-urlhandler
chmod 755 $(DESTDIR)$(PREFIX)/bin/st-copyout
chmod 755 $(DESTDIR)$(PREFIX)/bin/st-editscreen
mkdir -p $(DESTDIR)$(MANPREFIX)/man1
sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1
chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1

View File

@ -135,13 +135,20 @@ unsigned int defaultcs = 256;
static unsigned int defaultrcs = 257;
/*
* Default shape of cursor
* 2: Block ("")
* 4: Underline ("_")
* 6: Bar ("|")
* 7: Snowman ("")
* https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps-SP-q.1D81
* Default style of cursor
* 0: blinking block
* 1: blinking block (default)
* 2: steady block ("")
* 3: blinking underline
* 4: steady underline ("_")
* 5: blinking bar
* 6: steady bar ("|")
* 7: blinking st cursor
* 8: steady st cursor
*/
static unsigned int cursorshape = 2;
static unsigned int cursorstyle = 1;
static Rune stcursor = 0x2603; /* snowman ("☃") */
/*
* Default columns and rows numbers
@ -170,6 +177,11 @@ static unsigned int defaultattr = 11;
*/
static uint forcemousemod = ShiftMask;
/*
* The number of lines to scroll by when using the mouse wheel.
*/
const unsigned int mousescrollrate = 1;
/*
* Xresources preferences to load at startup
*/
@ -212,6 +224,8 @@ ResourcePref resources[] = {
*/
static MouseShortcut mshortcuts[] = {
/* mask button function argument release */
{ XK_ANY_MOD, Button4, kscrollup, {.i = mousescrollrate}, 0, /* !alt */ -1 },
{ XK_ANY_MOD, Button5, kscrolldown, {.i = mousescrollrate}, 0, /* !alt */ -1 },
{ XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 },
{ ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} },
{ XK_ANY_MOD, Button4, ttysend, {.s = "\031"} },

View File

@ -135,13 +135,20 @@ unsigned int defaultcs = 256;
static unsigned int defaultrcs = 257;
/*
* Default shape of cursor
* 2: Block ("")
* 4: Underline ("_")
* 6: Bar ("|")
* 7: Snowman ("")
* https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps-SP-q.1D81
* Default style of cursor
* 0: blinking block
* 1: blinking block (default)
* 2: steady block ("")
* 3: blinking underline
* 4: steady underline ("_")
* 5: blinking bar
* 6: steady bar ("|")
* 7: blinking st cursor
* 8: steady st cursor
*/
static unsigned int cursorshape = 2;
static unsigned int cursorstyle = 1;
static Rune stcursor = 0x2603; /* snowman ("☃") */
/*
* Default columns and rows numbers
@ -170,6 +177,16 @@ static unsigned int defaultattr = 11;
*/
static uint forcemousemod = ShiftMask;
/*
* The number of lines to scroll by when using the mouse wheel.
*/
const unsigned int mousescrollrate = 5;
/*
* The rate to zoom in (adjust the font size).
*/
const int zoomrate = 5;
/*
* Xresources preferences to load at startup
*/
@ -212,6 +229,10 @@ ResourcePref resources[] = {
*/
static MouseShortcut mshortcuts[] = {
/* mask button function argument release */
{ XK_NO_MOD, Button4, kscrollup, {.i = mousescrollrate}, 0, /* !alt */ -1 },
{ XK_NO_MOD, Button5, kscrolldown, {.i = mousescrollrate}, 0, /* !alt */ -1 },
{ ControlMask, Button4, zoom, {.f = zoomrate * +1} },
{ ControlMask, Button5, zoom, {.f = zoomrate * -1} },
{ XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 },
{ ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} },
{ XK_ANY_MOD, Button4, ttysend, {.s = "\031"} },
@ -223,22 +244,29 @@ static MouseShortcut mshortcuts[] = {
#define MODKEY Mod1Mask
#define TERMMOD (ControlMask|ShiftMask)
static char *openurlcmd[] = { "/bin/sh", "-c", "st-urlhandler -o", "externalpipe", NULL };
static char *copyurlcmd[] = { "/bin/sh", "-c", "st-urlhandler -c", "externalpipe", NULL };
static char *copyoutput[] = { "/bin/sh", "-c", "st-copyout", "externalpipe", NULL };
static char *editscreen[] = { "/bin/sh", "-c", "st-editscreen", "externalpipe", NULL };
static Shortcut shortcuts[] = {
/* mask keysym function argument */
{ XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} },
{ ControlMask, XK_Print, toggleprinter, {.i = 0} },
{ ShiftMask, XK_Print, printscreen, {.i = 0} },
{ XK_ANY_MOD, XK_Print, printsel, {.i = 0} },
{ TERMMOD, XK_Prior, zoom, {.f = +1} },
{ TERMMOD, XK_Next, zoom, {.f = -1} },
{ TERMMOD, XK_Up, zoom, {.f = zoomrate * +1} },
{ TERMMOD, XK_Down, zoom, {.f = zoomrate * -1} },
{ TERMMOD, XK_Home, zoomreset, {.f = 0} },
{ TERMMOD, XK_C, clipcopy, {.i = 0} },
{ TERMMOD, XK_V, clippaste, {.i = 0} },
{ TERMMOD, XK_Y, selpaste, {.i = 0} },
{ ShiftMask, XK_Insert, selpaste, {.i = 0} },
{ TERMMOD, XK_Num_Lock, numlock, {.i = 0} },
{ MODKEY, XK_Up, kscrollup, {.i = -1} },
{ MODKEY, XK_Down, kscrolldown, {.i = -1} },
{ MODKEY, XK_e, externalpipe, {.v = editscreen} },
{ MODKEY, XK_l, externalpipe, {.v = openurlcmd} },
{ MODKEY, XK_y, externalpipe, {.v = copyurlcmd} },
{ MODKEY, XK_o, externalpipe, {.v = copyoutput} },
};
/*

16
st-copyout Executable file
View File

@ -0,0 +1,16 @@
#!/bin/sh
# Using external pipe with st, give a dmenu prompt of recent commands,
# allowing the user to copy the output of one.
# xclip required for this script.
# By Jaywalker and Luke
tmpfile=$(mktemp /tmp/st-cmd-output.XXXXXX)
trap 'rm "$tmpfile"' 0 1 15
sed -n "w $tmpfile"
sed -i 's/\x0//g' "$tmpfile"
ps1="$(grep "\S" "$tmpfile" | tail -n 1 | sed 's/^\s*//' | cut -d' ' -f1)"
chosen="$(grep -F "$ps1" "$tmpfile" | sed '$ d' | tac | dmenu -i -l 10 | sed 's/[^^]/[&]/g; s/\^/\\^/g')"
eps1="$(echo "$ps1" | sed 's/[^^]/[&]/g; s/\^/\\^/g')"
awk "/^$chosen$/{p=1;print;next} p&&/$eps1/{p=0};p" "$tmpfile" | head -n -1 | tail -n +2 | xclip -selection clipboard
# st-copyout ends here

9
st-editscreen Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
# -*- mode: sh -*-
tmpfile=$(mktemp /tmp/st-edit.XXXXXX)
trap 'rm "$tmpfile"' 0 1 15
cat > "$tmpfile"
emacsclient -c "$tmpfile"
# st-editscreen ends here

21
st-urlhandler Executable file
View File

@ -0,0 +1,21 @@
#!/bin/sh
urlregex="(((http|https|gopher|gemini|ftp|ftps|git)://|www\\.)[a-zA-Z0-9.]*[:;a-zA-Z0-9./+@$&%?$\#=_~-]*)|((magnet:\\?xt=urn:btih:)[a-zA-Z0-9]*)"
urls="$(sed 's/.*│//g' | tr -d '\n' | # First remove linebreaks and mutt sidebars:
grep -aEo "$urlregex" | # grep only urls as defined above.
uniq | # Ignore neighboring duplicates.
sed "s/\(\.\|,\|;\|\!\\|\?\)$//;
s/^www./http:\/\/www\./")" # xdg-open will not detect url without http
[ -z "$urls" ] && exit 1
while getopts "hoc" o; do case "${o}" in
h) printf "Optional arguments for custom use:\\n -c: copy\\n -o: xdg-open\\n -h: Show this message\\n" && exit 1 ;;
o) chosen="$(echo "$urls" | dmenu -i -p 'Follow which url?' -l 10)"
setsid xdg-open "$chosen" >/dev/null 2>&1 & ;;
c) echo "$urls" | dmenu -i -p 'Copy which url?' -l 10 | tr -d '\n' | xclip -selection clipboard ;;
*) printf "Invalid option: -%s\\n" "$OPTARG" && exit 1 ;;
esac done
# st-urlhandler ends here

92
st.c
View File

@ -50,6 +50,10 @@
: term.line[(y) - term.scr] \
)
#define TLINE_HIST(y) ( \
(y) <= HISTSIZE-term.row+2 ? term.hist[(y)] : term.line[(y-HISTSIZE+term.row-3)] \
)
#define TLINEABS(y) ( \
(y) < 0 ? term.hist[(term.histi + (y) + 1 + HISTSIZE) % HISTSIZE] : term.line[(y)] \
)
@ -489,6 +493,20 @@ tgetline(char *buf, const Glyph *fgp)
return ptr - buf;
}
int
tlinehistlen(int y)
{
int i = term.col;
if (TLINE_HIST(y)[i - 1].mode & ATTR_WRAP)
return i;
while (i > 0 && TLINE_HIST(y)[i - 1].u == ' ')
--i;
return i;
}
void
selstart(int col, int row, int snap)
{
@ -793,8 +811,14 @@ sigchld(int a)
if ((p = waitpid(pid, &stat, WNOHANG)) < 0)
die("waiting for pid %hd failed: %s\n", pid, strerror(errno));
if (pid != p)
if (pid != p) {
if (p == 0 && wait(&stat) < 0)
die("wait: %s\n", strerror(errno));
/* reinstall sigchld handler */
signal(SIGCHLD, sigchld);
return;
}
if (WIFEXITED(stat) && WEXITSTATUS(stat))
die("child exited with status %d\n", WEXITSTATUS(stat));
@ -1063,6 +1087,11 @@ tsetdirtattr(int attr)
}
}
int tisaltscr(void)
{
return IS_SET(MODE_ALTSCREEN);
}
void
tfulldirt(void)
{
@ -2180,10 +2209,8 @@ strhandle(void)
fprintf(stderr, "erresc: invalid color j=%d, p=%s\n",
j, p ? p : "(null)");
} else {
/*
* TODO if defaultbg color is changed, borders
* are dirty
*/
if (j == defaultbg)
xclearwin();
tfulldirt();
}
return;
@ -2224,6 +2251,61 @@ strparse(void)
}
}
void
externalpipe(const Arg *arg)
{
int to[2];
char buf[UTF_SIZ];
void (*oldsigpipe)(int);
Glyph *bp, *end;
int lastpos, n, newline;
if (pipe(to) == -1)
return;
switch (fork()) {
case -1:
close(to[0]);
close(to[1]);
return;
case 0:
dup2(to[0], STDIN_FILENO);
close(to[0]);
close(to[1]);
execvp(((char **)arg->v)[0], (char **)arg->v);
fprintf(stderr, "st: execvp %s\n", ((char **)arg->v)[0]);
perror("failed");
exit(0);
}
close(to[0]);
/* ignore sigpipe for now, in case child exists early */
oldsigpipe = signal(SIGPIPE, SIG_IGN);
newline = 0;
for (n = 0; n <= HISTSIZE + 2; n++) {
bp = TLINE_HIST(n);
lastpos = MIN(tlinehistlen(n) + 1, term.col) - 1;
if (lastpos < 0)
break;
if (lastpos == 0)
continue;
end = &bp[lastpos + 1];
for (; bp < end; ++bp)
if (xwrite(to[1], buf, utf8encode(bp->u, buf)) < 0)
break;
if ((newline = TLINE_HIST(n)[lastpos].mode & ATTR_WRAP))
continue;
if (xwrite(to[1], "\n", 1) < 0)
break;
newline = 0;
}
if (newline)
(void)xwrite(to[1], "\n", 1);
close(to[1]);
/* restore */
signal(SIGPIPE, oldsigpipe);
}
void
strdump(void)
{

2
st.h
View File

@ -85,12 +85,14 @@ void draw(void);
void kscrolldown(const Arg *);
void kscrollup(const Arg *);
void externalpipe(const Arg *);
void printscreen(const Arg *);
void printsel(const Arg *);
void sendbreak(const Arg *);
void toggleprinter(const Arg *);
int tattrset(int);
int tisaltscr(void);
void tnew(int, int);
int tisaltscreen(void);
void tresize(int, int);

1
win.h
View File

@ -39,3 +39,4 @@ void xsetpointermotion(int);
void xsetsel(char *);
int xstartdraw(void);
void xximspot(int, int);
void xclearwin(void);

55
x.c
View File

@ -35,6 +35,7 @@ typedef struct {
void (*func)(const Arg *);
const Arg arg;
uint release;
int altscrn; /* 0: don't care, -1: not alt screen, 1: alt screen */
} MouseShortcut;
typedef struct {
@ -268,6 +269,7 @@ static char *opt_name = NULL;
static char *opt_title = NULL;
static uint buttons; /* bit field of pressed buttons */
static int cursorblinks = 0;
void
clipcopy(const Arg *dummy)
@ -470,6 +472,7 @@ mouseaction(XEvent *e, uint release)
for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
if (ms->release == release &&
ms->button == e->xbutton.button &&
(!ms->altscrn || (ms->altscrn == (tisaltscr() ? 1 : -1))) &&
(match(ms->mod, state) || /* exact or forced */
match(ms->mod, state & ~forcemousemod))) {
ms->func(&(ms->arg));
@ -874,6 +877,12 @@ xclear(int x1, int y1, int x2, int y2)
x1, y1, x2-x1, y2-y1);
}
void
xclearwin(void)
{
xclear(0, 0, win.w, win.h);
}
void
xhints(void)
{
@ -1585,29 +1594,44 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
/* draw the new one */
if (IS_SET(MODE_FOCUSED)) {
switch (win.cursor) {
case 7: /* st extension */
g.u = 0x2603; /* snowman (U+2603) */
default:
case 0: /* blinking block */
case 1: /* blinking block (default) */
if (IS_SET(MODE_BLINK))
break;
/* FALLTHROUGH */
case 0: /* Blinking Block */
case 1: /* Blinking Block (Default) */
case 2: /* Steady Block */
case 2: /* steady block */
xdrawglyph(g, cx, cy);
break;
case 3: /* Blinking Underline */
case 4: /* Steady Underline */
case 3: /* blinking underline */
if (IS_SET(MODE_BLINK))
break;
/* FALLTHROUGH */
case 4: /* steady underline */
XftDrawRect(xw.draw, &drawcol,
win.hborderpx + cx * win.cw,
win.vborderpx + (cy + 1) * win.ch - \
cursorthickness,
win.cw, cursorthickness);
break;
case 5: /* Blinking bar */
case 6: /* Steady bar */
case 5: /* blinking bar */
if (IS_SET(MODE_BLINK))
break;
/* FALLTHROUGH */
case 6: /* steady bar */
XftDrawRect(xw.draw, &drawcol,
win.hborderpx + cx * win.cw,
win.vborderpx + cy * win.ch,
cursorthickness, win.ch);
break;
case 7: /* blinking st cursor */
if (IS_SET(MODE_BLINK))
break;
/* FALLTHROUGH */
case 8: /* steady st cursor */
g.u = stcursor;
xdrawglyph(g, cx, cy);
break;
}
} else {
XftDrawRect(xw.draw, &drawcol,
@ -1764,9 +1788,12 @@ xsetmode(int set, unsigned int flags)
int
xsetcursor(int cursor)
{
if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */
if (!BETWEEN(cursor, 0, 8)) /* 7-8: st extensions */
return 1;
win.cursor = cursor;
cursorblinks = win.cursor == 0 || win.cursor == 1 ||
win.cursor == 3 || win.cursor == 5 ||
win.cursor == 7;
return 0;
}
@ -2013,6 +2040,10 @@ run(void)
if (FD_ISSET(ttyfd, &rfd) || xev) {
if (!drawing) {
trigger = now;
if (IS_SET(MODE_BLINK)) {
win.mode ^= MODE_BLINK;
}
lastblink = now;
drawing = 1;
}
timeout = (maxlatency - TIMEDIFF(now, trigger)) \
@ -2023,7 +2054,7 @@ run(void)
/* idle detected or maxlatency exhausted -> draw */
timeout = -1;
if (blinktimeout && tattrset(ATTR_BLINK)) {
if (blinktimeout && (cursorblinks || tattrset(ATTR_BLINK))) {
timeout = blinktimeout - TIMEDIFF(now, lastblink);
if (timeout <= 0) {
if (-timeout > blinktimeout) /* start visible */
@ -2112,7 +2143,7 @@ main(int argc, char *argv[])
{
xw.l = xw.t = 0;
xw.isfixed = False;
xsetcursor(cursorshape);
xsetcursor(cursorstyle);
ARGBEGIN {
case 'a':