12 Commits

Author SHA1 Message Date
9e0c60a428 agree st-clickurl with st-scrollback-ringbuffer and disable waitpid (handled by sigchld()) 2026-01-21 23:25:51 +01:00
Jishnu Sen
91f0a5a008 Highlight URLs with control and follow with click 2026-01-21 08:10:30 +01:00
6dd2af8162 apply patch st-expected-anysize-0.9.diff 2026-01-21 07:45:51 +01:00
98a2dd55b0 add dmenu dark and light variants 2025-10-10 07:30:34 +02:00
634f6cb5ad add copy/paste dmenu prompt and remove empty values from completion list 2025-10-09 22:35:21 +02:00
3c9aef2597 add key to insert text selected with dmenu as terminal input 2025-10-09 21:37:41 +02:00
ffd5e8750d add keys to edit screen content with editor and copy WORD using externalpipe 2025-10-09 08:55:49 +02:00
1a19738a57 agree st-externalpipe with st-scrollback-ringbuffer 2025-10-09 08:07:42 +02:00
a6f61c8b65 apply st-externalpipe-0.8.5.diff 2025-10-09 08:02:57 +02:00
a88f2f16ef update font 2025-10-09 08:00:12 +02:00
Timo Röhling
7cbc44eba5 Terminal scrollback with ring buffer
This patch adds a ring buffer for scrollback to the terminal.  The
advantage of using a ring buffer is that the common case, scrolling with
no static screen content, can be achieved very efficiently by
incrementing and decrementing the starting line (modulo buffer size).
2025-10-09 07:58:55 +02:00
wael
9fef53395a fix xresources with signal reloading removing arg.h and st.h & remove unneccesary xresources variables(?) 2025-10-09 07:53:45 +02:00
4 changed files with 490 additions and 211 deletions

View File

@@ -8,6 +8,40 @@
static char *font = "FiraCode Nerd Font:size=10"; static char *font = "FiraCode Nerd Font:size=10";
static int borderpx = 2; static int borderpx = 2;
static char *externaleditcmd[] = { "/bin/sh", "-c",
"tmpfile=$(mktemp /tmp/st-edit.XXXXXX)\n"
"trap 'rm \"$tmpfile\"' 0 1 15\n"
"cat > \"$tmpfile\"\n"
"st -e vis \"$tmpfile\"\n",
NULL};
int lightmode = 0;
#define COL_GRAY1 "'#222222'"
#define COL_GRAY2 "'#444444'"
#define COL_GRAY3 "'#bbbbbb'"
#define COL_GRAY4 "'#eeeeee'"
#define COL_CYAN "'#005577'"
#define DMENU_DARK "-l 10 -fn 'FiraCode Nerd Font:size=10' -nb " COL_GRAY1 " -nf " COL_GRAY3 " -sb " COL_CYAN " -sf " COL_GRAY4
#define DMENU_LIGHT "-l 10 -fn 'FiraCode Nerd Font:size=10' -nb " COL_GRAY3 " -nf " COL_GRAY1 " -sb " COL_CYAN " -sf " COL_GRAY4
static char *externalcopydarkcmd[] = { "/bin/sh", "-c",
"sed 's/[ \t][ \t]*/\\n/g' | tac | awk 'BEGIN{x[\"\"]++} !x[$0]++' | dmenu " DMENU_DARK " -w $WINDOWID -p Copy | tr -d '\\n' | vis-clipboard --copy",
NULL};
static char *externalpastedarkcmd[] = { "/bin/sh", "-c",
"sed 's/[ \t][ \t]*/\\n/g' | tac | awk 'BEGIN{x[\"\"]++} !x[$0]++' | dmenu " DMENU_DARK " -w $WINDOWID -p Paste | tr -d '\\n'",
NULL};
static char *externalcopylightcmd[] = { "/bin/sh", "-c",
"sed 's/[ \t][ \t]*/\\n/g' | tac | awk 'BEGIN{x[\"\"]++} !x[$0]++' | dmenu " DMENU_LIGHT " -w $WINDOWID -p Copy | tr -d '\\n' | vis-clipboard --copy",
NULL};
static char *externalpastelightcmd[] = { "/bin/sh", "-c",
"sed 's/[ \t][ \t]*/\\n/g' | tac | awk 'BEGIN{x[\"\"]++} !x[$0]++' | dmenu " DMENU_LIGHT " -w $WINDOWID -p Paste | tr -d '\\n'",
NULL};
/* /*
* What program is execed by st depends of these precedence rules: * What program is execed by st depends of these precedence rules:
* 1: program passed with -e * 1: program passed with -e
@@ -93,87 +127,46 @@ char *termname = "st-256color";
*/ */
unsigned int tabspaces = 8; unsigned int tabspaces = 8;
typedef struct { /* Terminal colors (16 first used in escape sequence) */
const char* const colors[258]; /* terminal colors */ static const char *colorname[] = {
unsigned int fg; /* foreground */ /* 8 normal colors */
unsigned int bg; /* background */ "black",
unsigned int cs; /* cursor */ "red3",
unsigned int rcs; /* reverse cursor */ "green3",
} ColorScheme; "yellow3",
/* "blue2",
* Terminal colors (16 first used in escape sequence, "magenta3",
* 2 last for custom cursor color), "cyan3",
* foreground, background, cursor, reverse cursor "gray90",
*/
static const ColorScheme schemes[] = {
// st (dark)
{{"black", "red3", "green3", "yellow3",
"blue2", "magenta3", "cyan3", "gray90",
"gray50", "red", "green", "yellow",
"#5c5cff", "magenta", "cyan", "white",
[256]="#cccccc", "#555555"}, 7, 0, 256, 257},
// Alacritty (dark) /* 8 bright colors */
{{"#1d1f21", "#cc6666", "#b5bd68", "#f0c674", "gray50",
"#81a2be", "#b294bb", "#8abeb7", "#c5c8c6", "red",
"#666666", "#d54e53", "#b9ca4a", "#e7c547", "green",
"#7aa6da", "#c397d8", "#70c0b1", "#eaeaea", "yellow",
[256]="#cccccc", "#555555"}, 7, 0, 256, 257}, "#5c5cff",
"magenta",
"cyan",
"white",
// One Half dark [255] = 0,
{{"#282c34", "#e06c75", "#98c379", "#e5c07b",
"#61afef", "#c678dd", "#56b6c2", "#dcdfe4",
"#282c34", "#e06c75", "#98c379", "#e5c07b",
"#61afef", "#c678dd", "#56b6c2", "#dcdfe4",
[256]="#cccccc", "#555555"}, 7, 0, 256, 257},
// One Half light /* more colors can be added after 255 to use with DefaultXX */
{{"#fafafa", "#e45649", "#50a14f", "#c18401", "#cccccc",
"#0184bc", "#a626a4", "#0997b3", "#383a42", "#555555",
"#fafafa", "#e45649", "#50a14f", "#c18401", "gray90", /* default foreground colour */
"#0184bc", "#a626a4", "#0997b3", "#383a42", "black", /* default background colour */
[256]="#cccccc", "#555555"}, 7, 0, 256, 257},
// Solarized dark
{{"#073642", "#dc322f", "#859900", "#b58900",
"#268bd2", "#d33682", "#2aa198", "#eee8d5",
"#002b36", "#cb4b16", "#586e75", "#657b83",
"#839496", "#6c71c4", "#93a1a1", "#fdf6e3",
[256]="#93a1a1", "#fdf6e3"}, 12, 8, 256, 257},
// Solarized light
{{"#eee8d5", "#dc322f", "#859900", "#b58900",
"#268bd2", "#d33682", "#2aa198", "#073642",
"#fdf6e3", "#cb4b16", "#93a1a1", "#839496",
"#657b83", "#6c71c4", "#586e75", "#002b36",
[256]="#586e75", "#002b36"}, 12, 8, 256, 257},
// Gruvbox dark
{{"#282828", "#cc241d", "#98971a", "#d79921",
"#458588", "#b16286", "#689d6a", "#a89984",
"#928374", "#fb4934", "#b8bb26", "#fabd2f",
"#83a598", "#d3869b", "#8ec07c", "#ebdbb2",
[256]="#ebdbb2", "#555555"}, 15, 0, 256, 257},
// Gruvbox light
{{"#fbf1c7", "#cc241d", "#98971a", "#d79921",
"#458588", "#b16286", "#689d6a", "#7c6f64",
"#928374", "#9d0006", "#79740e", "#b57614",
"#076678", "#8f3f71", "#427b58", "#3c3836",
[256]="#3c3836", "#555555"}, 15, 0, 256, 257},
}; };
static const char * const * colorname;
int colorscheme = 0;
/* /*
* Default colors (colorname index) * Default colors (colorname index)
* foreground, background, cursor, reverse cursor * foreground, background, cursor, reverse cursor
*/ */
unsigned int defaultfg; unsigned int defaultfg = 258;
unsigned int defaultbg; unsigned int defaultbg = 259;
unsigned int defaultcs; unsigned int defaultcs = 256;
static unsigned int defaultrcs; static unsigned int defaultrcs = 257;
/* /*
* Default shape of cursor * Default shape of cursor
@@ -240,18 +233,11 @@ static Shortcut shortcuts[] = {
{ TERMMOD, XK_C, clipcopy, {.i = 0} }, { TERMMOD, XK_C, clipcopy, {.i = 0} },
{ TERMMOD, XK_V, clippaste, {.i = 0} }, { TERMMOD, XK_V, clippaste, {.i = 0} },
{ TERMMOD, XK_Y, selpaste, {.i = 0} }, { TERMMOD, XK_Y, selpaste, {.i = 0} },
{ TERMMOD, XK_E, externalpipe, { .v = externaleditcmd } },
{ TERMMOD, XK_K, externalpipecopy, { 0 } },
{ TERMMOD, XK_I, externalpipepaste, { 0 } },
{ ShiftMask, XK_Insert, selpaste, {.i = 0} }, { ShiftMask, XK_Insert, selpaste, {.i = 0} },
{ TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, { TERMMOD, XK_Num_Lock, numlock, {.i = 0} },
{ MODKEY|ControlMask, XK_1, selectscheme, {.i = 0} },
{ MODKEY|ControlMask, XK_2, selectscheme, {.i = 1} },
{ MODKEY|ControlMask, XK_3, selectscheme, {.i = 2} },
{ MODKEY|ControlMask, XK_4, selectscheme, {.i = 3} },
{ MODKEY|ControlMask, XK_5, selectscheme, {.i = 4} },
{ MODKEY|ControlMask, XK_6, selectscheme, {.i = 5} },
{ MODKEY|ControlMask, XK_7, selectscheme, {.i = 6} },
{ MODKEY|ControlMask, XK_8, selectscheme, {.i = 7} },
{ MODKEY|ControlMask, XK_9, nextscheme, {.i = -1} },
{ MODKEY|ControlMask, XK_0, nextscheme, {.i = +1} },
{ ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} },
{ ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} },
}; };
@@ -525,3 +511,14 @@ static char ascii_printable[] =
" !\"#$%&'()*+,-./0123456789:;<=>?" " !\"#$%&'()*+,-./0123456789:;<=>?"
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
"`abcdefghijklmnopqrstuvwxyz{|}~"; "`abcdefghijklmnopqrstuvwxyz{|}~";
/*
* Open urls starting with urlprefixes, contatining urlchars
* by passing as ARG1 to urlhandler.
*/
char* urlhandler = "xdg-open";
char urlchars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-._~:/?#@!$&'*+,;=%";
char* urlprefixes[] = {"http://", "https://", NULL};

228
st.c
View File

@@ -652,6 +652,97 @@ getsel(void)
return str; return str;
} }
char *
strstrany(char* s, char** strs) {
char *match;
for (int i = 0; strs[i]; i++) {
if ((match = strstr(s, strs[i]))) {
return match;
}
}
return NULL;
}
void
highlighturls(void)
{
char *match;
char *linestr = calloc(sizeof(char), term.col+1); /* assume ascii */
for (int i = term.top; i < term.bot; i++) {
int url_start = -1;
for (int j = 0; j < term.col; j++) {
if (TLINE(i)[j].u < 127) {
linestr[j] = TLINE(i)[j].u;
}
linestr[term.col] = '\0';
}
while ((match = strstrany(linestr + url_start + 1, urlprefixes))) {
url_start = match - linestr;
for (int c = url_start; c < term.col && strchr(urlchars, linestr[c]); c++) {
TLINE(i)[c].mode |= ATTR_URL;
tsetdirt(i, c);
}
}
}
free(linestr);
}
void
unhighlighturls(void)
{
for (int i = term.top; i < term.bot; i++) {
for (int j = 0; j < term.col; j++) {
Glyph* g = &TLINE(i)[j];
if (g->mode & ATTR_URL) {
g->mode &= ~ATTR_URL;
tsetdirt(i, j);
}
}
}
return;
}
void
followurl(int x, int y) {
char *linestr = calloc(sizeof(char), term.col+1); /* assume ascii */
char *match;
for (int i = 0; i < term.col; i++) {
if (TLINE(x)[i].u < 127) {
linestr[i] = TLINE(x)[i].u;
}
linestr[term.col] = '\0';
}
int url_start = -1;
while ((match = strstrany(linestr + url_start + 1, urlprefixes))) {
url_start = match - linestr;
int url_end = url_start;
for (int c = url_start; c < term.col && strchr(urlchars, linestr[c]); c++) {
url_end++;
}
if (url_start <= y && y < url_end) {
linestr[url_end] = '\0';
break;
}
}
if (url_start == -1) {
free(linestr);
return;
}
pid_t chpid;
if ((chpid = fork()) == 0) {
if (fork() == 0)
execlp(urlhandler, urlhandler, linestr + url_start, NULL);
exit(1);
}
/* // handled by sigchld()
if (chpid > 0)
waitpid(chpid, NULL, 0);
*/
free(linestr);
unhighlighturls();
}
void void
selclear(void) selclear(void)
{ {
@@ -734,8 +825,14 @@ sigchld(int a)
if ((p = waitpid(pid, &stat, WNOHANG)) < 0) if ((p = waitpid(pid, &stat, WNOHANG)) < 0)
die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); 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; return;
}
if (WIFEXITED(stat) && WEXITSTATUS(stat)) if (WIFEXITED(stat) && WEXITSTATUS(stat))
die("child exited with status %d\n", WEXITSTATUS(stat)); die("child exited with status %d\n", WEXITSTATUS(stat));
@@ -819,7 +916,7 @@ ttynew(const char *line, char *cmd, const char *out, char **args)
break; break;
default: default:
#ifdef __OpenBSD__ #ifdef __OpenBSD__
if (pledge("stdio rpath tty proc", NULL) == -1) if (pledge("stdio rpath tty proc exec", NULL) == -1)
die("pledge\n"); die("pledge\n");
#endif #endif
close(s); close(s);
@@ -2110,6 +2207,111 @@ strparse(void)
} }
} }
void
externalpipeinout(const Arg *arg, void (*processout)(int fildes))
{
int to[2], from[2];
char buf[UTF_SIZ];
void (*oldsigpipe)(int);
Glyph *bp, *end;
int lastpos, n, newline;
if (pipe(to) == -1)
return;
if (processout != NULL && pipe(from) == -1)
return;
switch (fork()) {
case -1:
close(to[0]);
close(to[1]);
if (processout != NULL) {
close(from[0]);
close(from[1]);
}
return;
case 0:
dup2(to[0], STDIN_FILENO);
close(to[0]);
close(to[1]);
if (processout != NULL) {
dup2(from[1], STDOUT_FILENO);
close(from[0]);
close(from[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]);
if (processout != NULL)
close(from[1]);
/* ignore sigpipe for now, in case child exists early */
oldsigpipe = signal(SIGPIPE, SIG_IGN);
newline = 0;
for (n = 0; n < term.row; n++) {
bp = TLINE(n);
lastpos = MIN(tlinelen(n) + 1, term.col) - 1;
if (lastpos < 0)
break;
end = &bp[lastpos + 1];
for (; bp < end; ++bp)
if (xwrite(to[1], buf, utf8encode(bp->u, buf)) < 0)
break;
if ((newline = TLINE(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]);
if (processout != NULL) {
processout(from[0]);
close(from[0]);
}
/* restore */
signal(SIGPIPE, oldsigpipe);
}
void
externalpipe(const Arg *arg)
{
externalpipeinout(arg, NULL);
}
void
ttycopy(int fildes)
{
char buf[256];
int len;
len = read(fildes, buf, sizeof(buf));
if (len > 0 && len < sizeof(buf)) {
buf[len] = '\0';
ttywrite(buf, len, 1);
}
}
void
externalpipepaste(const Arg *)
{
Arg arg;
arg.v = externalpastecmd();
externalpipeinout(&arg, ttycopy);
}
void
externalpipecopy(const Arg *)
{
Arg arg;
arg.v = externalcopycmd();
externalpipeinout(&arg, NULL);
}
void void
strdump(void) strdump(void)
{ {
@@ -2291,28 +2493,6 @@ tstrsequence(uchar c)
term.esc |= ESC_STR; term.esc |= ESC_STR;
} }
void
tupdatebgcolor(int oldbg, int newbg)
{
for (int y = 0; y < term.row; y++) {
for (int x = 0; x < term.col; x++) {
if (TLINE(y)[x].bg == oldbg)
TLINE(y)[x].bg = newbg;
}
}
}
void
tupdatefgcolor(int oldfg, int newfg)
{
for (int y = 0; y < term.row; y++) {
for (int x = 0; x < term.col; x++) {
if (TLINE(y)[x].fg == oldfg)
TLINE(y)[x].fg = newfg;
}
}
}
void void
tcontrolcode(uchar ascii) tcontrolcode(uchar ascii)
{ {

46
st.h
View File

@@ -10,14 +10,14 @@
#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
#define DEFAULT(a, b) (a) = (a) ? (a) : (b) #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) #define LIMIT(x, a, b) (x) = (x)<(a) ? (a) : (x)>(b) ? (b) : (x)
#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ #define ATTRCMP(a, b) \
(a).bg != (b).bg) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg)
#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ #define TIMEDIFF(t1, t2) \
(t1.tv_nsec-t2.tv_nsec)/1E6) ((t1.tv_sec - t2.tv_sec) * 1000 + (t1.tv_nsec - t2.tv_nsec) / 1E6)
#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
#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))
#define HISTSIZE 2000 #define HISTSIZE 2000
@@ -35,23 +35,14 @@ enum glyph_attribute {
ATTR_WIDE = 1 << 9, ATTR_WIDE = 1 << 9,
ATTR_WDUMMY = 1 << 10, ATTR_WDUMMY = 1 << 10,
ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
ATTR_URL = 1 << 14,
}; };
enum selection_mode { enum selection_mode { SEL_IDLE = 0, SEL_EMPTY = 1, SEL_READY = 2 };
SEL_IDLE = 0,
SEL_EMPTY = 1,
SEL_READY = 2
};
enum selection_type { enum selection_type { SEL_REGULAR = 1, SEL_RECTANGULAR = 2 };
SEL_REGULAR = 1,
SEL_RECTANGULAR = 2
};
enum selection_snap { enum selection_snap { SNAP_WORD = 1, SNAP_LINE = 2 };
SNAP_WORD = 1,
SNAP_LINE = 2
};
typedef unsigned char uchar; typedef unsigned char uchar;
typedef unsigned int uint; typedef unsigned int uint;
@@ -82,6 +73,9 @@ void die(const char *, ...);
void redraw(void); void redraw(void);
void draw(void); void draw(void);
void externalpipe(const Arg *);
void externalpipecopy(const Arg *);
void externalpipepaste(const Arg *);
void printscreen(const Arg *); void printscreen(const Arg *);
void printsel(const Arg *); void printsel(const Arg *);
void sendbreak(const Arg *); void sendbreak(const Arg *);
@@ -91,8 +85,6 @@ int tattrset(int);
void tnew(int, int); void tnew(int, int);
void tresize(int, int); void tresize(int, int);
void tsetdirtattr(int); void tsetdirtattr(int);
void tupdatebgcolor(int, int);
void tupdatefgcolor(int, int);
void ttyhangup(void); void ttyhangup(void);
int ttynew(const char *, char *, const char *, char **); int ttynew(const char *, char *, const char *, char **);
size_t ttyread(void); size_t ttyread(void);
@@ -108,6 +100,10 @@ void selextend(int, int, int, int);
int selected(int, int); int selected(int, int);
char *getsel(void); char *getsel(void);
void highlighturls(void);
void unhighlighturls(void);
void followurl(int, int);
size_t utf8encode(Rune, char *); size_t utf8encode(Rune, char *);
void *xmalloc(size_t); void *xmalloc(size_t);
@@ -122,8 +118,16 @@ extern char *vtiden;
extern wchar_t *worddelimiters; extern wchar_t *worddelimiters;
extern int allowaltscreen; extern int allowaltscreen;
extern int allowwindowops; extern int allowwindowops;
extern int lightmode;
extern char *termname; extern char *termname;
extern unsigned int tabspaces; extern unsigned int tabspaces;
extern unsigned int defaultfg; extern unsigned int defaultfg;
extern unsigned int defaultbg; extern unsigned int defaultbg;
extern unsigned int defaultcs; extern unsigned int defaultcs;
extern char *urlhandler;
extern char urlchars[];
extern char *urlprefixes[];
extern int nurlprefixes;
char **externalcopycmd();
char **externalpastecmd();

206
x.c
View File

@@ -14,6 +14,7 @@
#include <X11/keysym.h> #include <X11/keysym.h>
#include <X11/Xft/Xft.h> #include <X11/Xft/Xft.h>
#include <X11/XKBlib.h> #include <X11/XKBlib.h>
#include <X11/Xresource.h>
char *argv0; char *argv0;
#include "arg.h" #include "arg.h"
@@ -59,14 +60,22 @@ static void zoom(const Arg *);
static void zoomabs(const Arg *); static void zoomabs(const Arg *);
static void zoomreset(const Arg *); static void zoomreset(const Arg *);
static void ttysend(const Arg *); static void ttysend(const Arg *);
static void nextscheme(const Arg *);
static void selectscheme(const Arg *);
void kscrollup(const Arg *); void kscrollup(const Arg *);
void kscrolldown(const Arg *); void kscrolldown(const Arg *);
/* config.h for applying patches and the configuration. */ /* config.h for applying patches and the configuration. */
#include "config.h" #include "config.h"
char **externalcopycmd()
{
return (lightmode) ? externalcopylightcmd : externalcopydarkcmd;
}
char **externalpastecmd()
{
return (lightmode) ? externalpastelightcmd : externalpastedarkcmd;
}
/* XEMBED messages */ /* XEMBED messages */
#define XEMBED_FOCUS_IN 4 #define XEMBED_FOCUS_IN 4
#define XEMBED_FOCUS_OUT 5 #define XEMBED_FOCUS_OUT 5
@@ -189,13 +198,13 @@ static void mousesel(XEvent *, int);
static void mousereport(XEvent *); static void mousereport(XEvent *);
static char *kmap(KeySym, uint); static char *kmap(KeySym, uint);
static int match(uint, uint); static int match(uint, uint);
static void updatescheme(void);
static void run(void); static void run(void);
static void usage(void); static void usage(void);
static void (*handler[LASTEvent])(XEvent *) = { static void (*handler[LASTEvent])(XEvent *) = {
[KeyPress] = kpress, [KeyPress] = kpress,
[KeyRelease] = kpress,
[ClientMessage] = cmessage, [ClientMessage] = cmessage,
[ConfigureNotify] = resize, [ConfigureNotify] = resize,
[VisibilityNotify] = visibility, [VisibilityNotify] = visibility,
@@ -457,6 +466,15 @@ mouseaction(XEvent *e, uint release)
/* ignore Button<N>mask for Button<N> - it's set on release */ /* ignore Button<N>mask for Button<N> - it's set on release */
uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); uint state = e->xbutton.state & ~buttonmask(e->xbutton.button);
if (release == 0 &&
e->xbutton.button == Button1 &&
(match(ControlMask, state) ||
match(ControlMask, state & ~forcemousemod))) {
followurl(evrow(e), evcol(e));
return 1;
}
for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
if (ms->release == release && if (ms->release == release &&
ms->button == e->xbutton.button && ms->button == e->xbutton.button &&
@@ -806,7 +824,7 @@ xloadcols(void)
for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp)
XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); XftColorFree(xw.dpy, xw.vis, xw.cmap, cp);
} else { } else {
dc.collen = 258; dc.collen = MAX(LEN(colorname), 256);
dc.col = xmalloc(dc.collen * sizeof(Color)); dc.col = xmalloc(dc.collen * sizeof(Color));
} }
@@ -874,8 +892,8 @@ xhints(void)
sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize;
sizeh->height = win.h; sizeh->height = win.h;
sizeh->width = win.w; sizeh->width = win.w;
sizeh->height_inc = win.ch; sizeh->height_inc = 1;
sizeh->width_inc = win.cw; sizeh->width_inc = 1;
sizeh->base_height = 2 * borderpx; sizeh->base_height = 2 * borderpx;
sizeh->base_width = 2 * borderpx; sizeh->base_width = 2 * borderpx;
sizeh->min_height = win.ch + 2 * borderpx; sizeh->min_height = win.ch + 2 * borderpx;
@@ -1500,7 +1518,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
XftDrawGlyphFontSpec(xw.draw, fg, specs, len); XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
/* Render underline and strikethrough. */ /* Render underline and strikethrough. */
if (base.mode & ATTR_UNDERLINE) { if (base.mode & ATTR_UNDERLINE || base.mode & ATTR_URL) {
XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1, XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1,
width, 1); width, 1);
} }
@@ -1864,6 +1882,18 @@ kpress(XEvent *ev)
} else { } else {
len = XLookupString(e, buf, sizeof buf, &ksym, NULL); len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
} }
/* 0. highlight URLs when control held */
if (ksym == XK_Control_L) {
highlighturls();
} else if (ev->type == KeyRelease && e->keycode == XKeysymToKeycode(e->display, XK_Control_L)) {
unhighlighturls();
}
/* KeyRelease not relevant to shortcuts */
if (ev->type == KeyRelease)
return;
/* 1. shortcuts */ /* 1. shortcuts */
for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
if (ksym == bp->keysym && match(bp->mod, e->state)) { if (ksym == bp->keysym && match(bp->mod, e->state)) {
@@ -2028,6 +2058,119 @@ run(void)
} }
} }
#define XRESOURCE_LOAD_META(NAME) \
if(!XrmGetResource(xrdb, "st." NAME, "st." NAME, &type, &ret)) \
XrmGetResource(xrdb, "*." NAME, "*." NAME, &type, &ret); \
if (ret.addr != NULL && !strncmp("String", type, 64))
#define XRESOURCE_LOAD_STRING(NAME, DST) \
XRESOURCE_LOAD_META(NAME) \
DST = ret.addr;
#define XRESOURCE_LOAD_CHAR(NAME, DST) \
XRESOURCE_LOAD_META(NAME) \
DST = ret.addr[0];
#define XRESOURCE_LOAD_INTEGER(NAME, DST) \
XRESOURCE_LOAD_META(NAME) \
DST = strtoul(ret.addr, NULL, 10);
#define XRESOURCE_LOAD_FLOAT(NAME, DST) \
XRESOURCE_LOAD_META(NAME) \
DST = strtof(ret.addr, NULL);
void
xrdb_load(void)
{
/* XXX */
char *xrm;
char *type;
XrmDatabase xrdb;
XrmValue ret;
Display *dpy;
if(!(dpy = XOpenDisplay(NULL)))
die("Can't open display\n");
XrmInitialize();
xrm = XResourceManagerString(dpy);
if (xrm != NULL) {
xrdb = XrmGetStringDatabase(xrm);
/* handling colors here without macros to do via loop. */
int i = 0;
char loadValue[12] = "";
for (i = 0; i < 256; i++)
{
sprintf(loadValue, "%s%d", "st.color", i);
if(!XrmGetResource(xrdb, loadValue, loadValue, &type, &ret))
{
sprintf(loadValue, "%s%d", "*.color", i);
if (!XrmGetResource(xrdb, loadValue, loadValue, &type, &ret))
/* reset if not found (unless in range for defaults). */
if (i > 15)
colorname[i] = NULL;
}
if (ret.addr != NULL && !strncmp("String", type, 64))
colorname[i] = ret.addr;
}
XRESOURCE_LOAD_STRING("foreground", colorname[defaultfg]);
XRESOURCE_LOAD_STRING("background", colorname[defaultbg]);
XRESOURCE_LOAD_STRING("cursorColor", colorname[defaultcs])
else {
// this looks confusing because we are chaining off of the if
// in the macro. probably we should be wrapping everything blocks
// so this isn't possible...
defaultcs = defaultfg;
}
XRESOURCE_LOAD_STRING("reverse-cursor", colorname[defaultrcs])
else {
// see above.
defaultrcs = defaultbg;
}
XRESOURCE_LOAD_STRING("font", font);
XRESOURCE_LOAD_STRING("termname", termname);
XRESOURCE_LOAD_INTEGER("blinktimeout", blinktimeout);
XRESOURCE_LOAD_INTEGER("bellvolume", bellvolume);
XRESOURCE_LOAD_INTEGER("borderpx", borderpx);
XRESOURCE_LOAD_INTEGER("cursorshape", cursorshape);
XRESOURCE_LOAD_INTEGER("lightmode", lightmode);
XRESOURCE_LOAD_FLOAT("cwscale", cwscale);
XRESOURCE_LOAD_FLOAT("chscale", chscale);
}
XFlush(dpy);
}
void
reload(int sig)
{
xrdb_load();
/* colors, fonts */
xloadcols();
xunloadfonts();
xloadfonts(font, 0);
/* pretend the window just got resized */
cresize(win.w, win.h);
redraw();
/* triggers re-render if we're visible. */
ttywrite("\033[O", 3, 1);
signal(SIGUSR1, reload);
}
void void
usage(void) usage(void)
{ {
@@ -2041,47 +2184,6 @@ usage(void)
" [stty_args ...]\n", argv0, argv0); " [stty_args ...]\n", argv0, argv0);
} }
void
nextscheme(const Arg *arg)
{
colorscheme += arg->i;
if (colorscheme >= (int)LEN(schemes))
colorscheme = 0;
else if (colorscheme < 0)
colorscheme = LEN(schemes) - 1;
updatescheme();
}
void
selectscheme(const Arg *arg)
{
if (BETWEEN(arg->i, 0, LEN(schemes)-1)) {
colorscheme = arg->i;
updatescheme();
}
}
void
updatescheme(void)
{
int oldbg, oldfg;
oldbg = defaultbg;
oldfg = defaultfg;
colorname = schemes[colorscheme].colors;
defaultbg = schemes[colorscheme].bg;
defaultfg = schemes[colorscheme].fg;
defaultcs = schemes[colorscheme].cs;
defaultrcs = schemes[colorscheme].rcs;
xloadcols();
if (defaultbg != oldbg)
tupdatebgcolor(oldbg, defaultbg);
if (defaultfg != oldfg)
tupdatefgcolor(oldfg, defaultfg);
cresize(win.w, win.h);
redraw();
}
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
@@ -2134,12 +2236,6 @@ main(int argc, char *argv[])
} ARGEND; } ARGEND;
run: run:
colorname = schemes[colorscheme].colors;
defaultbg = schemes[colorscheme].bg;
defaultfg = schemes[colorscheme].fg;
defaultcs = schemes[colorscheme].cs;
defaultrcs = schemes[colorscheme].rcs;
if (argc > 0) /* eat all remaining arguments */ if (argc > 0) /* eat all remaining arguments */
opt_cmd = argv; opt_cmd = argv;
@@ -2148,6 +2244,8 @@ run:
setlocale(LC_CTYPE, ""); setlocale(LC_CTYPE, "");
XSetLocaleModifiers(""); XSetLocaleModifiers("");
xrdb_load();
signal(SIGUSR1, reload);
cols = MAX(cols, 1); cols = MAX(cols, 1);
rows = MAX(rows, 1); rows = MAX(rows, 1);
tnew(cols, rows); tnew(cols, rows);