Compare commits

..

3 Commits

Author SHA1 Message Date
d9fa845fc4 patch: bold-is-not-bright 2026-01-01 13:13:04 -05:00
577f633ef4 patch: bold-is-not-bright 2026-01-01 13:13:04 -05:00
ab5d49f991 patch: scrollback-ringbuffer 2026-01-01 13:04:01 -05:00
11 changed files with 3587 additions and 126 deletions

View File

@@ -201,6 +201,8 @@ static Shortcut shortcuts[] = {
{ TERMMOD, XK_Y, selpaste, {.i = 0} }, { TERMMOD, XK_Y, selpaste, {.i = 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} },
{ ShiftMask, XK_Page_Up, kscrollup, {.i = -1} },
{ ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} },
}; };
/* /*

476
config.h Normal file
View File

@@ -0,0 +1,476 @@
/* See LICENSE file for copyright and license details. */
/*
* appearance
*
* font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
*/
static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true";
static int borderpx = 2;
/*
* What program is execed by st depends of these precedence rules:
* 1: program passed with -e
* 2: scroll and/or utmp
* 3: SHELL environment variable
* 4: value of shell in /etc/passwd
* 5: value of shell in config.h
*/
static char *shell = "/bin/sh";
char *utmp = NULL;
/* scroll program: to enable use a string like "scroll" */
char *scroll = NULL;
char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400";
/* identification sequence returned in DA and DECID */
char *vtiden = "\033[?6c";
/* Kerning / character bounding-box multipliers */
static float cwscale = 1.0;
static float chscale = 1.0;
/*
* word delimiter string
*
* More advanced example: L" `'\"()[]{}"
*/
wchar_t *worddelimiters = L" ";
/* selection timeouts (in milliseconds) */
static unsigned int doubleclicktimeout = 300;
static unsigned int tripleclicktimeout = 600;
/* alt screens */
int allowaltscreen = 1;
/* allow certain non-interactive (insecure) window operations such as:
setting the clipboard text */
int allowwindowops = 0;
/*
* draw latency range in ms - from new content/keypress/etc until drawing.
* within this range, st draws when content stops arriving (idle). mostly it's
* near minlatency, but it waits longer for slow updates to avoid partial draw.
* low minlatency will tear/flicker more, as it can "detect" idle too early.
*/
static double minlatency = 2;
static double maxlatency = 33;
/*
* blinking timeout (set to 0 to disable blinking) for the terminal blinking
* attribute.
*/
static unsigned int blinktimeout = 800;
/*
* thickness of underline and bar cursors
*/
static unsigned int cursorthickness = 2;
/*
* bell volume. It must be a value between -100 and 100. Use 0 for disabling
* it
*/
static int bellvolume = 0;
/* default TERM value */
char *termname = "st-256color";
/*
* spaces per tab
*
* When you are changing this value, don't forget to adapt the »it« value in
* the st.info and appropriately install the st.info in the environment where
* you use this st version.
*
* it#$tabspaces,
*
* Secondly make sure your kernel is not expanding tabs. When running `stty
* -a` »tab0« should appear. You can tell the terminal to not expand tabs by
* running following command:
*
* stty tabs
*/
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",
/* 8 bright colors */
"gray50",
"red",
"green",
"yellow",
"#5c5cff",
"magenta",
"cyan",
"white",
[255] = 0,
/* more colors can be added after 255 to use with DefaultXX */
"#cccccc",
"#555555",
"gray90", /* default foreground colour */
"black", /* default background colour */
};
/*
* 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;
/*
* Default shape of cursor
* 2: Block ("█")
* 4: Underline ("_")
* 6: Bar ("|")
* 7: Snowman ("☃")
*/
static unsigned int cursorshape = 2;
/*
* Default columns and rows numbers
*/
static unsigned int cols = 80;
static unsigned int rows = 24;
/*
* Default colour and shape of the mouse cursor
*/
static unsigned int mouseshape = XC_xterm;
static unsigned int mousefg = 7;
static unsigned int mousebg = 0;
/*
* Color used to display font attributes when fontconfig selected a font which
* doesn't match the ones requested.
*/
static unsigned int defaultattr = 11;
/*
* Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set).
* Note that if you want to use ShiftMask with selmasks, set this to an other
* modifier, set to 0 to not use it.
*/
static uint forcemousemod = ShiftMask;
/*
* Internal mouse shortcuts.
* Beware that overloading Button1 will disable the selection.
*/
static MouseShortcut mshortcuts[] = {
/* mask button function argument release */
{ XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 },
{ ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} },
{ XK_ANY_MOD, Button4, ttysend, {.s = "\031"} },
{ ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} },
{ XK_ANY_MOD, Button5, ttysend, {.s = "\005"} },
};
/* Internal keyboard shortcuts. */
#define MODKEY Mod1Mask
#define TERMMOD (ControlMask|ShiftMask)
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_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} },
{ ShiftMask, XK_Page_Up, kscrollup, {.i = -1} },
{ ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} },
};
/*
* Special keys (change & recompile st.info accordingly)
*
* Mask value:
* * Use XK_ANY_MOD to match the key no matter modifiers state
* * Use XK_NO_MOD to match the key alone (no modifiers)
* appkey value:
* * 0: no value
* * > 0: keypad application mode enabled
* * = 2: term.numlock = 1
* * < 0: keypad application mode disabled
* appcursor value:
* * 0: no value
* * > 0: cursor application mode enabled
* * < 0: cursor application mode disabled
*
* Be careful with the order of the definitions because st searches in
* this table sequentially, so any XK_ANY_MOD must be in the last
* position for a key.
*/
/*
* If you want keys other than the X11 function keys (0xFD00 - 0xFFFF)
* to be mapped below, add them to this array.
*/
static KeySym mappedkeys[] = { -1 };
/*
* State bits to ignore when matching key or button events. By default,
* numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored.
*/
static uint ignoremod = Mod2Mask|XK_SWITCH_MOD;
/*
* This is the huge key array which defines all compatibility to the Linux
* world. Please decide about changes wisely.
*/
static Key key[] = {
/* keysym mask string appkey appcursor */
{ XK_KP_Home, ShiftMask, "\033[2J", 0, -1},
{ XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1},
{ XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1},
{ XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1},
{ XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0},
{ XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1},
{ XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1},
{ XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0},
{ XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1},
{ XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1},
{ XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0},
{ XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1},
{ XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1},
{ XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0},
{ XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1},
{ XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1},
{ XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0},
{ XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0},
{ XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0},
{ XK_KP_End, ControlMask, "\033[J", -1, 0},
{ XK_KP_End, ControlMask, "\033[1;5F", +1, 0},
{ XK_KP_End, ShiftMask, "\033[K", -1, 0},
{ XK_KP_End, ShiftMask, "\033[1;2F", +1, 0},
{ XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0},
{ XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0},
{ XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0},
{ XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0},
{ XK_KP_Insert, ShiftMask, "\033[4l", -1, 0},
{ XK_KP_Insert, ControlMask, "\033[L", -1, 0},
{ XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0},
{ XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0},
{ XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0},
{ XK_KP_Delete, ControlMask, "\033[M", -1, 0},
{ XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0},
{ XK_KP_Delete, ShiftMask, "\033[2K", -1, 0},
{ XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0},
{ XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0},
{ XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0},
{ XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0},
{ XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0},
{ XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0},
{ XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0},
{ XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0},
{ XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0},
{ XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0},
{ XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0},
{ XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0},
{ XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0},
{ XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0},
{ XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0},
{ XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0},
{ XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0},
{ XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0},
{ XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0},
{ XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0},
{ XK_Up, ShiftMask, "\033[1;2A", 0, 0},
{ XK_Up, Mod1Mask, "\033[1;3A", 0, 0},
{ XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0},
{ XK_Up, ControlMask, "\033[1;5A", 0, 0},
{ XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0},
{ XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0},
{ XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0},
{ XK_Up, XK_ANY_MOD, "\033[A", 0, -1},
{ XK_Up, XK_ANY_MOD, "\033OA", 0, +1},
{ XK_Down, ShiftMask, "\033[1;2B", 0, 0},
{ XK_Down, Mod1Mask, "\033[1;3B", 0, 0},
{ XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0},
{ XK_Down, ControlMask, "\033[1;5B", 0, 0},
{ XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0},
{ XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0},
{ XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0},
{ XK_Down, XK_ANY_MOD, "\033[B", 0, -1},
{ XK_Down, XK_ANY_MOD, "\033OB", 0, +1},
{ XK_Left, ShiftMask, "\033[1;2D", 0, 0},
{ XK_Left, Mod1Mask, "\033[1;3D", 0, 0},
{ XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0},
{ XK_Left, ControlMask, "\033[1;5D", 0, 0},
{ XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0},
{ XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0},
{ XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0},
{ XK_Left, XK_ANY_MOD, "\033[D", 0, -1},
{ XK_Left, XK_ANY_MOD, "\033OD", 0, +1},
{ XK_Right, ShiftMask, "\033[1;2C", 0, 0},
{ XK_Right, Mod1Mask, "\033[1;3C", 0, 0},
{ XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0},
{ XK_Right, ControlMask, "\033[1;5C", 0, 0},
{ XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0},
{ XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0},
{ XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0},
{ XK_Right, XK_ANY_MOD, "\033[C", 0, -1},
{ XK_Right, XK_ANY_MOD, "\033OC", 0, +1},
{ XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0},
{ XK_Return, Mod1Mask, "\033\r", 0, 0},
{ XK_Return, XK_ANY_MOD, "\r", 0, 0},
{ XK_Insert, ShiftMask, "\033[4l", -1, 0},
{ XK_Insert, ShiftMask, "\033[2;2~", +1, 0},
{ XK_Insert, ControlMask, "\033[L", -1, 0},
{ XK_Insert, ControlMask, "\033[2;5~", +1, 0},
{ XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0},
{ XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0},
{ XK_Delete, ControlMask, "\033[M", -1, 0},
{ XK_Delete, ControlMask, "\033[3;5~", +1, 0},
{ XK_Delete, ShiftMask, "\033[2K", -1, 0},
{ XK_Delete, ShiftMask, "\033[3;2~", +1, 0},
{ XK_Delete, XK_ANY_MOD, "\033[P", -1, 0},
{ XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0},
{ XK_BackSpace, XK_NO_MOD, "\177", 0, 0},
{ XK_BackSpace, Mod1Mask, "\033\177", 0, 0},
{ XK_Home, ShiftMask, "\033[2J", 0, -1},
{ XK_Home, ShiftMask, "\033[1;2H", 0, +1},
{ XK_Home, XK_ANY_MOD, "\033[H", 0, -1},
{ XK_Home, XK_ANY_MOD, "\033[1~", 0, +1},
{ XK_End, ControlMask, "\033[J", -1, 0},
{ XK_End, ControlMask, "\033[1;5F", +1, 0},
{ XK_End, ShiftMask, "\033[K", -1, 0},
{ XK_End, ShiftMask, "\033[1;2F", +1, 0},
{ XK_End, XK_ANY_MOD, "\033[4~", 0, 0},
{ XK_Prior, ControlMask, "\033[5;5~", 0, 0},
{ XK_Prior, ShiftMask, "\033[5;2~", 0, 0},
{ XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0},
{ XK_Next, ControlMask, "\033[6;5~", 0, 0},
{ XK_Next, ShiftMask, "\033[6;2~", 0, 0},
{ XK_Next, XK_ANY_MOD, "\033[6~", 0, 0},
{ XK_F1, XK_NO_MOD, "\033OP" , 0, 0},
{ XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0},
{ XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0},
{ XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0},
{ XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0},
{ XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0},
{ XK_F2, XK_NO_MOD, "\033OQ" , 0, 0},
{ XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0},
{ XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0},
{ XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0},
{ XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0},
{ XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0},
{ XK_F3, XK_NO_MOD, "\033OR" , 0, 0},
{ XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0},
{ XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0},
{ XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0},
{ XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0},
{ XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0},
{ XK_F4, XK_NO_MOD, "\033OS" , 0, 0},
{ XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0},
{ XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0},
{ XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0},
{ XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0},
{ XK_F5, XK_NO_MOD, "\033[15~", 0, 0},
{ XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0},
{ XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0},
{ XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0},
{ XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0},
{ XK_F6, XK_NO_MOD, "\033[17~", 0, 0},
{ XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0},
{ XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0},
{ XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0},
{ XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0},
{ XK_F7, XK_NO_MOD, "\033[18~", 0, 0},
{ XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0},
{ XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0},
{ XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0},
{ XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0},
{ XK_F8, XK_NO_MOD, "\033[19~", 0, 0},
{ XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0},
{ XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0},
{ XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0},
{ XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0},
{ XK_F9, XK_NO_MOD, "\033[20~", 0, 0},
{ XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0},
{ XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0},
{ XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0},
{ XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0},
{ XK_F10, XK_NO_MOD, "\033[21~", 0, 0},
{ XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0},
{ XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0},
{ XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0},
{ XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0},
{ XK_F11, XK_NO_MOD, "\033[23~", 0, 0},
{ XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0},
{ XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0},
{ XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0},
{ XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0},
{ XK_F12, XK_NO_MOD, "\033[24~", 0, 0},
{ XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0},
{ XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0},
{ XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0},
{ XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0},
{ XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0},
{ XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0},
{ XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0},
{ XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0},
{ XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0},
{ XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0},
{ XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0},
{ XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0},
{ XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0},
{ XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0},
{ XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0},
{ XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0},
{ XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0},
{ XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0},
{ XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0},
{ XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0},
{ XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0},
{ XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0},
{ XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0},
{ XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0},
{ XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0},
{ XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0},
{ XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0},
};
/*
* Selection types' masks.
* Use the same masks as usual.
* Button1Mask is always unset, to make masks match between ButtonPress.
* ButtonRelease and MotionNotify.
* If no match is found, regular selection is used.
*/
static uint selmasks[] = {
[SEL_RECTANGULAR] = Mod1Mask,
};
/*
* Printable characters in ASCII, used to estimate the advance width
* of single wide characters.
*/
static char ascii_printable[] =
" !\"#$%&'()*+,-./0123456789:;<=>?"
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
"`abcdefghijklmnopqrstuvwxyz{|}~";

BIN
st Executable file

Binary file not shown.

View File

@@ -0,0 +1,27 @@
From e8f3c86d93613eef5af187dfd2dba9ae9919a2ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= <code@bnwr.net>
Date: Sun, 27 Jan 2019 13:31:28 +0800
Subject: [PATCH] Show bold not as bright
---
x.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/x.c b/x.c
index 0422421..904e1dc 100644
--- a/x.c
+++ b/x.c
@@ -1296,10 +1296,6 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
bg = &dc.col[base.bg];
}
- /* Change basic system colors [0-7] to bright system colors [8-15] */
- if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7))
- fg = &dc.col[base.fg + 8];
-
if (IS_SET(MODE_REVERSE)) {
if (fg == &dc.col[defaultfg]) {
fg = &dc.col[defaultbg];
--
2.20.1

View File

@@ -0,0 +1,730 @@
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.
diff --git a/config.def.h b/config.def.h
index 2cd740a..8b25d40 100644
--- a/config.def.h
+++ b/config.def.h
@@ -201,6 +201,8 @@ static Shortcut shortcuts[] = {
{ TERMMOD, XK_Y, selpaste, {.i = 0} },
{ ShiftMask, XK_Insert, selpaste, {.i = 0} },
{ TERMMOD, XK_Num_Lock, numlock, {.i = 0} },
+ { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} },
+ { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} },
};
/*
diff --git a/st.c b/st.c
index b9f66e7..d9b163e 100644
--- a/st.c
+++ b/st.c
@@ -43,6 +43,10 @@
#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
#define ISDELIM(u) (u && wcschr(worddelimiters, u))
+#define TSCREEN term.screen[IS_SET(MODE_ALTSCREEN)]
+#define TLINEOFFSET(y) (((y) + TSCREEN.cur - TSCREEN.off + TSCREEN.size) % TSCREEN.size)
+#define TLINE(y) (TSCREEN.buffer[TLINEOFFSET(y)])
+
enum term_mode {
MODE_WRAP = 1 << 0,
MODE_INSERT = 1 << 1,
@@ -109,12 +113,21 @@ typedef struct {
int alt;
} Selection;
+/* Screen lines */
+typedef struct {
+ Line* buffer; /* ring buffer */
+ int size; /* size of buffer */
+ int cur; /* start of active screen */
+ int off; /* scrollback line offset */
+ TCursor sc; /* saved cursor */
+} LineBuffer;
+
/* Internal representation of the screen */
typedef struct {
int row; /* nb row */
int col; /* nb col */
- Line *line; /* screen */
- Line *alt; /* alternate screen */
+ LineBuffer screen[2]; /* screen and alternate screen */
+ int linelen; /* allocated line length */
int *dirty; /* dirtyness of lines */
TCursor c; /* cursor */
int ocx; /* old cursor col */
@@ -203,6 +216,8 @@ static void tdeftran(char);
static void tstrsequence(uchar);
static void drawregion(int, int, int, int);
+static void clearline(Line, Glyph, int, int);
+static Line ensureline(Line);
static void selnormalize(void);
static void selscroll(int, int);
@@ -408,11 +423,12 @@ int
tlinelen(int y)
{
int i = term.col;
+ Line line = TLINE(y);
- if (term.line[y][i - 1].mode & ATTR_WRAP)
+ if (line[i - 1].mode & ATTR_WRAP)
return i;
- while (i > 0 && term.line[y][i - 1].u == ' ')
+ while (i > 0 && line[i - 1].u == ' ')
--i;
return i;
@@ -521,7 +537,7 @@ selsnap(int *x, int *y, int direction)
* Snap around if the word wraps around at the end or
* beginning of a line.
*/
- prevgp = &term.line[*y][*x];
+ prevgp = &TLINE(*y)[*x];
prevdelim = ISDELIM(prevgp->u);
for (;;) {
newx = *x + direction;
@@ -536,14 +552,14 @@ selsnap(int *x, int *y, int direction)
yt = *y, xt = *x;
else
yt = newy, xt = newx;
- if (!(term.line[yt][xt].mode & ATTR_WRAP))
+ if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
break;
}
if (newx >= tlinelen(newy))
break;
- gp = &term.line[newy][newx];
+ gp = &TLINE(newy)[newx];
delim = ISDELIM(gp->u);
if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
|| (delim && gp->u != prevgp->u)))
@@ -564,14 +580,14 @@ selsnap(int *x, int *y, int direction)
*x = (direction < 0) ? 0 : term.col - 1;
if (direction < 0) {
for (; *y > 0; *y += direction) {
- if (!(term.line[*y-1][term.col-1].mode
+ if (!(TLINE(*y-1)[term.col-1].mode
& ATTR_WRAP)) {
break;
}
}
} else if (direction > 0) {
for (; *y < term.row-1; *y += direction) {
- if (!(term.line[*y][term.col-1].mode
+ if (!(TLINE(*y)[term.col-1].mode
& ATTR_WRAP)) {
break;
}
@@ -602,13 +618,13 @@ getsel(void)
}
if (sel.type == SEL_RECTANGULAR) {
- gp = &term.line[y][sel.nb.x];
+ gp = &TLINE(y)[sel.nb.x];
lastx = sel.ne.x;
} else {
- gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
+ gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0];
lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
}
- last = &term.line[y][MIN(lastx, linelen-1)];
+ last = &TLINE(y)[MIN(lastx, linelen-1)];
while (last >= gp && last->u == ' ')
--last;
@@ -949,12 +965,15 @@ int
tattrset(int attr)
{
int i, j;
+ int y = TLINEOFFSET(0);
for (i = 0; i < term.row-1; i++) {
+ Line line = TSCREEN.buffer[y];
for (j = 0; j < term.col-1; j++) {
- if (term.line[i][j].mode & attr)
+ if (line[j].mode & attr)
return 1;
}
+ y = (y+1) % TSCREEN.size;
}
return 0;
@@ -976,14 +995,17 @@ void
tsetdirtattr(int attr)
{
int i, j;
+ int y = TLINEOFFSET(0);
for (i = 0; i < term.row-1; i++) {
+ Line line = TSCREEN.buffer[y];
for (j = 0; j < term.col-1; j++) {
- if (term.line[i][j].mode & attr) {
+ if (line[j].mode & attr) {
tsetdirt(i, i);
break;
}
}
+ y = (y+1) % TSCREEN.size;
}
}
@@ -996,27 +1018,19 @@ tfulldirt(void)
void
tcursor(int mode)
{
- static TCursor c[2];
- int alt = IS_SET(MODE_ALTSCREEN);
-
if (mode == CURSOR_SAVE) {
- c[alt] = term.c;
+ TSCREEN.sc = term.c;
} else if (mode == CURSOR_LOAD) {
- term.c = c[alt];
- tmoveto(c[alt].x, c[alt].y);
+ term.c = TSCREEN.sc;
+ tmoveto(term.c.x, term.c.y);
}
}
void
treset(void)
{
- uint i;
-
- term.c = (TCursor){{
- .mode = ATTR_NULL,
- .fg = defaultfg,
- .bg = defaultbg
- }, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
+ int i, j;
+ Glyph g = (Glyph){ .fg = defaultfg, .bg = defaultbg};
memset(term.tabs, 0, term.col * sizeof(*term.tabs));
for (i = tabspaces; i < term.col; i += tabspaces)
@@ -1028,17 +1042,37 @@ treset(void)
term.charset = 0;
for (i = 0; i < 2; i++) {
- tmoveto(0, 0);
- tcursor(CURSOR_SAVE);
- tclearregion(0, 0, term.col-1, term.row-1);
- tswapscreen();
+ term.screen[i].sc = (TCursor){{
+ .fg = defaultfg,
+ .bg = defaultbg
+ }};
+ term.screen[i].cur = 0;
+ term.screen[i].off = 0;
+ for (j = 0; j < term.row; ++j) {
+ if (term.col != term.linelen)
+ term.screen[i].buffer[j] = xrealloc(term.screen[i].buffer[j], term.col * sizeof(Glyph));
+ clearline(term.screen[i].buffer[j], g, 0, term.col);
+ }
+ for (j = term.row; j < term.screen[i].size; ++j) {
+ free(term.screen[i].buffer[j]);
+ term.screen[i].buffer[j] = NULL;
+ }
}
+ tcursor(CURSOR_LOAD);
+ term.linelen = term.col;
+ tfulldirt();
}
void
tnew(int col, int row)
{
- term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
+ int i;
+ term = (Term){};
+ term.screen[0].buffer = xmalloc(HISTSIZE * sizeof(Line));
+ term.screen[0].size = HISTSIZE;
+ term.screen[1].buffer = NULL;
+ for (i = 0; i < HISTSIZE; ++i) term.screen[0].buffer[i] = NULL;
+
tresize(col, row);
treset();
}
@@ -1046,14 +1080,42 @@ tnew(int col, int row)
void
tswapscreen(void)
{
- Line *tmp = term.line;
-
- term.line = term.alt;
- term.alt = tmp;
term.mode ^= MODE_ALTSCREEN;
tfulldirt();
}
+void
+kscrollup(const Arg *a)
+{
+ int n = a->i;
+
+ if (IS_SET(MODE_ALTSCREEN))
+ return;
+
+ if (n < 0) n = (-n) * term.row;
+ if (n > TSCREEN.size - term.row - TSCREEN.off) n = TSCREEN.size - term.row - TSCREEN.off;
+ while (!TLINE(-n)) --n;
+ TSCREEN.off += n;
+ selscroll(0, n);
+ tfulldirt();
+}
+
+void
+kscrolldown(const Arg *a)
+{
+
+ int n = a->i;
+
+ if (IS_SET(MODE_ALTSCREEN))
+ return;
+
+ if (n < 0) n = (-n) * term.row;
+ if (n > TSCREEN.off) n = TSCREEN.off;
+ TSCREEN.off -= n;
+ selscroll(0, -n);
+ tfulldirt();
+}
+
void
tscrolldown(int orig, int n)
{
@@ -1062,15 +1124,29 @@ tscrolldown(int orig, int n)
LIMIT(n, 0, term.bot-orig+1);
- tsetdirt(orig, term.bot-n);
- tclearregion(0, term.bot-n+1, term.col-1, term.bot);
+ /* Ensure that lines are allocated */
+ for (i = -n; i < 0; i++) {
+ TLINE(i) = ensureline(TLINE(i));
+ }
- for (i = term.bot; i >= orig+n; i--) {
- temp = term.line[i];
- term.line[i] = term.line[i-n];
- term.line[i-n] = temp;
+ /* Shift non-scrolling areas in ring buffer */
+ for (i = term.bot+1; i < term.row; i++) {
+ temp = TLINE(i);
+ TLINE(i) = TLINE(i-n);
+ TLINE(i-n) = temp;
+ }
+ for (i = 0; i < orig; i++) {
+ temp = TLINE(i);
+ TLINE(i) = TLINE(i-n);
+ TLINE(i-n) = temp;
}
+ /* Scroll buffer */
+ TSCREEN.cur = (TSCREEN.cur + TSCREEN.size - n) % TSCREEN.size;
+ /* Clear lines that have entered the view */
+ tclearregion(0, orig, term.linelen-1, orig+n-1);
+ /* Redraw portion of the screen that has scrolled */
+ tsetdirt(orig+n-1, term.bot);
selscroll(orig, n);
}
@@ -1082,15 +1158,29 @@ tscrollup(int orig, int n)
LIMIT(n, 0, term.bot-orig+1);
- tclearregion(0, orig, term.col-1, orig+n-1);
- tsetdirt(orig+n, term.bot);
+ /* Ensure that lines are allocated */
+ for (i = term.row; i < term.row + n; i++) {
+ TLINE(i) = ensureline(TLINE(i));
+ }
- for (i = orig; i <= term.bot-n; i++) {
- temp = term.line[i];
- term.line[i] = term.line[i+n];
- term.line[i+n] = temp;
+ /* Shift non-scrolling areas in ring buffer */
+ for (i = orig-1; i >= 0; i--) {
+ temp = TLINE(i);
+ TLINE(i) = TLINE(i+n);
+ TLINE(i+n) = temp;
+ }
+ for (i = term.row-1; i >term.bot; i--) {
+ temp = TLINE(i);
+ TLINE(i) = TLINE(i+n);
+ TLINE(i+n) = temp;
}
+ /* Scroll buffer */
+ TSCREEN.cur = (TSCREEN.cur + n) % TSCREEN.size;
+ /* Clear lines that have entered the view */
+ tclearregion(0, term.bot-n+1, term.linelen-1, term.bot);
+ /* Redraw portion of the screen that has scrolled */
+ tsetdirt(orig, term.bot-n+1);
selscroll(orig, -n);
}
@@ -1194,6 +1284,7 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
"⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */
"│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */
};
+ Line line = TLINE(y);
/*
* The table is proudly stolen from rxvt.
@@ -1202,25 +1293,25 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])
utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);
- if (term.line[y][x].mode & ATTR_WIDE) {
+ if (line[x].mode & ATTR_WIDE) {
if (x+1 < term.col) {
- term.line[y][x+1].u = ' ';
- term.line[y][x+1].mode &= ~ATTR_WDUMMY;
+ line[x+1].u = ' ';
+ line[x+1].mode &= ~ATTR_WDUMMY;
}
- } else if (term.line[y][x].mode & ATTR_WDUMMY) {
- term.line[y][x-1].u = ' ';
- term.line[y][x-1].mode &= ~ATTR_WIDE;
+ } else if (line[x].mode & ATTR_WDUMMY) {
+ line[x-1].u = ' ';
+ line[x-1].mode &= ~ATTR_WIDE;
}
term.dirty[y] = 1;
- term.line[y][x] = *attr;
- term.line[y][x].u = u;
+ line[x] = *attr;
+ line[x].u = u;
}
void
tclearregion(int x1, int y1, int x2, int y2)
{
- int x, y, temp;
+ int x, y, L, S, temp;
Glyph *gp;
if (x1 > x2)
@@ -1228,15 +1319,16 @@ tclearregion(int x1, int y1, int x2, int y2)
if (y1 > y2)
temp = y1, y1 = y2, y2 = temp;
- LIMIT(x1, 0, term.col-1);
- LIMIT(x2, 0, term.col-1);
+ LIMIT(x1, 0, term.linelen-1);
+ LIMIT(x2, 0, term.linelen-1);
LIMIT(y1, 0, term.row-1);
LIMIT(y2, 0, term.row-1);
+ L = TLINEOFFSET(y1);
for (y = y1; y <= y2; y++) {
term.dirty[y] = 1;
for (x = x1; x <= x2; x++) {
- gp = &term.line[y][x];
+ gp = &TSCREEN.buffer[L][x];
if (selected(x, y))
selclear();
gp->fg = term.c.attr.fg;
@@ -1244,6 +1336,7 @@ tclearregion(int x1, int y1, int x2, int y2)
gp->mode = 0;
gp->u = ' ';
}
+ L = (L + 1) % TSCREEN.size;
}
}
@@ -1258,7 +1351,7 @@ tdeletechar(int n)
dst = term.c.x;
src = term.c.x + n;
size = term.col - src;
- line = term.line[term.c.y];
+ line = TLINE(term.c.y);
memmove(&line[dst], &line[src], size * sizeof(Glyph));
tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
@@ -1275,7 +1368,7 @@ tinsertblank(int n)
dst = term.c.x + n;
src = term.c.x;
size = term.col - dst;
- line = term.line[term.c.y];
+ line = TLINE(term.c.y);
memmove(&line[dst], &line[src], size * sizeof(Glyph));
tclearregion(src, term.c.y, dst - 1, term.c.y);
@@ -2079,7 +2172,7 @@ tdumpline(int n)
char buf[UTF_SIZ];
const Glyph *bp, *end;
- bp = &term.line[n][0];
+ bp = &TLINE(n)[0];
end = &bp[MIN(tlinelen(n), term.col) - 1];
if (bp != end || bp->u != ' ') {
for ( ; bp <= end; ++bp)
@@ -2466,11 +2559,11 @@ check_control_code:
if (selected(term.c.x, term.c.y))
selclear();
- gp = &term.line[term.c.y][term.c.x];
+ gp = &TLINE(term.c.y)[term.c.x];
if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
gp->mode |= ATTR_WRAP;
tnewline(1);
- gp = &term.line[term.c.y][term.c.x];
+ gp = &TLINE(term.c.y)[term.c.x];
}
if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) {
@@ -2483,7 +2576,7 @@ check_control_code:
tnewline(1);
else
tmoveto(term.col - width, term.c.y);
- gp = &term.line[term.c.y][term.c.x];
+ gp = &TLINE(term.c.y)[term.c.x];
}
tsetchar(u, &term.c.attr, term.c.x, term.c.y);
@@ -2514,6 +2607,11 @@ twrite(const char *buf, int buflen, int show_ctrl)
Rune u;
int n;
+ if (TSCREEN.off) {
+ TSCREEN.off = 0;
+ tfulldirt();
+ }
+
for (n = 0; n < buflen; n += charsize) {
if (IS_SET(MODE_UTF8)) {
/* process a complete utf8 char */
@@ -2540,56 +2638,85 @@ twrite(const char *buf, int buflen, int show_ctrl)
}
void
-tresize(int col, int row)
+clearline(Line line, Glyph g, int x, int xend)
{
int i;
+ g.mode = 0;
+ g.u = ' ';
+ for (i = x; i < xend; ++i) {
+ line[i] = g;
+ }
+}
+
+Line
+ensureline(Line line)
+{
+ if (!line) {
+ line = xmalloc(term.linelen * sizeof(Glyph));
+ }
+ return line;
+}
+
+void
+tresize(int col, int row)
+{
+ int i, j;
int minrow = MIN(row, term.row);
int mincol = MIN(col, term.col);
+ int linelen = MAX(col, term.linelen);
int *bp;
- TCursor c;
- if (col < 1 || row < 1) {
+ if (col < 1 || row < 1 || row > HISTSIZE) {
fprintf(stderr,
"tresize: error resizing to %dx%d\n", col, row);
return;
}
- /*
- * slide screen to keep cursor where we expect it -
- * tscrollup would work here, but we can optimize to
- * memmove because we're freeing the earlier lines
- */
- for (i = 0; i <= term.c.y - row; i++) {
- free(term.line[i]);
- free(term.alt[i]);
+ /* Shift buffer to keep the cursor where we expect it */
+ if (row <= term.c.y) {
+ term.screen[0].cur = (term.screen[0].cur - row + term.c.y + 1) % term.screen[0].size;
+ }
+
+ /* Resize and clear line buffers as needed */
+ if (linelen > term.linelen) {
+ for (i = 0; i < term.screen[0].size; ++i) {
+ if (term.screen[0].buffer[i]) {
+ term.screen[0].buffer[i] = xrealloc(term.screen[0].buffer[i], linelen * sizeof(Glyph));
+ clearline(term.screen[0].buffer[i], term.c.attr, term.linelen, linelen);
+ }
+ }
+ for (i = 0; i < minrow; ++i) {
+ term.screen[1].buffer[i] = xrealloc(term.screen[1].buffer[i], linelen * sizeof(Glyph));
+ clearline(term.screen[1].buffer[i], term.c.attr, term.linelen, linelen);
+ }
}
- /* ensure that both src and dst are not NULL */
- if (i > 0) {
- memmove(term.line, term.line + i, row * sizeof(Line));
- memmove(term.alt, term.alt + i, row * sizeof(Line));
+ /* Allocate all visible lines for regular line buffer */
+ for (j = term.screen[0].cur, i = 0; i < row; ++i, j = (j + 1) % term.screen[0].size)
+ {
+ if (!term.screen[0].buffer[j]) {
+ term.screen[0].buffer[j] = xmalloc(linelen * sizeof(Glyph));
+ }
+ if (i >= term.row) {
+ clearline(term.screen[0].buffer[j], term.c.attr, 0, linelen);
+ }
}
- for (i += row; i < term.row; i++) {
- free(term.line[i]);
- free(term.alt[i]);
+ /* Resize alt screen */
+ term.screen[1].cur = 0;
+ term.screen[1].size = row;
+ for (i = row; i < term.row; ++i) {
+ free(term.screen[1].buffer[i]);
+ }
+ term.screen[1].buffer = xrealloc(term.screen[1].buffer, row * sizeof(Line));
+ for (i = term.row; i < row; ++i) {
+ term.screen[1].buffer[i] = xmalloc(linelen * sizeof(Glyph));
+ clearline(term.screen[1].buffer[i], term.c.attr, 0, linelen);
}
/* resize to new height */
- term.line = xrealloc(term.line, row * sizeof(Line));
- term.alt = xrealloc(term.alt, row * sizeof(Line));
term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
- /* resize each row to new width, zero-pad if needed */
- for (i = 0; i < minrow; i++) {
- term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
- term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph));
- }
-
- /* allocate any new rows */
- for (/* i = minrow */; i < row; i++) {
- term.line[i] = xmalloc(col * sizeof(Glyph));
- term.alt[i] = xmalloc(col * sizeof(Glyph));
- }
+ /* fix tabstops */
if (col > term.col) {
bp = term.tabs + term.col;
@@ -2599,26 +2726,16 @@ tresize(int col, int row)
for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
*bp = 1;
}
+
/* update terminal size */
term.col = col;
term.row = row;
+ term.linelen = linelen;
/* reset scrolling region */
tsetscroll(0, row-1);
/* make use of the LIMIT in tmoveto */
tmoveto(term.c.x, term.c.y);
- /* Clearing both screens (it makes dirty all lines) */
- c = term.c;
- for (i = 0; i < 2; i++) {
- if (mincol < col && 0 < minrow) {
- tclearregion(mincol, 0, col - 1, minrow - 1);
- }
- if (0 < col && minrow < row) {
- tclearregion(0, minrow, col - 1, row - 1);
- }
- tswapscreen();
- tcursor(CURSOR_LOAD);
- }
- term.c = c;
+ tfulldirt();
}
void
@@ -2630,14 +2747,15 @@ resettitle(void)
void
drawregion(int x1, int y1, int x2, int y2)
{
- int y;
+ int y, L;
+ L = TLINEOFFSET(y1);
for (y = y1; y < y2; y++) {
- if (!term.dirty[y])
- continue;
-
- term.dirty[y] = 0;
- xdrawline(term.line[y], x1, y, x2);
+ if (term.dirty[y]) {
+ term.dirty[y] = 0;
+ xdrawline(TSCREEN.buffer[L], x1, y, x2);
+ }
+ L = (L + 1) % TSCREEN.size;
}
}
@@ -2652,14 +2770,15 @@ draw(void)
/* adjust cursor position */
LIMIT(term.ocx, 0, term.col-1);
LIMIT(term.ocy, 0, term.row-1);
- if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY)
+ if (TLINE(term.ocy)[term.ocx].mode & ATTR_WDUMMY)
term.ocx--;
- if (term.line[term.c.y][cx].mode & ATTR_WDUMMY)
+ if (TLINE(term.c.y)[cx].mode & ATTR_WDUMMY)
cx--;
drawregion(0, 0, term.col, term.row);
- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
- term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
+ if (TSCREEN.off == 0)
+ xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
+ term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
term.ocx = cx;
term.ocy = term.c.y;
xfinishdraw();
diff --git a/st.h b/st.h
index fd3b0d8..3cea73b 100644
--- a/st.h
+++ b/st.h
@@ -19,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,
diff --git a/x.c b/x.c
index bd23686..25785a6 100644
--- a/x.c
+++ b/x.c
@@ -59,6 +59,8 @@ static void zoom(const Arg *);
static void zoomabs(const Arg *);
static void zoomreset(const Arg *);
static void ttysend(const Arg *);
+void kscrollup(const Arg *);
+void kscrolldown(const Arg *);
/* config.h for applying patches and the configuration. */
#include "config.h"

361
st.c
View File

@@ -43,6 +43,10 @@
#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
#define ISDELIM(u) (u && wcschr(worddelimiters, u)) #define ISDELIM(u) (u && wcschr(worddelimiters, u))
#define TSCREEN term.screen[IS_SET(MODE_ALTSCREEN)]
#define TLINEOFFSET(y) (((y) + TSCREEN.cur - TSCREEN.off + TSCREEN.size) % TSCREEN.size)
#define TLINE(y) (TSCREEN.buffer[TLINEOFFSET(y)])
enum term_mode { enum term_mode {
MODE_WRAP = 1 << 0, MODE_WRAP = 1 << 0,
MODE_INSERT = 1 << 1, MODE_INSERT = 1 << 1,
@@ -109,12 +113,21 @@ typedef struct {
int alt; int alt;
} Selection; } Selection;
/* Screen lines */
typedef struct {
Line* buffer; /* ring buffer */
int size; /* size of buffer */
int cur; /* start of active screen */
int off; /* scrollback line offset */
TCursor sc; /* saved cursor */
} LineBuffer;
/* Internal representation of the screen */ /* Internal representation of the screen */
typedef struct { typedef struct {
int row; /* nb row */ int row; /* nb row */
int col; /* nb col */ int col; /* nb col */
Line *line; /* screen */ LineBuffer screen[2]; /* screen and alternate screen */
Line *alt; /* alternate screen */ int linelen; /* allocated line length */
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 */
@@ -203,6 +216,8 @@ static void tdeftran(char);
static void tstrsequence(uchar); static void tstrsequence(uchar);
static void drawregion(int, int, int, int); static void drawregion(int, int, int, int);
static void clearline(Line, Glyph, int, int);
static Line ensureline(Line);
static void selnormalize(void); static void selnormalize(void);
static void selscroll(int, int); static void selscroll(int, int);
@@ -408,11 +423,12 @@ int
tlinelen(int y) tlinelen(int y)
{ {
int i = term.col; int i = term.col;
Line line = TLINE(y);
if (term.line[y][i - 1].mode & ATTR_WRAP) if (line[i - 1].mode & ATTR_WRAP)
return i; return i;
while (i > 0 && term.line[y][i - 1].u == ' ') while (i > 0 && line[i - 1].u == ' ')
--i; --i;
return i; return i;
@@ -521,7 +537,7 @@ selsnap(int *x, int *y, int direction)
* Snap around if the word wraps around at the end or * Snap around if the word wraps around at the end or
* beginning of a line. * beginning of a line.
*/ */
prevgp = &term.line[*y][*x]; prevgp = &TLINE(*y)[*x];
prevdelim = ISDELIM(prevgp->u); prevdelim = ISDELIM(prevgp->u);
for (;;) { for (;;) {
newx = *x + direction; newx = *x + direction;
@@ -536,14 +552,14 @@ selsnap(int *x, int *y, int direction)
yt = *y, xt = *x; yt = *y, xt = *x;
else else
yt = newy, xt = newx; yt = newy, xt = newx;
if (!(term.line[yt][xt].mode & ATTR_WRAP)) if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
break; break;
} }
if (newx >= tlinelen(newy)) if (newx >= tlinelen(newy))
break; break;
gp = &term.line[newy][newx]; gp = &TLINE(newy)[newx];
delim = ISDELIM(gp->u); delim = ISDELIM(gp->u);
if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
|| (delim && gp->u != prevgp->u))) || (delim && gp->u != prevgp->u)))
@@ -564,14 +580,14 @@ selsnap(int *x, int *y, int direction)
*x = (direction < 0) ? 0 : term.col - 1; *x = (direction < 0) ? 0 : term.col - 1;
if (direction < 0) { if (direction < 0) {
for (; *y > 0; *y += direction) { for (; *y > 0; *y += direction) {
if (!(term.line[*y-1][term.col-1].mode if (!(TLINE(*y-1)[term.col-1].mode
& ATTR_WRAP)) { & ATTR_WRAP)) {
break; break;
} }
} }
} else if (direction > 0) { } else if (direction > 0) {
for (; *y < term.row-1; *y += direction) { for (; *y < term.row-1; *y += direction) {
if (!(term.line[*y][term.col-1].mode if (!(TLINE(*y)[term.col-1].mode
& ATTR_WRAP)) { & ATTR_WRAP)) {
break; break;
} }
@@ -602,13 +618,13 @@ getsel(void)
} }
if (sel.type == SEL_RECTANGULAR) { if (sel.type == SEL_RECTANGULAR) {
gp = &term.line[y][sel.nb.x]; gp = &TLINE(y)[sel.nb.x];
lastx = sel.ne.x; lastx = sel.ne.x;
} else { } else {
gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0];
lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
} }
last = &term.line[y][MIN(lastx, linelen-1)]; last = &TLINE(y)[MIN(lastx, linelen-1)];
while (last >= gp && last->u == ' ') while (last >= gp && last->u == ' ')
--last; --last;
@@ -949,12 +965,15 @@ int
tattrset(int attr) tattrset(int attr)
{ {
int i, j; int i, j;
int y = TLINEOFFSET(0);
for (i = 0; i < term.row-1; i++) { for (i = 0; i < term.row-1; i++) {
Line line = TSCREEN.buffer[y];
for (j = 0; j < term.col-1; j++) { for (j = 0; j < term.col-1; j++) {
if (term.line[i][j].mode & attr) if (line[j].mode & attr)
return 1; return 1;
} }
y = (y+1) % TSCREEN.size;
} }
return 0; return 0;
@@ -976,14 +995,17 @@ void
tsetdirtattr(int attr) tsetdirtattr(int attr)
{ {
int i, j; int i, j;
int y = TLINEOFFSET(0);
for (i = 0; i < term.row-1; i++) { for (i = 0; i < term.row-1; i++) {
Line line = TSCREEN.buffer[y];
for (j = 0; j < term.col-1; j++) { for (j = 0; j < term.col-1; j++) {
if (term.line[i][j].mode & attr) { if (line[j].mode & attr) {
tsetdirt(i, i); tsetdirt(i, i);
break; break;
} }
} }
y = (y+1) % TSCREEN.size;
} }
} }
@@ -996,27 +1018,19 @@ tfulldirt(void)
void void
tcursor(int mode) tcursor(int mode)
{ {
static TCursor c[2];
int alt = IS_SET(MODE_ALTSCREEN);
if (mode == CURSOR_SAVE) { if (mode == CURSOR_SAVE) {
c[alt] = term.c; TSCREEN.sc = term.c;
} else if (mode == CURSOR_LOAD) { } else if (mode == CURSOR_LOAD) {
term.c = c[alt]; term.c = TSCREEN.sc;
tmoveto(c[alt].x, c[alt].y); tmoveto(term.c.x, term.c.y);
} }
} }
void void
treset(void) treset(void)
{ {
uint i; int i, j;
Glyph g = (Glyph){ .fg = defaultfg, .bg = defaultbg};
term.c = (TCursor){{
.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)
@@ -1028,17 +1042,37 @@ treset(void)
term.charset = 0; term.charset = 0;
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
tmoveto(0, 0); term.screen[i].sc = (TCursor){{
tcursor(CURSOR_SAVE); .fg = defaultfg,
tclearregion(0, 0, term.col-1, term.row-1); .bg = defaultbg
tswapscreen(); }};
term.screen[i].cur = 0;
term.screen[i].off = 0;
for (j = 0; j < term.row; ++j) {
if (term.col != term.linelen)
term.screen[i].buffer[j] = xrealloc(term.screen[i].buffer[j], term.col * sizeof(Glyph));
clearline(term.screen[i].buffer[j], g, 0, term.col);
} }
for (j = term.row; j < term.screen[i].size; ++j) {
free(term.screen[i].buffer[j]);
term.screen[i].buffer[j] = NULL;
}
}
tcursor(CURSOR_LOAD);
term.linelen = term.col;
tfulldirt();
} }
void void
tnew(int col, int row) tnew(int col, int row)
{ {
term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; int i;
term = (Term){};
term.screen[0].buffer = xmalloc(HISTSIZE * sizeof(Line));
term.screen[0].size = HISTSIZE;
term.screen[1].buffer = NULL;
for (i = 0; i < HISTSIZE; ++i) term.screen[0].buffer[i] = NULL;
tresize(col, row); tresize(col, row);
treset(); treset();
} }
@@ -1046,14 +1080,42 @@ tnew(int col, int row)
void void
tswapscreen(void) tswapscreen(void)
{ {
Line *tmp = term.line;
term.line = term.alt;
term.alt = tmp;
term.mode ^= MODE_ALTSCREEN; term.mode ^= MODE_ALTSCREEN;
tfulldirt(); tfulldirt();
} }
void
kscrollup(const Arg *a)
{
int n = a->i;
if (IS_SET(MODE_ALTSCREEN))
return;
if (n < 0) n = (-n) * term.row;
if (n > TSCREEN.size - term.row - TSCREEN.off) n = TSCREEN.size - term.row - TSCREEN.off;
while (!TLINE(-n)) --n;
TSCREEN.off += n;
selscroll(0, n);
tfulldirt();
}
void
kscrolldown(const Arg *a)
{
int n = a->i;
if (IS_SET(MODE_ALTSCREEN))
return;
if (n < 0) n = (-n) * term.row;
if (n > TSCREEN.off) n = TSCREEN.off;
TSCREEN.off -= n;
selscroll(0, -n);
tfulldirt();
}
void void
tscrolldown(int orig, int n) tscrolldown(int orig, int n)
{ {
@@ -1062,15 +1124,29 @@ tscrolldown(int orig, int n)
LIMIT(n, 0, term.bot-orig+1); LIMIT(n, 0, term.bot-orig+1);
tsetdirt(orig, term.bot-n); /* Ensure that lines are allocated */
tclearregion(0, term.bot-n+1, term.col-1, term.bot); for (i = -n; i < 0; i++) {
TLINE(i) = ensureline(TLINE(i));
for (i = term.bot; i >= orig+n; i--) {
temp = term.line[i];
term.line[i] = term.line[i-n];
term.line[i-n] = temp;
} }
/* Shift non-scrolling areas in ring buffer */
for (i = term.bot+1; i < term.row; i++) {
temp = TLINE(i);
TLINE(i) = TLINE(i-n);
TLINE(i-n) = temp;
}
for (i = 0; i < orig; i++) {
temp = TLINE(i);
TLINE(i) = TLINE(i-n);
TLINE(i-n) = temp;
}
/* Scroll buffer */
TSCREEN.cur = (TSCREEN.cur + TSCREEN.size - n) % TSCREEN.size;
/* Clear lines that have entered the view */
tclearregion(0, orig, term.linelen-1, orig+n-1);
/* Redraw portion of the screen that has scrolled */
tsetdirt(orig+n-1, term.bot);
selscroll(orig, n); selscroll(orig, n);
} }
@@ -1082,15 +1158,29 @@ tscrollup(int orig, int n)
LIMIT(n, 0, term.bot-orig+1); LIMIT(n, 0, term.bot-orig+1);
tclearregion(0, orig, term.col-1, orig+n-1); /* Ensure that lines are allocated */
tsetdirt(orig+n, term.bot); for (i = term.row; i < term.row + n; i++) {
TLINE(i) = ensureline(TLINE(i));
for (i = orig; i <= term.bot-n; i++) {
temp = term.line[i];
term.line[i] = term.line[i+n];
term.line[i+n] = temp;
} }
/* Shift non-scrolling areas in ring buffer */
for (i = orig-1; i >= 0; i--) {
temp = TLINE(i);
TLINE(i) = TLINE(i+n);
TLINE(i+n) = temp;
}
for (i = term.row-1; i >term.bot; i--) {
temp = TLINE(i);
TLINE(i) = TLINE(i+n);
TLINE(i+n) = temp;
}
/* Scroll buffer */
TSCREEN.cur = (TSCREEN.cur + n) % TSCREEN.size;
/* Clear lines that have entered the view */
tclearregion(0, term.bot-n+1, term.linelen-1, term.bot);
/* Redraw portion of the screen that has scrolled */
tsetdirt(orig, term.bot-n+1);
selscroll(orig, -n); selscroll(orig, -n);
} }
@@ -1197,6 +1287,7 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
"", "", "", "", "", "", "", "", /* p - w */ "", "", "", "", "", "", "", "", /* p - w */
"", "", "", "π", "", "£", "·", /* x - ~ */ "", "", "", "π", "", "£", "·", /* x - ~ */
}; };
Line line = TLINE(y);
/* /*
* The table is proudly stolen from rxvt. * The table is proudly stolen from rxvt.
@@ -1205,25 +1296,25 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])
utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);
if (term.line[y][x].mode & ATTR_WIDE) { if (line[x].mode & ATTR_WIDE) {
if (x+1 < term.col) { if (x+1 < term.col) {
term.line[y][x+1].u = ' '; line[x+1].u = ' ';
term.line[y][x+1].mode &= ~ATTR_WDUMMY; line[x+1].mode &= ~ATTR_WDUMMY;
} }
} else if (term.line[y][x].mode & ATTR_WDUMMY) { } else if (line[x].mode & ATTR_WDUMMY) {
term.line[y][x-1].u = ' '; line[x-1].u = ' ';
term.line[y][x-1].mode &= ~ATTR_WIDE; line[x-1].mode &= ~ATTR_WIDE;
} }
term.dirty[y] = 1; term.dirty[y] = 1;
term.line[y][x] = *attr; line[x] = *attr;
term.line[y][x].u = u; line[x].u = u;
} }
void void
tclearregion(int x1, int y1, int x2, int y2) tclearregion(int x1, int y1, int x2, int y2)
{ {
int x, y, temp; int x, y, L, S, temp;
Glyph *gp; Glyph *gp;
if (x1 > x2) if (x1 > x2)
@@ -1231,15 +1322,16 @@ tclearregion(int x1, int y1, int x2, int y2)
if (y1 > y2) if (y1 > y2)
temp = y1, y1 = y2, y2 = temp; temp = y1, y1 = y2, y2 = temp;
LIMIT(x1, 0, term.col-1); LIMIT(x1, 0, term.linelen-1);
LIMIT(x2, 0, term.col-1); LIMIT(x2, 0, term.linelen-1);
LIMIT(y1, 0, term.row-1); LIMIT(y1, 0, term.row-1);
LIMIT(y2, 0, term.row-1); LIMIT(y2, 0, term.row-1);
L = TLINEOFFSET(y1);
for (y = y1; y <= y2; y++) { for (y = y1; y <= y2; y++) {
term.dirty[y] = 1; term.dirty[y] = 1;
for (x = x1; x <= x2; x++) { for (x = x1; x <= x2; x++) {
gp = &term.line[y][x]; gp = &TSCREEN.buffer[L][x];
if (selected(x, y)) if (selected(x, y))
selclear(); selclear();
gp->fg = term.c.attr.fg; gp->fg = term.c.attr.fg;
@@ -1247,6 +1339,7 @@ tclearregion(int x1, int y1, int x2, int y2)
gp->mode = 0; gp->mode = 0;
gp->u = ' '; gp->u = ' ';
} }
L = (L + 1) % TSCREEN.size;
} }
} }
@@ -1261,7 +1354,7 @@ tdeletechar(int n)
dst = term.c.x; dst = term.c.x;
src = term.c.x + n; src = term.c.x + n;
size = term.col - src; size = term.col - src;
line = term.line[term.c.y]; line = TLINE(term.c.y);
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);
@@ -1278,7 +1371,7 @@ tinsertblank(int n)
dst = term.c.x + n; dst = term.c.x + n;
src = term.c.x; src = term.c.x;
size = term.col - dst; size = term.col - dst;
line = term.line[term.c.y]; line = TLINE(term.c.y);
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);
@@ -2105,7 +2198,7 @@ tdumpline(int n)
char buf[UTF_SIZ]; char buf[UTF_SIZ];
const Glyph *bp, *end; const Glyph *bp, *end;
bp = &term.line[n][0]; bp = &TLINE(n)[0];
end = &bp[MIN(tlinelen(n), term.col) - 1]; end = &bp[MIN(tlinelen(n), term.col) - 1];
if (bp != end || bp->u != ' ') { if (bp != end || bp->u != ' ') {
for ( ; bp <= end; ++bp) for ( ; bp <= end; ++bp)
@@ -2493,11 +2586,11 @@ check_control_code:
if (selected(term.c.x, term.c.y)) if (selected(term.c.x, term.c.y))
selclear(); selclear();
gp = &term.line[term.c.y][term.c.x]; gp = &TLINE(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)) {
gp->mode |= ATTR_WRAP; gp->mode |= ATTR_WRAP;
tnewline(1); tnewline(1);
gp = &term.line[term.c.y][term.c.x]; gp = &TLINE(term.c.y)[term.c.x];
} }
if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) { if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) {
@@ -2510,7 +2603,7 @@ check_control_code:
tnewline(1); tnewline(1);
else else
tmoveto(term.col - width, term.c.y); tmoveto(term.col - width, term.c.y);
gp = &term.line[term.c.y][term.c.x]; gp = &TLINE(term.c.y)[term.c.x];
} }
tsetchar(u, &term.c.attr, term.c.x, term.c.y); tsetchar(u, &term.c.attr, term.c.x, term.c.y);
@@ -2541,6 +2634,11 @@ twrite(const char *buf, int buflen, int show_ctrl)
Rune u; Rune u;
int n; int n;
if (TSCREEN.off) {
TSCREEN.off = 0;
tfulldirt();
}
for (n = 0; n < buflen; n += charsize) { for (n = 0; n < buflen; n += charsize) {
if (IS_SET(MODE_UTF8)) { if (IS_SET(MODE_UTF8)) {
/* process a complete utf8 char */ /* process a complete utf8 char */
@@ -2567,56 +2665,85 @@ twrite(const char *buf, int buflen, int show_ctrl)
} }
void void
tresize(int col, int row) clearline(Line line, Glyph g, int x, int xend)
{ {
int i; int i;
g.mode = 0;
g.u = ' ';
for (i = x; i < xend; ++i) {
line[i] = g;
}
}
Line
ensureline(Line line)
{
if (!line) {
line = xmalloc(term.linelen * sizeof(Glyph));
}
return line;
}
void
tresize(int col, int row)
{
int i, j;
int minrow = MIN(row, term.row); int minrow = MIN(row, term.row);
int mincol = MIN(col, term.col); int mincol = MIN(col, term.col);
int linelen = MAX(col, term.linelen);
int *bp; int *bp;
TCursor c;
if (col < 1 || row < 1) { if (col < 1 || row < 1 || row > HISTSIZE) {
fprintf(stderr, fprintf(stderr,
"tresize: error resizing to %dx%d\n", col, row); "tresize: error resizing to %dx%d\n", col, row);
return; return;
} }
/* /* Shift buffer to keep the cursor where we expect it */
* slide screen to keep cursor where we expect it - if (row <= term.c.y) {
* tscrollup would work here, but we can optimize to term.screen[0].cur = (term.screen[0].cur - row + term.c.y + 1) % term.screen[0].size;
* memmove because we're freeing the earlier lines
*/
for (i = 0; i <= term.c.y - row; i++) {
free(term.line[i]);
free(term.alt[i]);
} }
/* ensure that both src and dst are not NULL */
if (i > 0) { /* Resize and clear line buffers as needed */
memmove(term.line, term.line + i, row * sizeof(Line)); if (linelen > term.linelen) {
memmove(term.alt, term.alt + i, row * sizeof(Line)); for (i = 0; i < term.screen[0].size; ++i) {
if (term.screen[0].buffer[i]) {
term.screen[0].buffer[i] = xrealloc(term.screen[0].buffer[i], linelen * sizeof(Glyph));
clearline(term.screen[0].buffer[i], term.c.attr, term.linelen, linelen);
} }
for (i += row; i < term.row; i++) { }
free(term.line[i]); for (i = 0; i < minrow; ++i) {
free(term.alt[i]); term.screen[1].buffer[i] = xrealloc(term.screen[1].buffer[i], linelen * sizeof(Glyph));
clearline(term.screen[1].buffer[i], term.c.attr, term.linelen, linelen);
}
}
/* Allocate all visible lines for regular line buffer */
for (j = term.screen[0].cur, i = 0; i < row; ++i, j = (j + 1) % term.screen[0].size)
{
if (!term.screen[0].buffer[j]) {
term.screen[0].buffer[j] = xmalloc(linelen * sizeof(Glyph));
}
if (i >= term.row) {
clearline(term.screen[0].buffer[j], term.c.attr, 0, linelen);
}
}
/* Resize alt screen */
term.screen[1].cur = 0;
term.screen[1].size = row;
for (i = row; i < term.row; ++i) {
free(term.screen[1].buffer[i]);
}
term.screen[1].buffer = xrealloc(term.screen[1].buffer, row * sizeof(Line));
for (i = term.row; i < row; ++i) {
term.screen[1].buffer[i] = xmalloc(linelen * sizeof(Glyph));
clearline(term.screen[1].buffer[i], term.c.attr, 0, linelen);
} }
/* resize to new height */ /* resize to new height */
term.line = xrealloc(term.line, row * sizeof(Line));
term.alt = xrealloc(term.alt, row * sizeof(Line));
term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
/* resize each row to new width, zero-pad if needed */ /* fix tabstops */
for (i = 0; i < minrow; i++) {
term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph));
}
/* allocate any new rows */
for (/* i = minrow */; i < row; i++) {
term.line[i] = xmalloc(col * sizeof(Glyph));
term.alt[i] = xmalloc(col * sizeof(Glyph));
}
if (col > term.col) { if (col > term.col) {
bp = term.tabs + term.col; bp = term.tabs + term.col;
@@ -2626,26 +2753,16 @@ tresize(int col, int row)
for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
*bp = 1; *bp = 1;
} }
/* update terminal size */ /* update terminal size */
term.col = col; term.col = col;
term.row = row; term.row = row;
term.linelen = linelen;
/* reset scrolling region */ /* reset scrolling region */
tsetscroll(0, row-1); tsetscroll(0, row-1);
/* make use of the LIMIT in tmoveto */ /* make use of the LIMIT in tmoveto */
tmoveto(term.c.x, term.c.y); tmoveto(term.c.x, term.c.y);
/* Clearing both screens (it makes dirty all lines) */ tfulldirt();
c = term.c;
for (i = 0; i < 2; i++) {
if (mincol < col && 0 < minrow) {
tclearregion(mincol, 0, col - 1, minrow - 1);
}
if (0 < col && minrow < row) {
tclearregion(0, minrow, col - 1, row - 1);
}
tswapscreen();
tcursor(CURSOR_LOAD);
}
term.c = c;
} }
void void
@@ -2657,14 +2774,15 @@ resettitle(void)
void void
drawregion(int x1, int y1, int x2, int y2) drawregion(int x1, int y1, int x2, int y2)
{ {
int y; int y, L;
L = TLINEOFFSET(y1);
for (y = y1; y < y2; y++) { for (y = y1; y < y2; y++) {
if (!term.dirty[y]) if (term.dirty[y]) {
continue;
term.dirty[y] = 0; term.dirty[y] = 0;
xdrawline(term.line[y], x1, y, x2); xdrawline(TSCREEN.buffer[L], x1, y, x2);
}
L = (L + 1) % TSCREEN.size;
} }
} }
@@ -2679,14 +2797,15 @@ draw(void)
/* adjust cursor position */ /* adjust cursor position */
LIMIT(term.ocx, 0, term.col-1); LIMIT(term.ocx, 0, term.col-1);
LIMIT(term.ocy, 0, term.row-1); LIMIT(term.ocy, 0, term.row-1);
if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY) if (TLINE(term.ocy)[term.ocx].mode & ATTR_WDUMMY)
term.ocx--; term.ocx--;
if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) if (TLINE(term.c.y)[cx].mode & ATTR_WDUMMY)
cx--; cx--;
drawregion(0, 0, term.col, term.row); drawregion(0, 0, term.col, term.row);
xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], if (TSCREEN.off == 0)
term.ocx, term.ocy, term.line[term.ocy][term.ocx]); xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
term.ocx = cx; term.ocx = cx;
term.ocy = term.c.y; term.ocy = term.c.y;
xfinishdraw(); xfinishdraw();

1
st.h
View File

@@ -19,6 +19,7 @@
#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
enum glyph_attribute { enum glyph_attribute {
ATTR_NULL = 0, ATTR_NULL = 0,

BIN
st.o Normal file

Binary file not shown.

6
x.c
View File

@@ -59,6 +59,8 @@ 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 *);
void kscrollup(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"
@@ -1415,10 +1417,6 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
bg = &dc.col[base.bg]; bg = &dc.col[base.bg];
} }
/* Change basic system colors [0-7] to bright system colors [8-15] */
if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7))
fg = &dc.col[base.fg + 8];
if (IS_SET(MODE_REVERSE)) { if (IS_SET(MODE_REVERSE)) {
if (fg == &dc.col[defaultfg]) { if (fg == &dc.col[defaultfg]) {
fg = &dc.col[defaultbg]; fg = &dc.col[defaultbg];

2108
x.c.orig Normal file

File diff suppressed because it is too large Load Diff

BIN
x.o Normal file

Binary file not shown.