12 Commits

Author SHA1 Message Date
cd9cf3e007 Merge branch 'scrollback' 2025-11-25 19:26:44 -05:00
847d3a991b removed binaries 2025-11-25 19:24:52 -05:00
40a681d588 added basic gitignore file to remove bin files 2025-11-25 19:23:25 -05:00
b5942724bc added basic scroll functionality 2025-11-25 19:20:51 -05:00
Hiltjo Posthuma
6e97047474 bump version to 0.9.3 2025-08-09 14:35:14 +02:00
Hiltjo Posthuma
5a4666c19e add a few comments 2025-08-09 14:22:28 +02:00
Ayman Bagabas
d6c431859c Support OSC 110, 111, and 112 for resetting colors
This adds support for OSC 110, 111, and 112 escape sequences to reset
the foreground, background, and cursor colors in the terminal. The
changes include handling these sequences in the `strhandle` function of
`st.c`, allowing applications to reset colors to their default values.

The OSC sequences originated from Xterm control sequences and are now
widely used in terminal applications and supported by many terminal
emulators. For applications, this allows them to reset colors to
default values without needing to know the colors beforehand.

Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
2025-08-09 12:34:01 +02:00
sasha
f114bcedd1 Eat up "CSI 58" sequences
This is used in the wild by systemd systemctl for example and st
misinterpreted it as "blink", because it didn't know "58", then saw "5"
as "blink", and then didn't know "245".

This should print "foo" as normal text:

    printf '\e[58:5:245mfoo\n'
    printf '\e[58:2:50:100:200mfoo\n'
2025-07-27 20:06:54 +02:00
Johannes Altmanninger
98610fcd37 Do not interpret CSI ? u as DECRC
The kitty keyboard protocol docs recommend CSI ? u to query support for
that protocol, see https://sw.kovidgoyal.net/kitty/keyboard-protocol/

For better or worse, fish shell uses this query to work around bugs
in other terminals triggered by requesting that protocol via CSI = 5 u.

Unfortunately, st interprets CSI ? u as DECRC (restore cursor
position). reproduce with 'printf "\x1b[?u"; cat'.

fish could work around this by switching to the alternate screen
before running this query; but that might cause tearing on terminals
that don't support Synchronized Output. I'm not sure.

In the meantime, let's correct our parser.

This adds a redundant else-after-return, for consistency with the
surrounding code.
2025-01-30 17:50:37 +01:00
Markus Rinne
6009e6e25b Clear screen: Fix edge case
With sequence \e[1J, if cursor is on second line, clear the first line.
2024-12-06 13:42:50 +01:00
Lucas de Sena
a0274bc20e fix BadMatch error when embedding on some windows
When embedded, st fails with BadMatch error if the embedder's window has
non-default colormap/depth/visual.  This commit fixes that by creating
st's window inside root and then reparent it into embedder.

The reference window for dc.gc is also changed to match root's visuals.

A similar commit had been made for dmenu[1].
See this issue[2] on github for context.

[1]: https://git.suckless.org/dmenu/commit/0fe460dbd469a1d5b6a7140d0e1801935e4a923b.html
[2]: https://github.com/phillbush/xfiles/issues/47
2024-08-09 13:34:56 +02:00
Hiltjo Posthuma
5dbcca4926 support colons in SGR character attributes
Patch by Mikhail Kot <to@myrrc.dev>
With some modifications to behave more like xterm (see note below).

Example:

	printf '\033[48;2;255:0:0mtest\n'

https://invisible-island.net/xterm/ctlseqs/ctlseqs.html

Some notes:

"CSI Pm m  Character Attributes (SGR).
[...]
o   xterm allows either colons (standard) or semicolons
(legacy) to separate the subparameters (but after the
first colon, colons must be used).
2024-05-01 20:45:39 +02:00
9 changed files with 977 additions and 45 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.o

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{|}~";

View File

@@ -1,5 +1,5 @@
# st version # st version
VERSION = 0.9.2 VERSION = 0.9.3
# Customize below to fit your system # Customize below to fit your system

View File

@@ -0,0 +1,351 @@
diff --git a/config.def.h b/config.def.h
index 2cd740a..40b7d93 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..2478942 100644
--- a/st.c
+++ b/st.c
@@ -35,6 +35,7 @@
#define ESC_ARG_SIZ 16
#define STR_BUF_SIZ ESC_BUF_SIZ
#define STR_ARG_SIZ ESC_ARG_SIZ
+#define HISTSIZE 2000
/* macros */
#define IS_SET(flag) ((term.mode & (flag)) != 0)
@@ -42,6 +43,9 @@
#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
#define ISDELIM(u) (u && wcschr(worddelimiters, u))
+#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \
+ term.scr + HISTSIZE + 1) % HISTSIZE] : \
+ term.line[(y) - term.scr])
enum term_mode {
MODE_WRAP = 1 << 0,
@@ -115,6 +119,9 @@ typedef struct {
int col; /* nb col */
Line *line; /* screen */
Line *alt; /* alternate screen */
+ Line hist[HISTSIZE]; /* history buffer */
+ int histi; /* history index */
+ int scr; /* scroll back */
int *dirty; /* dirtyness of lines */
TCursor c; /* cursor */
int ocx; /* old cursor col */
@@ -185,8 +192,8 @@ static void tnewline(int);
static void tputtab(int);
static void tputc(Rune);
static void treset(void);
-static void tscrollup(int, int);
-static void tscrolldown(int, int);
+static void tscrollup(int, int, int);
+static void tscrolldown(int, int, int);
static void tsetattr(const int *, int);
static void tsetchar(Rune, const Glyph *, int, int);
static void tsetdirt(int, int);
@@ -409,10 +416,10 @@ tlinelen(int y)
{
int i = term.col;
- if (term.line[y][i - 1].mode & ATTR_WRAP)
+ if (TLINE(y)[i - 1].mode & ATTR_WRAP)
return i;
- while (i > 0 && term.line[y][i - 1].u == ' ')
+ while (i > 0 && TLINE(y)[i - 1].u == ' ')
--i;
return i;
@@ -521,7 +528,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 +543,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 +571,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 +609,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;
@@ -844,6 +851,9 @@ void
ttywrite(const char *s, size_t n, int may_echo)
{
const char *next;
+ Arg arg = (Arg) { .i = term.scr };
+
+ kscrolldown(&arg);
if (may_echo && IS_SET(MODE_ECHO))
twrite(s, n, 1);
@@ -1055,13 +1065,53 @@ tswapscreen(void)
}
void
-tscrolldown(int orig, int n)
+kscrolldown(const Arg* a)
+{
+ int n = a->i;
+
+ if (n < 0)
+ n = term.row + n;
+
+ if (n > term.scr)
+ n = term.scr;
+
+ if (term.scr > 0) {
+ term.scr -= n;
+ selscroll(0, -n);
+ tfulldirt();
+ }
+}
+
+void
+kscrollup(const Arg* a)
+{
+ int n = a->i;
+
+ if (n < 0)
+ n = term.row + n;
+
+ if (term.scr <= HISTSIZE-n) {
+ term.scr += n;
+ selscroll(0, n);
+ tfulldirt();
+ }
+}
+
+void
+tscrolldown(int orig, int n, int copyhist)
{
int i;
Line temp;
LIMIT(n, 0, term.bot-orig+1);
+ if (copyhist) {
+ term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE;
+ temp = term.hist[term.histi];
+ term.hist[term.histi] = term.line[term.bot];
+ term.line[term.bot] = temp;
+ }
+
tsetdirt(orig, term.bot-n);
tclearregion(0, term.bot-n+1, term.col-1, term.bot);
@@ -1071,17 +1121,28 @@ tscrolldown(int orig, int n)
term.line[i-n] = temp;
}
- selscroll(orig, n);
+ if (term.scr == 0)
+ selscroll(orig, n);
}
void
-tscrollup(int orig, int n)
+tscrollup(int orig, int n, int copyhist)
{
int i;
Line temp;
LIMIT(n, 0, term.bot-orig+1);
+ if (copyhist) {
+ term.histi = (term.histi + 1) % HISTSIZE;
+ temp = term.hist[term.histi];
+ term.hist[term.histi] = term.line[orig];
+ term.line[orig] = temp;
+ }
+
+ if (term.scr > 0 && term.scr < HISTSIZE)
+ term.scr = MIN(term.scr + n, HISTSIZE-1);
+
tclearregion(0, orig, term.col-1, orig+n-1);
tsetdirt(orig+n, term.bot);
@@ -1091,7 +1152,8 @@ tscrollup(int orig, int n)
term.line[i+n] = temp;
}
- selscroll(orig, -n);
+ if (term.scr == 0)
+ selscroll(orig, -n);
}
void
@@ -1120,7 +1182,7 @@ tnewline(int first_col)
int y = term.c.y;
if (y == term.bot) {
- tscrollup(term.top, 1);
+ tscrollup(term.top, 1, 1);
} else {
y++;
}
@@ -1285,14 +1347,14 @@ void
tinsertblankline(int n)
{
if (BETWEEN(term.c.y, term.top, term.bot))
- tscrolldown(term.c.y, n);
+ tscrolldown(term.c.y, n, 0);
}
void
tdeleteline(int n)
{
if (BETWEEN(term.c.y, term.top, term.bot))
- tscrollup(term.c.y, n);
+ tscrollup(term.c.y, n, 0);
}
int32_t
@@ -1730,11 +1792,11 @@ csihandle(void)
case 'S': /* SU -- Scroll <n> line up */
if (csiescseq.priv) break;
DEFAULT(csiescseq.arg[0], 1);
- tscrollup(term.top, csiescseq.arg[0]);
+ tscrollup(term.top, csiescseq.arg[0], 0);
break;
case 'T': /* SD -- Scroll <n> line down */
DEFAULT(csiescseq.arg[0], 1);
- tscrolldown(term.top, csiescseq.arg[0]);
+ tscrolldown(term.top, csiescseq.arg[0], 0);
break;
case 'L': /* IL -- Insert <n> blank lines */
DEFAULT(csiescseq.arg[0], 1);
@@ -2306,7 +2368,7 @@ eschandle(uchar ascii)
return 0;
case 'D': /* IND -- Linefeed */
if (term.c.y == term.bot) {
- tscrollup(term.top, 1);
+ tscrollup(term.top, 1, 1);
} else {
tmoveto(term.c.x, term.c.y+1);
}
@@ -2319,7 +2381,7 @@ eschandle(uchar ascii)
break;
case 'M': /* RI -- Reverse index */
if (term.c.y == term.top) {
- tscrolldown(term.top, 1);
+ tscrolldown(term.top, 1, 1);
} else {
tmoveto(term.c.x, term.c.y-1);
}
@@ -2542,7 +2604,7 @@ twrite(const char *buf, int buflen, int show_ctrl)
void
tresize(int col, int row)
{
- int i;
+ int i, j;
int minrow = MIN(row, term.row);
int mincol = MIN(col, term.col);
int *bp;
@@ -2579,6 +2641,14 @@ tresize(int col, int row)
term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
+ for (i = 0; i < HISTSIZE; i++) {
+ term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph));
+ for (j = mincol; j < col; j++) {
+ term.hist[i][j] = term.c.attr;
+ term.hist[i][j].u = ' ';
+ }
+ }
+
/* 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));
@@ -2637,7 +2707,7 @@ drawregion(int x1, int y1, int x2, int y2)
continue;
term.dirty[y] = 0;
- xdrawline(term.line[y], x1, y, x2);
+ xdrawline(TLINE(y), x1, y, x2);
}
}
@@ -2658,8 +2728,9 @@ draw(void)
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 (term.scr == 0)
+ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
+ term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
term.ocx = cx;
term.ocy = term.c.y;
xfinishdraw();
diff --git a/st.h b/st.h
index fd3b0d8..818a6f8 100644
--- a/st.h
+++ b/st.h
@@ -81,6 +81,8 @@ void die(const char *, ...);
void redraw(void);
void draw(void);
+void kscrolldown(const Arg *);
+void kscrollup(const Arg *);
void printscreen(const Arg *);
void printsel(const Arg *);
void sendbreak(const Arg *);

BIN
st Executable file

Binary file not shown.

177
st.c
View File

@@ -35,6 +35,7 @@
#define ESC_ARG_SIZ 16 #define ESC_ARG_SIZ 16
#define STR_BUF_SIZ ESC_BUF_SIZ #define STR_BUF_SIZ ESC_BUF_SIZ
#define STR_ARG_SIZ ESC_ARG_SIZ #define STR_ARG_SIZ ESC_ARG_SIZ
#define HISTSIZE 2000
/* macros */ /* macros */
#define IS_SET(flag) ((term.mode & (flag)) != 0) #define IS_SET(flag) ((term.mode & (flag)) != 0)
@@ -42,6 +43,9 @@
#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
#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 TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \
term.scr + HISTSIZE + 1) % HISTSIZE] : \
term.line[(y) - term.scr])
enum term_mode { enum term_mode {
MODE_WRAP = 1 << 0, MODE_WRAP = 1 << 0,
@@ -115,6 +119,9 @@ typedef struct {
int col; /* nb col */ int col; /* nb col */
Line *line; /* screen */ Line *line; /* screen */
Line *alt; /* alternate screen */ Line *alt; /* alternate screen */
Line hist[HISTSIZE]; /* history buffer */
int histi; /* history index */
int scr; /* scroll back */
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 */
@@ -185,8 +192,8 @@ static void tnewline(int);
static void tputtab(int); static void tputtab(int);
static void tputc(Rune); static void tputc(Rune);
static void treset(void); static void treset(void);
static void tscrollup(int, int); static void tscrollup(int, int, int);
static void tscrolldown(int, int); static void tscrolldown(int, int, int);
static void tsetattr(const int *, int); static void tsetattr(const int *, int);
static void tsetchar(Rune, const Glyph *, int, int); static void tsetchar(Rune, const Glyph *, int, int);
static void tsetdirt(int, int); static void tsetdirt(int, int);
@@ -409,10 +416,10 @@ tlinelen(int y)
{ {
int i = term.col; int i = term.col;
if (term.line[y][i - 1].mode & ATTR_WRAP) if (TLINE(y)[i - 1].mode & ATTR_WRAP)
return i; return i;
while (i > 0 && term.line[y][i - 1].u == ' ') while (i > 0 && TLINE(y)[i - 1].u == ' ')
--i; --i;
return i; return i;
@@ -521,7 +528,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 +543,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 +571,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 +609,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;
@@ -844,6 +851,9 @@ void
ttywrite(const char *s, size_t n, int may_echo) ttywrite(const char *s, size_t n, int may_echo)
{ {
const char *next; const char *next;
Arg arg = (Arg) { .i = term.scr };
kscrolldown(&arg);
if (may_echo && IS_SET(MODE_ECHO)) if (may_echo && IS_SET(MODE_ECHO))
twrite(s, n, 1); twrite(s, n, 1);
@@ -1055,13 +1065,53 @@ tswapscreen(void)
} }
void void
tscrolldown(int orig, int n) kscrolldown(const Arg* a)
{
int n = a->i;
if (n < 0)
n = term.row + n;
if (n > term.scr)
n = term.scr;
if (term.scr > 0) {
term.scr -= n;
selscroll(0, -n);
tfulldirt();
}
}
void
kscrollup(const Arg* a)
{
int n = a->i;
if (n < 0)
n = term.row + n;
if (term.scr <= HISTSIZE-n) {
term.scr += n;
selscroll(0, n);
tfulldirt();
}
}
void
tscrolldown(int orig, int n, int copyhist)
{ {
int i; int i;
Line temp; Line temp;
LIMIT(n, 0, term.bot-orig+1); LIMIT(n, 0, term.bot-orig+1);
if (copyhist) {
term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE;
temp = term.hist[term.histi];
term.hist[term.histi] = term.line[term.bot];
term.line[term.bot] = temp;
}
tsetdirt(orig, term.bot-n); tsetdirt(orig, term.bot-n);
tclearregion(0, term.bot-n+1, term.col-1, term.bot); tclearregion(0, term.bot-n+1, term.col-1, term.bot);
@@ -1071,17 +1121,28 @@ tscrolldown(int orig, int n)
term.line[i-n] = temp; term.line[i-n] = temp;
} }
selscroll(orig, n); if (term.scr == 0)
selscroll(orig, n);
} }
void void
tscrollup(int orig, int n) tscrollup(int orig, int n, int copyhist)
{ {
int i; int i;
Line temp; Line temp;
LIMIT(n, 0, term.bot-orig+1); LIMIT(n, 0, term.bot-orig+1);
if (copyhist) {
term.histi = (term.histi + 1) % HISTSIZE;
temp = term.hist[term.histi];
term.hist[term.histi] = term.line[orig];
term.line[orig] = temp;
}
if (term.scr > 0 && term.scr < HISTSIZE)
term.scr = MIN(term.scr + n, HISTSIZE-1);
tclearregion(0, orig, term.col-1, orig+n-1); tclearregion(0, orig, term.col-1, orig+n-1);
tsetdirt(orig+n, term.bot); tsetdirt(orig+n, term.bot);
@@ -1091,7 +1152,8 @@ tscrollup(int orig, int n)
term.line[i+n] = temp; term.line[i+n] = temp;
} }
selscroll(orig, -n); if (term.scr == 0)
selscroll(orig, -n);
} }
void void
@@ -1120,7 +1182,7 @@ tnewline(int first_col)
int y = term.c.y; int y = term.c.y;
if (y == term.bot) { if (y == term.bot) {
tscrollup(term.top, 1); tscrollup(term.top, 1, 1);
} else { } else {
y++; y++;
} }
@@ -1132,6 +1194,7 @@ csiparse(void)
{ {
char *p = csiescseq.buf, *np; char *p = csiescseq.buf, *np;
long int v; long int v;
int sep = ';'; /* colon or semi-colon, but not both */
csiescseq.narg = 0; csiescseq.narg = 0;
if (*p == '?') { if (*p == '?') {
@@ -1149,7 +1212,9 @@ csiparse(void)
v = -1; v = -1;
csiescseq.arg[csiescseq.narg++] = v; csiescseq.arg[csiescseq.narg++] = v;
p = np; p = np;
if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) if (sep == ';' && *p == ':')
sep = ':'; /* allow override to colon once */
if (*p != sep || csiescseq.narg == ESC_ARG_SIZ)
break; break;
p++; p++;
} }
@@ -1285,14 +1350,14 @@ void
tinsertblankline(int n) tinsertblankline(int n)
{ {
if (BETWEEN(term.c.y, term.top, term.bot)) if (BETWEEN(term.c.y, term.top, term.bot))
tscrolldown(term.c.y, n); tscrolldown(term.c.y, n, 0);
} }
void void
tdeleteline(int n) tdeleteline(int n)
{ {
if (BETWEEN(term.c.y, term.top, term.bot)) if (BETWEEN(term.c.y, term.top, term.bot))
tscrollup(term.c.y, n); tscrollup(term.c.y, n, 0);
} }
int32_t int32_t
@@ -1417,16 +1482,22 @@ tsetattr(const int *attr, int l)
if ((idx = tdefcolor(attr, &i, l)) >= 0) if ((idx = tdefcolor(attr, &i, l)) >= 0)
term.c.attr.fg = idx; term.c.attr.fg = idx;
break; break;
case 39: case 39: /* set foreground color to default */
term.c.attr.fg = defaultfg; term.c.attr.fg = defaultfg;
break; break;
case 48: case 48:
if ((idx = tdefcolor(attr, &i, l)) >= 0) if ((idx = tdefcolor(attr, &i, l)) >= 0)
term.c.attr.bg = idx; term.c.attr.bg = idx;
break; break;
case 49: case 49: /* set background color to default */
term.c.attr.bg = defaultbg; term.c.attr.bg = defaultbg;
break; break;
case 58:
/* This starts a sequence to change the color of
* "underline" pixels. We don't support that and
* instead eat up a following "5;n" or "2;r;g;b". */
tdefcolor(attr, &i, l);
break;
default: default:
if (BETWEEN(attr[i], 30, 37)) { if (BETWEEN(attr[i], 30, 37)) {
term.c.attr.fg = attr[i] - 30; term.c.attr.fg = attr[i] - 30;
@@ -1523,7 +1594,7 @@ tsetmode(int priv, int set, const int *args, int narg)
case 1006: /* 1006: extended reporting mode */ case 1006: /* 1006: extended reporting mode */
xsetmode(set, MODE_MOUSESGR); xsetmode(set, MODE_MOUSESGR);
break; break;
case 1034: case 1034: /* 1034: enable 8-bit mode for keyboard input */
xsetmode(set, MODE_8BIT); xsetmode(set, MODE_8BIT);
break; break;
case 1049: /* swap screen & set/restore cursor as xterm */ case 1049: /* swap screen & set/restore cursor as xterm */
@@ -1531,8 +1602,8 @@ tsetmode(int priv, int set, const int *args, int narg)
break; break;
tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
/* FALLTHROUGH */ /* FALLTHROUGH */
case 47: /* swap screen */ case 47: /* swap screen buffer */
case 1047: case 1047: /* swap screen buffer */
if (!allowaltscreen) if (!allowaltscreen)
break; break;
alt = IS_SET(MODE_ALTSCREEN); alt = IS_SET(MODE_ALTSCREEN);
@@ -1545,7 +1616,7 @@ tsetmode(int priv, int set, const int *args, int narg)
if (*args != 1049) if (*args != 1049)
break; break;
/* FALLTHROUGH */ /* FALLTHROUGH */
case 1048: case 1048: /* save/restore cursor (like DECSC/DECRC) */
tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
break; break;
case 2004: /* 2004: bracketed paste mode */ case 2004: /* 2004: bracketed paste mode */
@@ -1702,7 +1773,7 @@ csihandle(void)
} }
break; break;
case 1: /* above */ case 1: /* above */
if (term.c.y > 1) if (term.c.y > 0)
tclearregion(0, 0, term.col-1, term.c.y-1); tclearregion(0, 0, term.col-1, term.c.y-1);
tclearregion(0, term.c.y, term.c.x, term.c.y); tclearregion(0, term.c.y, term.c.x, term.c.y);
break; break;
@@ -1730,11 +1801,11 @@ csihandle(void)
case 'S': /* SU -- Scroll <n> line up */ case 'S': /* SU -- Scroll <n> line up */
if (csiescseq.priv) break; if (csiescseq.priv) break;
DEFAULT(csiescseq.arg[0], 1); DEFAULT(csiescseq.arg[0], 1);
tscrollup(term.top, csiescseq.arg[0]); tscrollup(term.top, csiescseq.arg[0], 0);
break; break;
case 'T': /* SD -- Scroll <n> line down */ case 'T': /* SD -- Scroll <n> line down */
DEFAULT(csiescseq.arg[0], 1); DEFAULT(csiescseq.arg[0], 1);
tscrolldown(term.top, csiescseq.arg[0]); tscrolldown(term.top, csiescseq.arg[0], 0);
break; break;
case 'L': /* IL -- Insert <n> blank lines */ case 'L': /* IL -- Insert <n> blank lines */
DEFAULT(csiescseq.arg[0], 1); DEFAULT(csiescseq.arg[0], 1);
@@ -1798,7 +1869,11 @@ csihandle(void)
tcursor(CURSOR_SAVE); tcursor(CURSOR_SAVE);
break; break;
case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */
tcursor(CURSOR_LOAD); if (csiescseq.priv) {
goto unknown;
} else {
tcursor(CURSOR_LOAD);
}
break; break;
case ' ': case ' ':
switch (csiescseq.mode[1]) { switch (csiescseq.mode[1]) {
@@ -1900,7 +1975,7 @@ strhandle(void)
if (narg > 1) if (narg > 1)
xsettitle(strescseq.args[1]); xsettitle(strescseq.args[1]);
return; return;
case 52: case 52: /* manipulate selection data */
if (narg > 2 && allowwindowops) { if (narg > 2 && allowwindowops) {
dec = base64dec(strescseq.args[2]); dec = base64dec(strescseq.args[2]);
if (dec) { if (dec) {
@@ -1911,9 +1986,9 @@ strhandle(void)
} }
} }
return; return;
case 10: case 10: /* set dynamic VT100 text foreground color */
case 11: case 11: /* set dynamic VT100 text background color */
case 12: case 12: /* set dynamic text cursor color */
if (narg < 2) if (narg < 2)
break; break;
p = strescseq.args[1]; p = strescseq.args[1];
@@ -1954,6 +2029,19 @@ strhandle(void)
tfulldirt(); tfulldirt();
} }
return; return;
case 110: /* reset dynamic VT100 text foreground color */
case 111: /* reset dynamic VT100 text background color */
case 112: /* reset dynamic text cursor color */
if (narg != 1)
break;
if ((j = par - 110) < 0 || j >= LEN(osc_table))
break; /* shouldn't be possible */
if (xsetcolorname(osc_table[j].idx, NULL)) {
fprintf(stderr, "erresc: %s color not found\n", osc_table[j].str);
} else {
tfulldirt();
}
return;
} }
break; break;
case 'k': /* old title set compatibility */ case 'k': /* old title set compatibility */
@@ -2306,7 +2394,7 @@ eschandle(uchar ascii)
return 0; return 0;
case 'D': /* IND -- Linefeed */ case 'D': /* IND -- Linefeed */
if (term.c.y == term.bot) { if (term.c.y == term.bot) {
tscrollup(term.top, 1); tscrollup(term.top, 1, 1);
} else { } else {
tmoveto(term.c.x, term.c.y+1); tmoveto(term.c.x, term.c.y+1);
} }
@@ -2319,7 +2407,7 @@ eschandle(uchar ascii)
break; break;
case 'M': /* RI -- Reverse index */ case 'M': /* RI -- Reverse index */
if (term.c.y == term.top) { if (term.c.y == term.top) {
tscrolldown(term.top, 1); tscrolldown(term.top, 1, 1);
} else { } else {
tmoveto(term.c.x, term.c.y-1); tmoveto(term.c.x, term.c.y-1);
} }
@@ -2542,7 +2630,7 @@ twrite(const char *buf, int buflen, int show_ctrl)
void void
tresize(int col, int row) tresize(int col, int row)
{ {
int i; 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 *bp; int *bp;
@@ -2579,6 +2667,14 @@ tresize(int col, int row)
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));
for (i = 0; i < HISTSIZE; i++) {
term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph));
for (j = mincol; j < col; j++) {
term.hist[i][j] = term.c.attr;
term.hist[i][j].u = ' ';
}
}
/* resize each row to new width, zero-pad if needed */ /* resize each row to new width, zero-pad if needed */
for (i = 0; i < minrow; i++) { for (i = 0; i < minrow; i++) {
term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
@@ -2637,7 +2733,7 @@ drawregion(int x1, int y1, int x2, int y2)
continue; continue;
term.dirty[y] = 0; term.dirty[y] = 0;
xdrawline(term.line[y], x1, y, x2); xdrawline(TLINE(y), x1, y, x2);
} }
} }
@@ -2658,8 +2754,9 @@ draw(void)
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 (term.scr == 0)
term.ocx, term.ocy, term.line[term.ocy][term.ocx]); xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
term.ocx = cx; term.ocx = cx;
term.ocy = term.c.y; term.ocy = term.c.y;
xfinishdraw(); xfinishdraw();

2
st.h
View File

@@ -81,6 +81,8 @@ void die(const char *, ...);
void redraw(void); void redraw(void);
void draw(void); void draw(void);
void kscrolldown(const Arg *);
void kscrollup(const Arg *);
void printscreen(const Arg *); void printscreen(const Arg *);
void printsel(const Arg *); void printsel(const Arg *);
void sendbreak(const Arg *); void sendbreak(const Arg *);

11
x.c
View File

@@ -1131,7 +1131,7 @@ xinit(int cols, int rows)
{ {
XGCValues gcvalues; XGCValues gcvalues;
Cursor cursor; Cursor cursor;
Window parent; Window parent, root;
pid_t thispid = getpid(); pid_t thispid = getpid();
XColor xmousefg, xmousebg; XColor xmousefg, xmousebg;
@@ -1168,16 +1168,19 @@ xinit(int cols, int rows)
| ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
xw.attrs.colormap = xw.cmap; xw.attrs.colormap = xw.cmap;
root = XRootWindow(xw.dpy, xw.scr);
if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0))))
parent = XRootWindow(xw.dpy, xw.scr); parent = root;
xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, xw.win = XCreateWindow(xw.dpy, root, xw.l, xw.t,
win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput,
xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
| CWEventMask | CWColormap, &xw.attrs); | CWEventMask | CWColormap, &xw.attrs);
if (parent != root)
XReparentWindow(xw.dpy, xw.win, parent, xw.l, xw.t);
memset(&gcvalues, 0, sizeof(gcvalues)); memset(&gcvalues, 0, sizeof(gcvalues));
gcvalues.graphics_exposures = False; gcvalues.graphics_exposures = False;
dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, dc.gc = XCreateGC(xw.dpy, xw.win, GCGraphicsExposures,
&gcvalues); &gcvalues);
xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
DefaultDepth(xw.dpy, xw.scr)); DefaultDepth(xw.dpy, xw.scr));