5 Commits

Author SHA1 Message Date
3919a18b2b st: replace Solarize dark with lupan Dark 2025-09-13 23:18:19 +02:00
b92efccbab change font and color scheme keys 2025-09-11 22:26:09 +02:00
57940e9403 fix: agree st-colorschemes with st-scrollback-ringbuffer 2025-09-10 23:41:52 +02:00
6ba0170dd6 Terminal scrollback with ring buffer
(apply st-scrollback-ringbuffer-0.9.2.diff)

from the patch header:

commit 0663bdf11a409961da5b1120741a69814da8ce65
Author: Timo Röhling <timo@gaussglocke.de>
Date:   Tue Nov 23 19:45:33 2021 +0100

    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).

    The scrollback buffer is limited to HISTSIZE lines in order to bound
    memory usage. As the lines are allocated on demand, it is possible to
    implement unlimited scrollback with few changes.  If the terminal is
    reset, the scroll back buffer is reset, too.
2025-09-10 23:20:16 +02:00
Max Schillinger
04ea60ca8e Add multiple color schemes and key bindings to change them
This commits adds these color schemes:

- the default (dark) st color scheme
- the default (dark) alacritty color scheme
- One Half (dark & light)
- Solarized (dark & light)
- Gruvbox (dark & light)

Select one with Alt+1..8.
Select the next one with Alt+0.
Select the previous one with Ctrl+Alt+0.
2025-09-10 23:16:05 +02:00
10 changed files with 580 additions and 1204 deletions

View File

@@ -15,7 +15,7 @@ config.h:
.c.o:
$(CC) $(STCFLAGS) -c $<
st.o: config.h st.h win.h normalMode.h normalMode.c utils.h
st.o: config.h st.h win.h
x.o: arg.h config.h st.h win.h
$(OBJ): config.h config.mk

View File

@@ -5,51 +5,9 @@
*
* font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
*/
static char *fonts[] = {
"FiraCode Nerd Font:size=10",
"Monaspace Neon:size=10",
"Monaspace Argon:size=10",
"Monaspace Xenon:size=10",
"Monaspace Radon:size=10",
"Monaspace Krypton:size=10",
};
static size_t currentfont = 0;
static char *font = "FiraCode Nerd Font:size=10";
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:
* 1: program passed with -e
@@ -135,60 +93,87 @@ char *termname = "st-256color";
*/
unsigned int tabspaces = 8;
/* Terminal colors (16 first used in escape sequence) */
static const char *colorname[] = {
/* 8 normal colors */
"black",
"red3",
"green3",
"yellow3",
"blue2",
"magenta3",
"cyan3",
"gray90",
typedef struct {
const char* const colors[258]; /* terminal colors */
unsigned int fg; /* foreground */
unsigned int bg; /* background */
unsigned int cs; /* cursor */
unsigned int rcs; /* reverse cursor */
} ColorScheme;
/*
* Terminal colors (16 first used in escape sequence,
* 2 last for custom cursor color),
* foreground, background, cursor, reverse cursor
*/
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},
/* 8 bright colors */
"gray50",
"red",
"green",
"yellow",
"#5c5cff",
"magenta",
"cyan",
"white",
// Alacritty (dark)
{{"#1d1f21", "#cc6666", "#b5bd68", "#f0c674",
"#81a2be", "#b294bb", "#8abeb7", "#c5c8c6",
"#666666", "#d54e53", "#b9ca4a", "#e7c547",
"#7aa6da", "#c397d8", "#70c0b1", "#eaeaea",
[256]="#cccccc", "#555555"}, 7, 0, 256, 257},
[255] = 0,
// One Half dark
{{"#282c34", "#e06c75", "#98c379", "#e5c07b",
"#61afef", "#c678dd", "#56b6c2", "#dcdfe4",
"#282c34", "#e06c75", "#98c379", "#e5c07b",
"#61afef", "#c678dd", "#56b6c2", "#dcdfe4",
[256]="#cccccc", "#555555"}, 7, 0, 256, 257},
/* more colors can be added after 255 to use with DefaultXX */
"#cccccc",
"#555555",
"gray90", /* default foreground colour */
"black", /* default background colour */
// One Half light
{{"#fafafa", "#e45649", "#50a14f", "#c18401",
"#0184bc", "#a626a4", "#0997b3", "#383a42",
"#fafafa", "#e45649", "#50a14f", "#c18401",
"#0184bc", "#a626a4", "#0997b3", "#383a42",
[256]="#cccccc", "#555555"}, 7, 0, 256, 257},
// Lupan dark
{{"#1f212e","#862d2d", "#3a783a", "#707010",
"#345eb2","#cc66cc", "#3a7878", "#a6a6a6",
"#4d4d4d","#c27070", "#40bf40", "#acac53",
"#6b8ac7","#8f248f", "#5ebaba", "#e6fefe",
[256]="#bf8040", "#555555"}, 7, 0, 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)
* foreground, background, cursor, reverse cursor
*/
unsigned int defaultfg = 258;
unsigned int defaultbg = 259;
unsigned int defaultcs = 256;
static unsigned int defaultrcs = 257;
unsigned int const currentBg = 179, currentFg = 0, buffSize = 2048;
/// Enable double / triple click yanking / selection of word / line.
int const mouseYank = 1, mouseSelect = 0;
/// [Vim Browse] Colors for search results currently on screen.
unsigned int const highlightBg = 160, highlightFg = 15;
char const wDelS[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", wDelL[] = " \t";
char *nmKeys [] = { ///< Shortcusts executed in normal mode
"P/lupan@\nN", "F/: error:\nN", "f/: error:\n", "DQf"
};
unsigned int const amountNmKeys = sizeof(nmKeys) / sizeof(*nmKeys);
/// Style of the {command, search} string shown in the right corner (y,v,V,/)
Glyph styleSearch = {' ', ATTR_ITALIC | ATTR_BOLD_FAINT, 7, 16};
Glyph style[] = {{' ',ATTR_ITALIC|ATTR_FAINT,15,16}, {' ',ATTR_ITALIC,232,11},
{' ', ATTR_ITALIC, 232, 4}, {' ', ATTR_ITALIC, 232, 12}};
unsigned int defaultfg;
unsigned int defaultbg;
unsigned int defaultcs;
static unsigned int defaultrcs;
/*
* Default shape of cursor
@@ -255,13 +240,20 @@ static Shortcut shortcuts[] = {
{ TERMMOD, XK_C, clipcopy, {.i = 0} },
{ TERMMOD, XK_V, clippaste, {.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} },
{ TERMMOD, XK_Num_Lock, numlock, {.i = 0} },
{ MODKEY, XK_c, normalMode, {.i = 0} },
{ TERMMOD, XK_S, cyclefonts, {} },
{ 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_Down, kscrolldown, {.i = -1} },
};
/*
@@ -533,14 +525,3 @@ static char ascii_printable[] =
" !\"#$%&'()*+,-./0123456789:;<=>?"
"@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};

View File

@@ -1,284 +0,0 @@
#include <X11/keysym.h>
#include <X11/XKBlib.h>
#include "normalMode.h"
#include "utils.h"
extern Glyph const styleSearch, style[];
extern char const wDelS[], wDelL[], *nmKeys[];
extern unsigned int bg[], fg, currentBg, currentFg, highlightBg, highlightFg, amountNmKeys;
typedef struct { int p[3]; } Pos;
typedef enum {visual='v', visualLine='V', yank = 'y'} Op;
typedef enum {infix_none=0, infix_i='i', infix_a='a'} Infix;
typedef enum {fw='/', bw='?'} Search;
struct NormalModeState {
struct OperationState { Op op; Infix infix; } cmd;
struct MotionState { uint32_t c; int active; Pos searchPos; Search search; } m;
} defaultNormalMode, state;
DynamicArray searchStr=UTF8_ARRAY, cCmd=UTF8_ARRAY, lCmd=UTF8_ARRAY;
Glyph styleCmd;
char posBuffer[10], braces[6][3] = { {"()"}, {"<>"}, {"{}"}, {"[]"}, {"\"\""}, {"''"}};
int exited=1, overlay=1;
static inline Rune cChar() { return term.line[term.c.y][term.c.x].u; }
static inline int pos(int p, int h) {return IS_SET(MODE_ALTSCREEN)?p:rangeY(p+h*histOff-insertOff);}
static inline int contains(Rune l, char const * values, size_t const memSize) {
for (uint32_t i = 0; i < memSize; ++i) if (l == values[i]) return 1;
return 0;
}
static inline void decodeTo(char const *cs, size_t len, DynamicArray *arr) {
char *var = expand(arr);
if (!var) empty(arr); else utf8decode(cs, (Rune*)(var), len);
}
static inline void applyPos(Pos p) {
term.c.x = p.p[0], term.c.y = p.p[1];
if (!IS_SET(MODE_ALTSCREEN) && histOp) term.line = &buf[histOff = p.p[2]];
}
/// Find string in history buffer, and provide string-match-lookup for highlighting matches
static int highlighted(int x, int y) {
int const s=term.row*term.col, i=y*term.col+x, sz=size(&searchStr);
return sz && i<s && mark[i]!=sz && i+mark[i]<s && !mark[i+mark[i]];
}
static void markSearchMatches(int all) {
int sz = size(&searchStr), ox = 0, oy = 0, oi=0;
for (int y=0; sz && all && y<term.row; ++y)
for (int x=0; x<term.col; ++x) term.dirty[y] |= highlighted(x, y);
for (int y = 0, wi=0, owi=0, i=0; sz && y < term.row; ++y)
for (int x=0; x<term.col; ++x, wi%=sz, ++i, owi=wi)
if (all || term.dirty[y]) {
mark[i]=sz-(wi=(getU32(&searchStr,wi,1)==term.line[y][x].u?wi+1:0));
if (wi==1) ox=x, oy=y, oi=i; else if (!wi && owi) x=ox, y=oy, i=oi;
}
for (int y=0; sz &&all &&y<term.row; ++y)
for (int x=0; x<term.col; ++x) term.dirty[y] |= highlighted(x, y);
}
static int findString(int s, int all) {
Pos p = (Pos) {.p={term.c.x, term.c.y, IS_SET(MODE_ALTSCREEN) ? 0 : histOff}};
historyMove(s, 0, 0);
uint32_t strSz=size(&searchStr), maxIter=rows()*term.col+strSz, wIdx=0;
for (uint32_t i=0, wi = 0; wIdx<strSz && ++i<=maxIter; historyMove(s, 0, 0), wi=wIdx) {
wIdx = (getU32(&searchStr, wIdx, s>0)==cChar())?wIdx+1:0;
if (wi && !wIdx) historyMove(-(int)(s*wi), 0, 0);
}
if (wIdx == strSz && wIdx) historyMove(-(int)(s*strSz), 0, 0);
else applyPos(p);
markSearchMatches(all);
return wIdx == strSz;
}
/// Execute series of normal-mode commands from char array / decoded from dynamic array
ExitState pressKeys(char const* s, size_t e) {
ExitState x=success;
for (size_t i=0; i<e && (x=(!s[i] ? x : kPressHist(&s[i], 1, 0, NULL))); ++i);
return x;
}
static ExitState executeCommand(uint32_t *cs, size_t z) {
ExitState x=success;
char dc [32];
for (size_t i=0; i<z && (x=kPressHist(dc, utf8encode(cs[i],dc),0,NULL));++i);
return x;
}
/// Get character for overlay, if the overlay (st) has something to show, else normal char.
static void getChar(DynamicArray *st, Glyph *glyphChange, int y, int xEnd, int width, int x) {
if (x < xEnd - min(min(width,xEnd), size(st))) *glyphChange = term.line[y][x];
else if (x<xEnd) glyphChange->u = *((Rune*)(st->content + (size(st)+x-xEnd)*st->elSize));
}
/// Expand "infix" expression: for instance (w =>) l b | | v e | | y
static ExitState expandExpression(char l) { // ({ =>) l ? { \n | l | v / } \n | h | y
int a=state.cmd.infix==infix_a, yank=state.cmd.op=='y', lc=tolower(l), found=1;
state.cmd.infix = infix_none;
if(!yank && state.cmd.op!=visual && state.cmd.op!=visualLine) return failed;
char mot[11] = {'l', 0, 'b', 0, 0, 'v', 0, 'e', 0, 0, (char)(yank ? 'y' : 0)};
if (lc == 'w') mot[2] = (char) ('b' - lc + l), mot[7] = (char) ((a ? 'w' : 'e') - lc + l), mot[9]=(char)(a?'h':0);
else {
mot[1]='?', mot[3]=mot[8]='\n', mot[6]='/', mot[4]=(char)(a?0:'l'), mot[9]=(char)(a?0:'h');
for (int i=found=0; !found && i < 6; ++i)
if ((found=contains(l,braces[i],2))) mot[2]=braces[i][0], mot[7]=braces[i][1];
}
if (!found) return failed;
assign(&lCmd, &cCmd);
empty(&cCmd);
state.cmd = defaultNormalMode.cmd;
return pressKeys(mot, 11);
}
ExitState executeMotion(char const cs, KeySym const *const ks) {
state.m.c = state.m.c < 1u ? 1u : state.m.c;
if (ks && *ks == XK_d) historyMove(0, 0, term.row / 2);
else if (ks && *ks == XK_u) historyMove(0, 0, -term.row / 2);
else if (ks && *ks == XK_f) historyMove(0, 0, term.row-1+(term.c.y=0));
else if (ks && *ks == XK_b) historyMove(0, 0, -(term.c.y=term.row-1));
else if (ks && *ks == XK_h) overlay = !overlay;
else if (cs == 'K') historyMove(0, 0, -(int)state.m.c);
else if (cs == 'J') historyMove(0, 0, (int)state.m.c);
else if (cs == 'k') historyMove(0, -(int)state.m.c, 0);
else if (cs == 'j') historyMove(0, (int)state.m.c, 0);
else if (cs == 'h') historyMove(-(int)state.m.c, 0, 0);
else if (cs == 'l') historyMove( (int)state.m.c, 0, 0);
else if (cs == 'H') term.c.y = 0;
else if (cs == 'M') term.c.y = term.bot / 2;
else if (cs == 'L') term.c.y = term.bot;
else if (cs == 's' || cs == 'S') altToggle = cs == 's' ? !altToggle : 1;
else if (cs == 'G' || cs == 'g') {
if (cs == 'G') term.c = c[0] = c[IS_SET(MODE_ALTSCREEN)+1];
if (!IS_SET(MODE_ALTSCREEN)) term.line = &buf[histOff=insertOff];
} else if (cs == '0') term.c.x = 0;
else if (cs == '$') term.c.x = term.col-1;
else if (cs == 't') sel.type = sel.type==SEL_REGULAR ? SEL_RECTANGULAR : SEL_REGULAR;
else if (cs == 'n' || cs == 'N') {
int const d = ((cs=='N')!=(state.m.search==bw))?-1:1;
for (uint32_t i = state.m.c; i && findString(d, 0); --i);
} else if (contains(cs, "wWeEbB", 6)) {
int const low=cs<=90, off=tolower(cs)!='w', sgn=(tolower(cs)=='b')?-1:1;
size_t const l=strlen(wDelL), s=strlen(wDelS), maxIt=rows()*term.col;
for (int it=0, on=0; state.m.c > 0 && it < maxIt; ++it) {
// If an offset is to be performed in beginning or not in beginning, move in history.
if ((off || it) && historyMove(sgn, 0, 0)) break;
// Determine if the category of the current letter changed since last iteration.
int n = 1<<(contains(cChar(),wDelS,s) ?(2-low) :!contains(cChar(),wDelL,l)),
found = (on|=n)^n && ((off ?on^n :n)!=1);
// If a reverse offset is to be performed and this is the last letter:
if (found && off) historyMove(-sgn, 0, 0);
// Terminate iteration: reset #it and old n value #on and decrease operation count:
if (found) it=-1, on=0, --state.m.c;
}
} else return failed;
state.m.c = 0;
return state.cmd.op == yank ? exitMotion : success;
}
ExitState kPressHist(char const *cs, size_t len, int ctrl, KeySym const *kSym) {
historyOpToggle(1, 1);
int const prevYOff=IS_SET(MODE_ALTSCREEN)?0:histOff, search=state.m.search&&state.m.active,
prevAltToggle=altToggle, prevOverlay=overlay;
int const noOp=!state.cmd.op&&!state.cmd.infix, num=len==1&&BETWEEN(cs[0],48,57),
esc=kSym&&*kSym==XK_Escape, ret=(kSym&&*kSym==XK_Return)||(len==1&&cs[0]=='\n'),
quantifier=num&&(cs[0]!='0'||state.m.c), ins=!search &&noOp &&len &&cs[0]=='i';
exited = 0;
ExitState result = success;
if (esc || ret || ins) { result = exitMotion, len = 0;
} else if (kSym && *kSym == XK_BackSpace) {
if ((search || state.m.c) && size(&cCmd)) pop(&cCmd);
if (search) {
if (size(&searchStr)) pop(&searchStr);
else result = exitMotion;
if (!size(&searchStr)) tfulldirt();
applyPos(state.m.searchPos);
findString(state.m.search==fw ? 1 : -1, 1);
} else if (state.m.c) state.m.c /= 10;
len = 0;
} else if (search) {
if (len >= 1) decodeTo(cs, len, &searchStr);
applyPos(state.m.searchPos);
findString(state.m.search==fw ? 1 : -1, 1);
} else if (len == 0) { result = failed;
} else if (quantifier) { state.m.c = min(SHRT_MAX, (int)state.m.c*10+cs[0]-48);
} else if (state.cmd.infix && state.cmd.op && (result = expandExpression(cs[0]), len=0)) {
} else if (cs[0] == 'd') { state = defaultNormalMode; result = exitMotion; state.m.active = 1;
} else if (cs[0] == '.') {
if (size(&cCmd)) assign(&lCmd, &cCmd);
empty(&cCmd);
executeCommand((uint32_t*) lCmd.content, size(&lCmd));
empty(&cCmd);
len = 0;
} else if (cs[0] == 'r') { tfulldirt();
} else if (cs[0] == 'c') {
empty(&lCmd);
empty(&cCmd);
empty(&searchStr);
tfulldirt();
len = 0;
} else if (cs[0] == fw || cs[0] == bw) {
empty(&searchStr);
state.m.search = (Search) cs[0];
state.m.searchPos = (Pos){.p={term.c.x, term.c.y, prevYOff}};
state.m.active = 1;
} else if (cs[0]==infix_i || cs[0]==infix_a) { state.cmd.infix=(Infix) cs[0];
} else if (cs[0] == 'y') {
if (state.cmd.op) {
result = (state.cmd.op == yank || state.cmd.op == visualLine) ? exitOp : exitMotion;
if (state.cmd.op == yank) selstart(0, term.c.y, 0);
} else selstart(term.c.x, term.c.y, 0);
state.cmd.op = yank;
} else if (cs[0] == visual || cs[0] == visualLine) {
if (state.cmd.op != (Op) cs[0]) {
state.cmd = defaultNormalMode.cmd;
state.cmd.op = (Op) cs[0];
selstart(cs[0] == visualLine ?0 :term.c.x, term.c.y, 0);
} else result = exitOp;
} else if (!(result =executeMotion((char) (len?cs[0]:0), ctrl?kSym:NULL))) {
result=failed;
for (size_t i = 0; !ctrl && i < amountNmKeys; ++i)
if (cs[0]==nmKeys[i][0] &&
failed!=(result=pressKeys(&nmKeys[i][1], strlen(nmKeys[i])-1))) goto end;
} // Operation/Motion finished if valid: update cmd string, extend selection, update search
if (result != failed) {
if (len == 1 && !ctrl) decodeTo(cs, len, &cCmd);
if ((state.cmd.op == visualLine) || ((state.cmd.op == yank) && (result == exitOp))) {
int const off = term.c.y + (IS_SET(MODE_ALTSCREEN) ? 0 : histOff) < sel.ob.y; //< Selection start below end.
sel.ob.x = off ? term.col - 1 : 0;
selextend(off ? 0 : term.col-1, term.c.y, sel.type, 0);
} else if (sel.oe.x != -1) {
selextend(term.c.x, term.c.y, sel.type, 0);
}
} // Set repaint for motion or status bar
if (!IS_SET(MODE_ALTSCREEN) && prevYOff != histOff) tfulldirt();
// Terminate Motion / operation if thus indicated
if (result == exitMotion) {
if (!state.m.active) result = (exited=noOp) ? finish : exitOp;
state.m.active = (int) (state.m.c = 0u);
}
if (result == exitOp || result == finish) {
if (state.cmd.op == yank) {
xsetsel(getsel());
xclipcopy();
}
state = defaultNormalMode;
selclear();
if (!esc) assign(&lCmd, &cCmd);
empty(&cCmd);
} // Update the content displayed in the history overlay
styleCmd = style[state.cmd.op==yank ? 1 : (state.cmd.op==visual ? 2 :
(state.cmd.op==visualLine ? 3 :0))];
int const posLin = !IS_SET(MODE_ALTSCREEN) ? rangeY(insertOff-histOff):0, h=rows()-term.row;
if (!posLin || posLin==h || !h) strcpy(posBuffer, posLin ? " [BOT] " : " [TOP] ");
else sprintf(posBuffer, " % 3d%c ", min(100, max(0, (int)(.5 + posLin * 100. / h))),'%');
if ((overlay || overlay!=prevOverlay) && term.col>9 && term.row>4) {
if (!term.dirty[term.row-1]) xdrawline(term.line[term.row-1], term.col*2/3, term.row-1, term.col-1);
if (!term.dirty[term.row-2]) xdrawline(term.line[term.row-2], term.col*2/3, term.row-2, term.col-1);
}
if (result==finish) altToggle = 0;
if (altToggle != prevAltToggle) tswapscreen();
end:
historyOpToggle(-1, 1);
return result;
}
void historyOverlay(int x, int y, Glyph* g) {
if (!histMode) return;
TCursor const *cHist = histOp ? &term.c : &c[0];
if(overlay && term.col > 9 && term.row > 4 && (x > (2*term.col/3)) && (y >= (term.row-2))) {
*g = (y == term.row - 2) ? styleSearch : styleCmd;
if (y == term.row-2) getChar(&searchStr, g, term.row-2, term.col-2, term.col/3, x);
else if (x > term.col - 7) g->u = (Rune)(posBuffer[x - term.col + 7]);
else getChar(size(&cCmd) ?&cCmd :&lCmd, g, term.row-1, term.col-7, term.col/3-6, x);
} else if (highlighted(x, y)) g->bg = highlightBg, g->fg = highlightFg;
else if ((x==cHist->x) ^ (y==cHist->y)) { g->bg = currentBg; g->fg = currentFg; }
else if (x==cHist->x) g->mode^=ATTR_REVERSE;
}
void historyPreDraw() {
static Pos op = {.p={0, 0, 0}};
historyOpToggle(1, 0);
// Draw the cursor cross if changed
if (term.c.y >= term.row || op.p[1] >= term.row) tfulldirt();
else if (exited || (op.p[1] != term.c.y)) term.dirty[term.c.y] = term.dirty[op.p[1]] = 1;
for (int i=0; (exited || term.c.x != op.p[0]) && i<term.row; ++i) if (!term.dirty[i]) {
xdrawline(term.line[i], term.c.x, i, term.c.x + 1);
xdrawline(term.line[i], op.p[0], i, op.p[0] + 1);
}
// Update search results either only for lines with new content or all results if exiting
markSearchMatches(exited);
op = (Pos){.p = {term.c.x, term.c.y, 0}};
historyOpToggle(-1, 0);
}

View File

@@ -1,8 +0,0 @@
void normalMode();
void historyPreDraw();
void historyOverlay(int x, int y, Glyph* g);
void historyModeToggle(int start);
void historyOpToggle(int, int);
typedef enum {failed=0, success=1, exitMotion=2, exitOp=3, finish=4} ExitState;
ExitState kPressHist(char const *txt, size_t len, int ctrl, KeySym const *kSym);
ExitState pressKeys(char const* s, size_t e);

934
st.c

File diff suppressed because it is too large Load Diff

21
st.h
View File

@@ -8,7 +8,6 @@
#define MAX(a, b) ((a) < (b) ? (b) : (a))
#define LEN(a) (sizeof(a) / sizeof(a)[0])
#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
#define OUT(x, a, b) ((a) <= (x) || (x) <= (b))
#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
#define DEFAULT(a, b) (a) = (a) ? (a) : (b)
#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
@@ -20,6 +19,7 @@
#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
#define IS_TRUECOL(x) (1 << 24 & (x))
#define HISTSIZE 2000
enum glyph_attribute {
ATTR_NULL = 0,
@@ -35,7 +35,6 @@ enum glyph_attribute {
ATTR_WIDE = 1 << 9,
ATTR_WDUMMY = 1 << 10,
ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
ATTR_URL = 1 << 14,
};
enum selection_mode {
@@ -83,9 +82,6 @@ void die(const char *, ...);
void redraw(void);
void draw(void);
void externalpipe(const Arg *);
void externalpipecopy(const Arg *);
void externalpipepaste(const Arg *);
void printscreen(const Arg *);
void printsel(const Arg *);
void sendbreak(const Arg *);
@@ -94,8 +90,9 @@ void toggleprinter(const Arg *);
int tattrset(int);
void tnew(int, int);
void tresize(int, int);
void tmoveto(int x, int y);
void tsetdirtattr(int);
void tupdatebgcolor(int, int);
void tupdatefgcolor(int, int);
void ttyhangup(void);
int ttynew(const char *, char *, const char *, char **);
size_t ttyread(void);
@@ -111,10 +108,6 @@ void selextend(int, int, int, int);
int selected(int, int);
char *getsel(void);
void highlighturls(void);
void unhighlighturls(void);
void followurl(int, int);
size_t utf8encode(Rune, char *);
void *xmalloc(size_t);
@@ -129,16 +122,8 @@ extern char *vtiden;
extern wchar_t *worddelimiters;
extern int allowaltscreen;
extern int allowwindowops;
extern int lightmode;
extern char *termname;
extern unsigned int tabspaces;
extern unsigned int defaultfg;
extern unsigned int defaultbg;
extern unsigned int defaultcs;
extern char *urlhandler;
extern char urlchars[];
extern char *urlprefixes[];
extern int nurlprefixes;
char **externalcopycmd();
char **externalpastecmd();

View File

@@ -161,7 +161,7 @@ st-mono| simpleterm monocolor,
rin=\E[%p1%dT,
ritm=\E[23m,
rmacs=\E(B,
rmcup=\E[?1049l\E[23;0;0t,
rmcup=\E[?1049l,
rmir=\E[4l,
rmkx=\E[?1l\E>,
rmso=\E[27m,
@@ -172,7 +172,7 @@ st-mono| simpleterm monocolor,
sitm=\E[3m,
sgr0=\E[0m,
smacs=\E(0,
smcup=\E[?1049h\E[22;0;0t,
smcup=\E[?1049h,
smir=\E[4h,
smkx=\E[?1h\E=,
smso=\E[7m,

23
utils.h
View File

@@ -1,23 +0,0 @@
/// Dynamic memory-chunk, with (1) datatype size, (2/3) initialized / allocated chunk, (4) content
typedef struct { uint8_t const elSize; uint32_t init, alloc; char* content; } DynamicArray;
#define UTF8_ARRAY {4, 0, 0, NULL}
static inline int p_alloc(DynamicArray *s, uint32_t amount) {
uint32_t const diff=s->init+s->elSize*amount-s->alloc, nas=s->alloc+max(diff,15)*s->elSize;
if (s->alloc < s->init + s->elSize * amount) {
char* tmp = realloc(s->content, nas);
if (!tmp) return 0;
s->alloc = nas, s->content = tmp;
}
return 1;
}
static inline char *view(DynamicArray * s, uint32_t i) { return s->content + i*s->elSize; }
static inline char *end(DynamicArray *s, uint32_t i) { return s->content +s->init-(i+1)*s->elSize; }
static inline uint32_t getU32(DynamicArray* s, uint32_t i, int b) { return *((uint32_t*) (b ?view(s,i) :end(s,i))); }
static char *expand(DynamicArray *s) { if (!p_alloc(s, 1)) return NULL; s->init += s->elSize; return end(s, 0); }
static inline void pop(DynamicArray* s) { s->init -= s->elSize; }
static inline void empty(DynamicArray* s) { s->init = 0; }
static inline int size(DynamicArray const * s) { return s->init / s->elSize; }
static inline void assign(DynamicArray* s, DynamicArray const *o) {
if (p_alloc(s, size(o))) memcpy(s->content, o->content, (s->init=o->init));
}

5
win.h
View File

@@ -19,7 +19,6 @@ enum win_mode {
MODE_MOUSEMANY = 1 << 15,
MODE_BRCKTPASTE = 1 << 16,
MODE_NUMLOCK = 1 << 17,
MODE_NORMAL = 1 << 18,
MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
|MODE_MOUSEMANY,
};
@@ -33,9 +32,7 @@ void xloadcols(void);
int xsetcolorname(int, const char *);
int xgetcolor(int, unsigned char *, unsigned char *, unsigned char *);
void xseticontitle(char *);
void xfreetitlestack(void);
void xsettitle(char *, int);
void xpushtitle(void);
void xsettitle(char *);
int xsetcursor(int);
void xsetmode(int, unsigned int);
void xsetpointermotion(int);

316
x.c
View File

@@ -14,13 +14,11 @@
#include <X11/keysym.h>
#include <X11/Xft/Xft.h>
#include <X11/XKBlib.h>
#include <X11/Xresource.h>
char *argv0;
#include "arg.h"
#include "st.h"
#include "win.h"
#include "normalMode.h"
/* types used in config.h */
typedef struct {
@@ -61,24 +59,14 @@ static void zoom(const Arg *);
static void zoomabs(const Arg *);
static void zoomreset(const Arg *);
static void ttysend(const Arg *);
static void cyclefonts(const Arg *);
static void nextscheme(const Arg *);
static void selectscheme(const Arg *);
void kscrollup(const Arg *);
void kscrolldown(const Arg *);
/* config.h for applying patches and the configuration. */
#include "config.h"
char **externalcopycmd()
{
return (lightmode) ? externalcopylightcmd : externalcopydarkcmd;
}
char **externalpastecmd()
{
return (lightmode) ? externalpastelightcmd : externalpastedarkcmd;
}
/* size of title stack */
#define TITLESTACKSIZE 8
/* XEMBED messages */
#define XEMBED_FOCUS_IN 4
#define XEMBED_FOCUS_OUT 5
@@ -201,13 +189,13 @@ static void mousesel(XEvent *, int);
static void mousereport(XEvent *);
static char *kmap(KeySym, uint);
static int match(uint, uint);
static void updatescheme(void);
static void run(void);
static void usage(void);
static void (*handler[LASTEvent])(XEvent *) = {
[KeyPress] = kpress,
[KeyRelease] = kpress,
[ClientMessage] = cmessage,
[ConfigureNotify] = resize,
[VisibilityNotify] = visibility,
@@ -237,8 +225,6 @@ static DC dc;
static XWindow xw;
static XSelection xsel;
static TermWindow win;
static int tstki; /* title stack index */
static char *titlestack[TITLESTACKSIZE]; /* title stack */
/* Font Ring Cache */
enum {
@@ -280,7 +266,6 @@ clipcopy(const Arg *dummy)
free(xsel.clipboard);
xsel.clipboard = NULL;
xsetsel(getsel());
if (xsel.primary != NULL) {
xsel.clipboard = xstrdup(xsel.primary);
@@ -335,7 +320,11 @@ void
zoomreset(const Arg *arg)
{
Arg larg;
zoomabs(&larg);
if (defaultfontsize > 0) {
larg.f = defaultfontsize;
zoomabs(&larg);
}
}
void
@@ -360,15 +349,6 @@ evrow(XEvent *e)
return y / win.ch;
}
void cyclefonts(const Arg *arg) {
currentfont++;
currentfont %= (sizeof fonts / sizeof fonts[0]);
usedfont = fonts[currentfont];
Arg larg;
larg.f = usedfontsize;
zoomabs(&larg);
}
void
mousesel(XEvent *e, int done)
{
@@ -477,15 +457,6 @@ mouseaction(XEvent *e, uint release)
/* ignore Button<N>mask for Button<N> - it's set on release */
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++) {
if (ms->release == release &&
ms->button == e->xbutton.button &&
@@ -504,6 +475,7 @@ bpress(XEvent *e)
{
int btn = e->xbutton.button;
struct timespec now;
int snap;
if (1 <= btn && btn <= 11)
buttons |= 1 << (btn-1);
@@ -522,34 +494,17 @@ bpress(XEvent *e)
* snapping behaviour is exposed.
*/
clock_gettime(CLOCK_MONOTONIC, &now);
int const tripleClick = TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout,
doubleClick = TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout;
if ((mouseYank || mouseSelect) && (tripleClick || doubleClick)) {
if (!IS_SET(MODE_NORMAL)) normalMode();
historyOpToggle(1, 1);
tmoveto(evcol(e), evrow(e));
if (tripleClick) {
if (mouseYank) pressKeys("dVy", 3);
if (mouseSelect) pressKeys("dV", 2);
} else if (doubleClick) {
if (mouseYank) pressKeys("dyiW", 4);
if (mouseSelect) {
tmoveto(evcol(e), evrow(e));
pressKeys("viW", 3);
}
}
historyOpToggle(-1, 1);
if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) {
snap = SNAP_LINE;
} else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) {
snap = SNAP_WORD;
} else {
if (!IS_SET(MODE_NORMAL)) selstart(evcol(e), evrow(e), 0);
else {
historyOpToggle(1, 1);
tmoveto(evcol(e), evrow(e));
pressKeys("v", 1);
historyOpToggle(-1, 1);
}
snap = 0;
}
xsel.tclick2 = xsel.tclick1;
xsel.tclick1 = now;
selstart(evcol(e), evrow(e), snap);
}
}
@@ -759,7 +714,8 @@ brelease(XEvent *e)
if (mouseaction(e, 1))
return;
if (btn == Button1 && !IS_SET(MODE_NORMAL)) mousesel(e, 1);
if (btn == Button1)
mousesel(e, 1);
}
void
@@ -839,8 +795,6 @@ xloadcolor(int i, const char *name, Color *ncolor)
return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor);
}
void normalMode() { historyModeToggle((win.mode ^=MODE_NORMAL) & MODE_NORMAL); }
void
xloadcols(void)
{
@@ -852,7 +806,7 @@ xloadcols(void)
for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp)
XftColorFree(xw.dpy, xw.vis, xw.cmap, cp);
} else {
dc.collen = MAX(LEN(colorname), 256);
dc.collen = 258;
dc.col = xmalloc(dc.collen * sizeof(Color));
}
@@ -920,8 +874,8 @@ xhints(void)
sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize;
sizeh->height = win.h;
sizeh->width = win.w;
sizeh->height_inc = 1;
sizeh->width_inc = 1;
sizeh->height_inc = win.ch;
sizeh->width_inc = win.cw;
sizeh->base_height = 2 * borderpx;
sizeh->base_width = 2 * borderpx;
sizeh->min_height = win.ch + 2 * borderpx;
@@ -1195,7 +1149,7 @@ xinit(int cols, int rows)
if (!FcInit())
die("could not init fontconfig.\n");
usedfont = (opt_font == NULL) ? fonts[currentfont] : opt_font;
usedfont = (opt_font == NULL)? font : opt_font;
xloadfonts(usedfont, 0);
/* colors */
@@ -1311,10 +1265,8 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
/* Fetch rune and mode for current glyph. */
Glyph g = glyphs[i];
historyOverlay(x+i, y, &g);
rune = g.u;
mode = g.mode;
rune = glyphs[i].u;
mode = glyphs[i].mode;
/* Skip dummy wide-character spacing. */
if (mode == ATTR_WDUMMY)
@@ -1548,7 +1500,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
/* Render underline and strikethrough. */
if (base.mode & ATTR_UNDERLINE || base.mode & ATTR_URL) {
if (base.mode & ATTR_UNDERLINE) {
XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1,
width, 1);
}
@@ -1685,30 +1637,10 @@ xseticontitle(char *p)
}
void
xfreetitlestack(void)
{
for (int i = 0; i < LEN(titlestack); i++) {
free(titlestack[i]);
titlestack[i] = NULL;
}
}
void
xsettitle(char *p, int pop)
xsettitle(char *p)
{
XTextProperty prop;
free(titlestack[tstki]);
if (pop) {
titlestack[tstki] = NULL;
tstki = (tstki - 1 + TITLESTACKSIZE) % TITLESTACKSIZE;
p = titlestack[tstki] ? titlestack[tstki] : opt_title;
} else if (p) {
titlestack[tstki] = xstrdup(p);
} else {
titlestack[tstki] = NULL;
p = opt_title;
}
DEFAULT(p, opt_title);
if (p[0] == '\0')
p = opt_title;
@@ -1721,16 +1653,6 @@ xsettitle(char *p, int pop)
XFree(prop.value);
}
void
xpushtitle(void)
{
int tstkin = (tstki + 1) % TITLESTACKSIZE;
free(titlestack[tstkin]);
titlestack[tstkin] = titlestack[tstki] ? xstrdup(titlestack[tstki]) : NULL;
tstki = tstkin;
}
int
xstartdraw(void)
{
@@ -1748,7 +1670,6 @@ xdrawline(Line line, int x1, int y1, int x2)
i = ox = 0;
for (x = x1; x < x2 && i < numspecs; x++) {
new = line[x];
historyOverlay(x, y1, &new);
if (new.mode == ATTR_WDUMMY)
continue;
if (selected(x, y1))
@@ -1943,23 +1864,6 @@ kpress(XEvent *ev)
} else {
len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
}
if (IS_SET(MODE_NORMAL)) {
if (kPressHist(buf, len, match(ControlMask, e->state), &ksym)
== finish) normalMode();
return;
}
/* 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 */
for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
if (ksym == bp->keysym && match(bp->mod, e->state)) {
@@ -2124,119 +2028,6 @@ 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", fonts[0]);
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(fonts[currentfont], 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
usage(void)
{
@@ -2250,6 +2041,47 @@ usage(void)
" [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
main(int argc, char *argv[])
{
@@ -2302,6 +2134,12 @@ main(int argc, char *argv[])
} ARGEND;
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 */
opt_cmd = argv;
@@ -2310,8 +2148,6 @@ run:
setlocale(LC_CTYPE, "");
XSetLocaleModifiers("");
xrdb_load();
signal(SIGUSR1, reload);
cols = MAX(cols, 1);
rows = MAX(rows, 1);
tnew(cols, rows);