diff --git a/.Xdefaults b/.Xdefaults index b1aee67..b4322ee 100644 --- a/.Xdefaults +++ b/.Xdefaults @@ -1,9 +1,12 @@ -!! Transparency (0-255): -st.alpha: 240 +!! Transparency (0-1): +st.alpha: 0.92 !! Set a default font and font size as below: st.font: Monospace-11; +! st.termname: st-256color +! st.borderpx: 2 + /* !! gruvbox: */ /* *.color0: #1d2021 */ /* *.color1: #cc241d */ diff --git a/Makefile b/Makefile index 0b3cecd..a836907 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ config.h: $(CC) $(STCFLAGS) -c $< st.o: config.h st.h win.h -x.o: arg.h st.h win.h +x.o: arg.h config.h st.h win.h $(OBJ): config.h config.mk @@ -43,7 +43,9 @@ dist: clean install: st mkdir -p $(DESTDIR)$(PREFIX)/bin cp -f st $(DESTDIR)$(PREFIX)/bin + cp -f st-copyout $(DESTDIR)$(PREFIX)/bin chmod 755 $(DESTDIR)$(PREFIX)/bin/st + chmod 755 $(DESTDIR)$(PREFIX)/bin/st-copyout mkdir -p $(DESTDIR)$(MANPREFIX)/man1 sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1 chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 @@ -52,6 +54,7 @@ install: st uninstall: rm -f $(DESTDIR)$(PREFIX)/bin/st + rm -f $(DESTDIR)$(PREFIX)/bin/st-copyout rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1 .PHONY: all options clean dist install uninstall diff --git a/PKGBUILD b/PKGBUILD index 959a2e9..98a057d 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -2,9 +2,9 @@ pkgname=st-luke-git _pkgname=st -_pkgver=0.8.1 -pkgver=0.8.1.r1046.1cd0b79 +pkgver=0.8.2.r1062.2087ab9 pkgrel=1 +epoch=1 pkgdesc="Luke's simple (suckless) terminal with vim-bindings, transparency, xresources, etc. " url='https://github.com/LukeSmithxyz/st' arch=('i686' 'x86_64') @@ -21,12 +21,13 @@ conflicts=("${_pkgname}") pkgver() { cd "${_pkgname}" - printf "${_pkgver}.r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" + printf "%s.r%s.%s" "$(awk '/^VERSION =/ {print $3}' config.mk)" \ + "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" } prepare() { cd $srcdir/${_pkgname} - # skip terminfo which conflicts with nsurses + # skip terminfo which conflicts with ncurses sed -i '/tic /d' Makefile } @@ -40,4 +41,5 @@ package() { make PREFIX=/usr DESTDIR="${pkgdir}" install install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" install -Dm644 README.md "${pkgdir}/usr/share/doc/${pkgname}/README.md" + install -Dm644 .Xdefaults "${pkgdir}/usr/share/doc/${pkgname}/Xdefaults.example" } diff --git a/README.md b/README.md index 5afdbb1..ae4078e 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,43 @@ # Luke's build of st - the simple (suckless) terminal -The [suckless terminal (st)](https://st.suckless.org/) with some additional features: +The [suckless terminal (st)](https://st.suckless.org/) with some additional features that make it literally the best terminal emulator ever: + +## Unique features (using dmenu) + ++ **follow urls** by pressing `alt-l` ++ **copy urls** in the same way with `alt-y` ++ **copy the output of commands** with `alt-o` + +## Bindings for + ++ **scrollback** with `alt-↑/↓` or `alt-pageup/down` or `shift` while scrolling the mouse ++ OR **vim-bindings**: scroll up/down in history with `alt-k` and `alt-j`. Faster with `alt-u`/`alt-d`. ++ **zoom/change font size**: same bindings as above, but holding down shift as well. `alt-home` returns to default ++ **copy text** with `alt-c`, **paste** is `alt-v` or `shift-insert` + +## Pretty stuff + Compatibility with `Xresources` and `pywal` for dynamic colors. The `Xdefaults` file shows a usage example. + Default [gruvbox](https://github.com/morhetz/gruvbox) colors otherwise. -+ Transparency/alpha, which is also adjustable from `~/.Xresources`. ++ Transparency/alpha, which is also adjustable from your `Xresources`. + Default font is system "mono" at 16pt, meaning the font will match your system font. -+ Very useful keybinds including: - + Copy is alt-c, paste is alt-v or alt-p pastes from primary selection - + Alt-l feeds all urls on screen to dmenu, so they user can choose and - follow one (requires dmenu installed). - + Zoom in/out or increase font size with Alt+Shift+k/j or u/d for larger intervals. - + Hold alt and press either ↑/↓ or the vim keys k/j to move up/down in the terminal. - + Shift+Mouse wheel do the same. - + Alt-u and Alt-d scroll back/forward in history a page at a time. - + Alt-PageUp and Alt-PageDown will do the same. + +## Other st patches + + Vertcenter + Scrollback -+ updated to latest version 0.8.1 - -The following additional bindings were added before I forked this: - -+ Scroll through history -- Shift+PageUp/PageDown or Shift+Mouse wheel -+ Increase/decrease font size -- Shift+Alt+PageUp/PageDown -+ Return to default font size -- Alt+Home -+ Paste -- Shift+Insert ++ updated to latest version 0.8.2 ## Installation for newbs ``` -make +git clone https://github.com/LukeSmithxyz/st +cd st sudo make install ``` +Users of Arch-based distros can also install it from the AUR as [st-luke-git](https://aur.archlinux.org/packages/st-luke-git/). + Obviously, `make` is required to build. `fontconfig` is required for the default build, since it asks `fontconfig` for your system monospace font. It might be obvious, but `libX11` and `libXft` are required as well. Chances are, you have all of this installed already. On OpenBSD, be sure to edit `config.mk` first and remove `-lrt` from the `$LIBS` before compiling. @@ -47,13 +52,12 @@ For example, you can define your desired fonts, transparency or colors: ``` *.font: Liberation Mono:pixelsize=12:antialias=true:autohint=true; -*.alpha: 150 +*.alpha: 0.9 *.color0: #111 ... ``` -The `alpha` value (for transparency) goes from `0` (transparent) to `255` -(opaque). +The `alpha` value (for transparency) goes from `0` (transparent) to `1` (opaque). ### Colors diff --git a/config.h b/config.h index b622536..d691716 100644 --- a/config.h +++ b/config.h @@ -31,9 +31,9 @@ static float chscale = 1.0; /* * word delimiter string * - * More advanced example: " `'\"()[]{}" + * More advanced example: L" `'\"()[]{}" */ -char *worddelimiters = " "; +wchar_t *worddelimiters = L" "; /* selection timeouts (in milliseconds) */ static unsigned int doubleclicktimeout = 300; @@ -84,8 +84,9 @@ char *termname = "st-256color"; unsigned int tabspaces = 8; /* bg opacity */ -unsigned int alpha = 0xed; +float alpha = 0.92; +/* Terminal colors (16 first used in escape sequence) */ static const char *colorname[] = { "#282828", /* hard contrast: #1d2021 / soft contrast: #32302f */ "#cc241d", @@ -171,6 +172,7 @@ ResourcePref resources[] = { { "color15", STRING, &colorname[15] }, { "background", STRING, &colorname[256] }, { "foreground", STRING, &colorname[257] }, + { "cursorColor", STRING, &colorname[258] }, { "termname", STRING, &termname }, { "shell", STRING, &shell }, { "xfps", INTEGER, &xfps }, @@ -178,9 +180,10 @@ ResourcePref resources[] = { { "blinktimeout", INTEGER, &blinktimeout }, { "bellvolume", INTEGER, &bellvolume }, { "tabspaces", INTEGER, &tabspaces }, + { "borderpx", INTEGER, &borderpx }, { "cwscale", FLOAT, &cwscale }, { "chscale", FLOAT, &chscale }, - { "alpha", INTEGER, &alpha }, + { "alpha", FLOAT, &alpha }, }; /* @@ -195,6 +198,7 @@ static MouseShortcut mshortcuts[] = { /* Internal keyboard shortcuts. */ #define MODKEY Mod1Mask +#define TERMMOD (Mod1Mask|ShiftMask) MouseKey mkeys[] = { /* button mask function argument */ @@ -202,46 +206,54 @@ MouseKey mkeys[] = { { Button5, ShiftMask, kscrolldown, {.i = 1} }, { Button4, MODKEY, kscrollup, {.i = 1} }, { Button5, MODKEY, kscrolldown, {.i = 1} }, - { Button4, MODKEY|ShiftMask, zoom, {.f = +1} }, - { Button5, MODKEY|ShiftMask, zoom, {.f = -1} }, + { Button4, TERMMOD, zoom, {.f = +1} }, + { Button5, TERMMOD, zoom, {.f = -1} }, }; static char *openurlcmd[] = { "/bin/sh", "-c", - "grep -aEo '(http|https)://[a-zA-Z0-9./?=_-]*' | uniq | dmenu -l 10 | xargs -r xdg-open", + "sed 's/.*│//g' | tr -d '\n' | grep -aEo '((http|https)://|www\\.)[a-zA-Z0-9./&?=_-]*' | uniq | sed 's/^www./http:\\/\\/www\\./g' | dmenu -p 'Follow which url?' -l 10 | xargs -r xdg-open", "externalpipe", NULL }; +static char *copyurlcmd[] = { "/bin/sh", "-c", + "sed 's/.*│//g' | tr -d '\n' | grep -aEo '((http|https)://|www\\.)[a-zA-Z0-9./&?=_-]*' | uniq | sed 's/^www./http:\\/\\/www\\./g' | dmenu -p 'Copy which url?' -l 10 | tr -d '\n' | xclip -selection clipboard", + "externalpipe", NULL }; + +static char *copyoutput[] = { "/bin/sh", "-c", "st-copyout", "externalpipe", NULL }; + static Shortcut shortcuts[] = { /* mask keysym function argument */ { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, { ControlMask, XK_Print, toggleprinter, {.i = 0} }, { ShiftMask, XK_Print, printscreen, {.i = 0} }, { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, - { MODKEY|ShiftMask, XK_Prior, zoom, {.f = +1} }, - { MODKEY|ShiftMask, XK_Next, zoom, {.f = -1} }, - { MODKEY, XK_Home, zoomreset, {.f = 0} }, + { TERMMOD, XK_Prior, zoom, {.f = +1} }, + { TERMMOD, XK_Next, zoom, {.f = -1} }, + { MODKEY, XK_Home, zoomreset, {.f = 0} }, { ShiftMask, XK_Insert, clippaste, {.i = 0} }, { MODKEY, XK_c, clipcopy, {.i = 0} }, { MODKEY, XK_v, clippaste, {.i = 0} }, { MODKEY, XK_p, selpaste, {.i = 0} }, - { MODKEY, XK_Num_Lock, numlock, {.i = 0} }, + { MODKEY, XK_Num_Lock, numlock, {.i = 0} }, { MODKEY, XK_Control_L, iso14755, {.i = 0} }, { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, { MODKEY, XK_Page_Up, kscrollup, {.i = -1} }, { MODKEY, XK_Page_Down, kscrolldown, {.i = -1} }, - { MODKEY, XK_k, kscrollup, {.i = 1} }, - { MODKEY, XK_j, kscrolldown, {.i = 1} }, - { MODKEY, XK_Up, kscrollup, {.i = 1} }, - { MODKEY, XK_Down, kscrolldown, {.i = 1} }, - { MODKEY, XK_u, kscrollup, {.i = -1} }, - { MODKEY, XK_d, kscrolldown, {.i = -1} }, - { MODKEY|ShiftMask, XK_Up, zoom, {.f = +1} }, - { MODKEY|ShiftMask, XK_Down, zoom, {.f = -1} }, - { MODKEY|ShiftMask, XK_K, zoom, {.f = +1} }, - { MODKEY|ShiftMask, XK_J, zoom, {.f = -1} }, - { MODKEY|ShiftMask, XK_U, zoom, {.f = +2} }, - { MODKEY|ShiftMask, XK_D, zoom, {.f = -2} }, - { MODKEY, XK_l, externalpipe, { .v = openurlcmd } }, + { MODKEY, XK_k, kscrollup, {.i = 1} }, + { MODKEY, XK_j, kscrolldown, {.i = 1} }, + { MODKEY, XK_Up, kscrollup, {.i = 1} }, + { MODKEY, XK_Down, kscrolldown, {.i = 1} }, + { MODKEY, XK_u, kscrollup, {.i = -1} }, + { MODKEY, XK_d, kscrolldown, {.i = -1} }, + { TERMMOD, XK_Up, zoom, {.f = +1} }, + { TERMMOD, XK_Down, zoom, {.f = -1} }, + { TERMMOD, XK_K, zoom, {.f = +1} }, + { TERMMOD, XK_J, zoom, {.f = -1} }, + { TERMMOD, XK_U, zoom, {.f = +2} }, + { TERMMOD, XK_D, zoom, {.f = -2} }, + { MODKEY, XK_l, externalpipe, {.v = openurlcmd } }, + { MODKEY, XK_y, externalpipe, {.v = copyurlcmd } }, + { MODKEY, XK_o, externalpipe, {.v = copyoutput } }, }; /* diff --git a/config.mk b/config.mk index 3b00d7e..eba98c3 100644 --- a/config.mk +++ b/config.mk @@ -1,28 +1,35 @@ # st version -VERSION = 0.8.1 +VERSION = 0.8.2 # Customize below to fit your system # paths -PREFIX = /usr/local +PREFIX ?= /usr/local MANPREFIX = $(PREFIX)/share/man X11INC = /usr/X11R6/include X11LIB = /usr/X11R6/lib +PKG_CONFIG = pkg-config + # includes and libs INCS = -I$(X11INC) \ - `pkg-config --cflags fontconfig` \ - `pkg-config --cflags freetype2` -LIBS = -L${X11LIB} -lm -lrt -lX11 -lutil -lXft -lXrender \ - `pkg-config --libs fontconfig` \ - `pkg-config --libs freetype2` + `$(PKG_CONFIG) --cflags fontconfig` \ + `$(PKG_CONFIG) --cflags freetype2` +LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender \ + `$(PKG_CONFIG) --libs fontconfig` \ + `$(PKG_CONFIG) --libs freetype2` # flags -CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -STCFLAGS = $(INCS) $(CPPFLAGS) $(CFLAGS) +STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 +STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) STLDFLAGS = $(LIBS) $(LDFLAGS) +# OpenBSD: +#CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE +#LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ +# `pkg-config --libs fontconfig` \ +# `pkg-config --libs freetype2` + # compiler and linker # CC = c99 - diff --git a/st-copyout b/st-copyout new file mode 100755 index 0000000..e40fedd --- /dev/null +++ b/st-copyout @@ -0,0 +1,12 @@ +#!/bin/sh +# Using external pipe with st, give a dmenu prompt of recent commands, +# allowing the user to copy the output of one. +# xclip required for this script. +# By Jaywalker and Luke +tmpfile=$(mktemp /tmp/st-cmd-output.XXXXXX) +trap 'rm "$tmpfile"' 0 1 15 +sed -n "w $tmpfile" +ps1="$(grep "\S" "$tmpfile" | tail -n 1 | cut -d' ' -f1)" +chosen="$(grep -F "$ps1" "$tmpfile" | sed '$ d' | tac | dmenu -p "Copy which command's output?" -i -l 10 | sed 's/[^^]/[&]/g; s/\^/\\^/g')" +eps1="$(echo "$ps1" | sed 's/[^^]/[&]/g; s/\^/\\^/g')" +awk "/^$chosen$/{p=1;print;next} p&&/^$eps1/{p=0};p" "$tmpfile" | xclip -selection clipboard diff --git a/st.1 b/st.1 index 8cf4265..57c35f4 100644 --- a/st.1 +++ b/st.1 @@ -149,6 +149,14 @@ Paste/input primary selection. .B Alt-l Show dmenu menu of all URLs on screen and choose one to open. .TP +.B Alt-y +Show dmenu menu of all URLs on screen and choose one to copy. +.TP +.B Alt-o +Show dmenu menu of all recently run commands and copy the output of the chosen command to the clipboard. +.I xclip +required. +.TP .B Break Send a break in the serial line. Break key is obtained in PC keyboards @@ -166,7 +174,7 @@ Print the full screen to the Print the selection to the .I iofile. .TP -.B Ctrl-Shift-i +.B Alt-Ctrl Launch dmenu to enter a unicode codepoint and send the corresponding glyph to st. .SH CUSTOMIZATION diff --git a/st.c b/st.c index 9fd04bd..21c6739 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,7 +43,12 @@ #define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177') #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) -#define ISDELIM(u) (utf8strchr(worddelimiters, u) != NULL) +#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]) + +#define TLINE_HIST(y) ((y) <= HISTSIZE-term.row+2 ? term.hist[(y)] : term.line[(y-HISTSIZE+term.row-3)]) /* constants */ #define ISO14755CMD "dmenu -w \"$WINDOWID\" -p codepoint: 0 && TLINE_HIST(y)[i - 1].u == ' ') + --i; + + return i; +} + void selstart(int col, int row, int snap) { selclear(); sel.mode = SEL_EMPTY; sel.type = SEL_REGULAR; + sel.alt = IS_SET(MODE_ALTSCREEN); sel.snap = snap; sel.oe.x = sel.ob.x = col; sel.oe.y = sel.ob.y = row; @@ -477,13 +480,12 @@ selextend(int col, int row, int type, int done) oldsey = sel.ne.y; oldtype = sel.type; - sel.alt = IS_SET(MODE_ALTSCREEN); sel.oe.x = col; sel.oe.y = row; selnormalize(); sel.type = type; - if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type) + if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); sel.mode = done ? SEL_IDLE : SEL_READY; @@ -690,7 +692,7 @@ execsh(char *cmd, char **args) errno = 0; if ((pw = getpwuid(getuid())) == NULL) { if (errno) - die("getpwuid:%s\n", strerror(errno)); + die("getpwuid: %s\n", strerror(errno)); else die("who are you?\n"); } @@ -733,7 +735,7 @@ sigchld(int a) pid_t p; if ((p = waitpid(pid, &stat, WNOHANG)) < 0) - die("Waiting for pid %hd failed: %s\n", pid, strerror(errno)); + die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); if (pid != p) return; @@ -786,7 +788,8 @@ ttynew(char *line, char *cmd, char *out, char **args) if (line) { if ((cmdfd = open(line, O_RDWR)) < 0) - die("open line failed: %s\n", strerror(errno)); + die("open line '%s' failed: %s\n", + line, strerror(errno)); dup2(cmdfd, 0); stty(args); return cmdfd; @@ -798,7 +801,7 @@ ttynew(char *line, char *cmd, char *out, char **args) switch (pid = fork()) { case -1: - die("fork failed\n"); + die("fork failed: %s\n", strerror(errno)); break; case 0: close(iofd); @@ -810,9 +813,17 @@ ttynew(char *line, char *cmd, char *out, char **args) die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); close(s); close(m); +#ifdef __OpenBSD__ + if (pledge("stdio getpw proc exec", NULL) == -1) + die("pledge\n"); +#endif execsh(cmd, args); break; default: +#ifdef __OpenBSD__ + if (pledge("stdio rpath tty proc", NULL) == -1) + die("pledge\n"); +#endif close(s); cmdfd = m; signal(SIGCHLD, sigchld); @@ -831,7 +842,7 @@ ttyread(void) /* append read bytes to unprocessed bytes */ if ((ret = read(cmdfd, buf+buflen, LEN(buf)-buflen)) < 0) - die("Couldn't read from shell: %s\n", strerror(errno)); + die("couldn't read from shell: %s\n", strerror(errno)); buflen += ret; written = twrite(buf, buflen, 0); @@ -840,9 +851,6 @@ ttyread(void) if (buflen > 0) memmove(buf, buf + written, buflen); - if (term.scr > 0 && term.scr < HISTSIZE-1) - term.scr++; - return ret; } @@ -1096,7 +1104,6 @@ kscrollup(const Arg* a) } } - void tscrolldown(int orig, int n, int copyhist) { @@ -1139,6 +1146,9 @@ tscrollup(int orig, int n, int copyhist) 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); @@ -1627,6 +1637,7 @@ tsetmode(int priv, int set, int *args, int narg) case 1015: /* urxvt mangled mouse mode; incompatible and can be mistaken for other control codes. */ + break; default: fprintf(stderr, "erresc: unknown private set/reset mode %d\n", @@ -1898,7 +1909,7 @@ csireset(void) void strhandle(void) { - char *p = NULL; + char *p = NULL, *dec; int j, narg, par; term.esc &= ~(ESC_STR_END|ESC_STR); @@ -1916,8 +1927,6 @@ strhandle(void) return; case 52: if (narg > 2) { - char *dec; - dec = base64dec(strescseq.args[2]); if (dec) { xsetsel(dec); @@ -1935,7 +1944,10 @@ strhandle(void) case 104: /* color reset, here p = NULL */ j = (narg > 1) ? atoi(strescseq.args[1]) : -1; if (xsetcolorname(j, p)) { - fprintf(stderr, "erresc: invalid color %s\n", p); + if (par == 104 && narg <= 1) + return; /* color reset without parameter */ + fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", + j, p ? p : "(null)"); } else { /* * TODO if defaultbg color is changed, borders @@ -2063,16 +2075,19 @@ externalpipe(const Arg *arg) /* ignore sigpipe for now, in case child exists early */ oldsigpipe = signal(SIGPIPE, SIG_IGN); newline = 0; - for (n = 0; n < term.row; n++) { - bp = term.line[n]; - lastpos = MIN(tlinelen(n) + 1, term.col) - 1; + /* modify externalpipe patch to pipe history too */ + for (n = 0; n <= HISTSIZE + 2; n++) { + bp = TLINE_HIST(n); + lastpos = MIN(tlinehistlen(n) +1, term.col) - 1; if (lastpos < 0) break; + if (lastpos == 0) + continue; end = &bp[lastpos + 1]; for (; bp < end; ++bp) if (xwrite(to[1], buf, utf8encode(bp->u, buf)) < 0) break; - if ((newline = term.line[n][lastpos].mode & ATTR_WRAP)) + if ((newline = TLINE_HIST(n)[lastpos].mode & ATTR_WRAP)) continue; if (xwrite(to[1], "\n", 1) < 0) break; @@ -2462,7 +2477,6 @@ tputc(Rune u) goto check_control_code; } - if (IS_SET(MODE_SIXEL)) { /* TODO: implement sixel mode */ return; @@ -2647,7 +2661,7 @@ tresize(int col, int row) } } - /* resize each r w to new width, zero-pad if needed */ + /* 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)); @@ -2725,12 +2739,12 @@ draw(void) cx--; drawregion(0, 0, term.col, term.row); - if (term.scr == 0) { + 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(); + xximspot(term.ocx, term.ocy); } void diff --git a/st.h b/st.h index 01cad23..94ce5c3 100644 --- a/st.h +++ b/st.h @@ -3,9 +3,6 @@ #include #include -/* Arbitrary size */ -#define HISTSIZE 2000 - /* macros */ #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) < (b) ? (b) : (a)) @@ -22,8 +19,6 @@ #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) #define IS_TRUECOL(x) (1 << 24 & (x)) -#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - term.scr \ - + HISTSIZE + 1) % HISTSIZE] : term.line[(y) - term.scr]) enum glyph_attribute { ATTR_NULL = 0, @@ -94,6 +89,8 @@ void draw(void); void externalpipe(const Arg *); void iso14755(const Arg *); +void kscrolldown(const Arg *); +void kscrollup(const Arg *); void printscreen(const Arg *); void printsel(const Arg *); void sendbreak(const Arg *); @@ -124,18 +121,15 @@ void *xmalloc(size_t); void *xrealloc(void *, size_t); char *xstrdup(char *); -void kscrolldown(const Arg *); -void kscrollup(const Arg *); - /* config.h globals */ extern char *utmp; extern char *stty_args; extern char *vtiden; -extern char *worddelimiters; +extern wchar_t *worddelimiters; extern int allowaltscreen; extern char *termname; extern unsigned int tabspaces; -extern unsigned int alpha; extern unsigned int defaultfg; extern unsigned int defaultbg; +extern float alpha; extern MouseKey mkeys[]; diff --git a/win.h b/win.h index d277477..a6ef1b9 100644 --- a/win.h +++ b/win.h @@ -23,10 +23,6 @@ enum win_mode { |MODE_MOUSEMANY, }; -/* alpha */ -#define OPAQUE 0Xff -#define USE_ARGB (alpha != OPAQUE && opt_embed == NULL) - void xbell(void); void xclipcopy(void); void xdrawcursor(int, int, Glyph, int, int, Glyph); @@ -40,3 +36,4 @@ void xsetmode(int, unsigned int); void xsetpointermotion(int); void xsetsel(char *); int xstartdraw(void); +void xximspot(int, int); diff --git a/x.c b/x.c index da5084b..cc2df91 100644 --- a/x.c +++ b/x.c @@ -155,6 +155,9 @@ static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); static void xdrawglyph(Glyph, int, int); static void xclear(int, int, int, int); static int xgeommasktogravity(int); +static void ximopen(Display *); +static void ximinstantiate(Display *, XPointer, XPointer); +static void ximdestroy(XIM, XPointer, XPointer); static void xinit(int, int); static void cresize(int, int); static void xresize(int, int); @@ -239,12 +242,14 @@ typedef struct { } Fontcache; /* Fontcache is an array now. A new font will be appended to the array. */ -static Fontcache frc[16]; +static Fontcache *frc = NULL; static int frclen = 0; +static int frccap = 0; static char *usedfont = NULL; static double usedfontsize = 0; static double defaultfontsize = 0; +static char *opt_alpha = NULL; static char *opt_class = NULL; static char **opt_cmd = NULL; static char *opt_embed = NULL; @@ -653,7 +658,7 @@ setsel(char *str, Time t) if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) selclear(); - xclipcopy(); + clipcopy(NULL); } void @@ -671,7 +676,7 @@ brelease(XEvent *e) } if (e->xbutton.button == Button2) - selpaste(NULL); + clippaste(NULL); else if (e->xbutton.button == Button1) mousesel(e, 1); } @@ -771,17 +776,17 @@ xloadcols(void) for (i = 0; i < dc.collen; i++) if (!xloadcolor(i, NULL, &dc.col[i])) { if (colorname[i]) - die("Could not allocate color '%s'\n", colorname[i]); + die("could not allocate color '%s'\n", colorname[i]); else - die("Could not allocate color %d\n", i); + die("could not allocate color %d\n", i); } /* set alpha value of bg color */ - if (USE_ARGB) { - dc.col[defaultbg].color.alpha = (0xffff * alpha) / OPAQUE; - dc.col[defaultbg].pixel &= 0x00111111; - dc.col[defaultbg].pixel |= alpha << 24; - } + if (opt_alpha) + alpha = strtof(opt_alpha, NULL); + dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); + dc.col[defaultbg].pixel &= 0x00FFFFFF; + dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; loaded = 1; } @@ -793,7 +798,6 @@ xsetcolorname(int x, const char *name) if (!BETWEEN(x, 0, dc.collen)) return 1; - if (!xloadcolor(x, name, &ncolor)) return 1; @@ -803,17 +807,6 @@ xsetcolorname(int x, const char *name) return 0; } -void -xtermclear(int col1, int row1, int col2, int row2) -{ - XftDrawRect(xw.draw, - &dc.col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg], - borderpx + col1 * win.cw, - borderpx + row1 * win.ch, - (col2-col1+1) * win.cw, - (row2-row1+1) * win.ch); -} - /* * Absolute coordinates. */ @@ -918,7 +911,7 @@ xloadfont(Font *f, FcPattern *pattern) if ((XftPatternGetInteger(f->match->pattern, "slant", 0, &haveattr) != XftResultMatch) || haveattr < wantattr) { f->badslant = 1; - fputs("st: font slant does not match\n", stderr); + fputs("font slant does not match\n", stderr); } } @@ -927,7 +920,7 @@ xloadfont(Font *f, FcPattern *pattern) if ((XftPatternGetInteger(f->match->pattern, "weight", 0, &haveattr) != XftResultMatch) || haveattr != wantattr) { f->badweight = 1; - fputs("st: font weight does not match\n", stderr); + fputs("font weight does not match\n", stderr); } } @@ -955,14 +948,13 @@ xloadfonts(char *fontstr, double fontsize) FcPattern *pattern; double fontval; - if (fontstr[0] == '-') { + if (fontstr[0] == '-') pattern = XftXlfdParse(fontstr, False, False); - } else { + else pattern = FcNameParse((FcChar8 *)fontstr); - } if (!pattern) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); if (fontsize > 1) { FcPatternDel(pattern, FC_PIXEL_SIZE); @@ -988,7 +980,7 @@ xloadfonts(char *fontstr, double fontsize) } if (xloadfont(&dc.font, pattern)) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); if (usedfontsize < 0) { FcPatternGetDouble(dc.font.match->pattern, @@ -1006,17 +998,17 @@ xloadfonts(char *fontstr, double fontsize) FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); if (xloadfont(&dc.ifont, pattern)) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); FcPatternDel(pattern, FC_WEIGHT); FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); if (xloadfont(&dc.ibfont, pattern)) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); if (xloadfont(&dc.bfont, pattern)) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); FcPatternDestroy(pattern); } @@ -1043,6 +1035,43 @@ xunloadfonts(void) xunloadfont(&dc.ibfont); } +void +ximopen(Display *dpy) +{ + XIMCallback destroy = { .client_data = NULL, .callback = ximdestroy }; + + if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { + XSetLocaleModifiers("@im=local"); + if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { + XSetLocaleModifiers("@im="); + if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) + die("XOpenIM failed. Could not open input device.\n"); + } + } + if (XSetIMValues(xw.xim, XNDestroyCallback, &destroy, NULL) != NULL) + die("XSetIMValues failed. Could not set input method value.\n"); + xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, xw.win, XNFocusWindow, xw.win, NULL); + if (xw.xic == NULL) + die("XCreateIC failed. Could not obtain input method.\n"); +} + +void +ximinstantiate(Display *dpy, XPointer client, XPointer call) +{ + ximopen(dpy); + XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); +} + +void +ximdestroy(XIM xim, XPointer client, XPointer call) +{ + xw.xim = NULL; + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); +} + void xinit(int cols, int rows) { @@ -1051,56 +1080,31 @@ xinit(int cols, int rows) Window parent; pid_t thispid = getpid(); XColor xmousefg, xmousebg; + XWindowAttributes attr; + XVisualInfo vis; xw.scr = XDefaultScreen(xw.dpy); - xw.depth = (USE_ARGB) ? 32: XDefaultDepth(xw.dpy, xw.scr); - if (!USE_ARGB) - xw.vis = XDefaultVisual(xw.dpy, xw.scr); - else { - XVisualInfo *vis; - XRenderPictFormat *fmt; - int nvi; - int i; - XVisualInfo tpl = { - .screen = xw.scr, - .depth = 32, - .class = TrueColor - }; - - vis = XGetVisualInfo(xw.dpy, - VisualScreenMask | VisualDepthMask | VisualClassMask, - &tpl, &nvi); - xw.vis = NULL; - for (i = 0; i < nvi; i++) { - fmt = XRenderFindVisualFormat(xw.dpy, vis[i].visual); - if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) { - xw.vis = vis[i].visual; - break; - } - } - - XFree(vis); - - if (!xw.vis) { - fprintf(stderr, "Couldn't find ARGB visual.\n"); - exit(1); - } + if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) { + parent = XRootWindow(xw.dpy, xw.scr); + xw.depth = 32; + } else { + XGetWindowAttributes(xw.dpy, parent, &attr); + xw.depth = attr.depth; } + XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); + xw.vis = vis.visual; + /* font */ if (!FcInit()) - die("Could not init fontconfig.\n"); + die("could not init fontconfig.\n"); usedfont = (opt_font == NULL)? font : opt_font; xloadfonts(usedfont, 0); /* colors */ - if (!USE_ARGB) - xw.cmap = XDefaultColormap(xw.dpy, xw.scr); - else - xw.cmap = XCreateColormap(xw.dpy, XRootWindow(xw.dpy, xw.scr), - xw.vis, None); + xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); xloadcols(); /* adjust fixed window geometry */ @@ -1115,13 +1119,11 @@ xinit(int cols, int rows) xw.attrs.background_pixel = dc.col[defaultbg].pixel; xw.attrs.border_pixel = dc.col[defaultbg].pixel; xw.attrs.bit_gravity = NorthWestGravity; - xw.attrs.event_mask = FocusChangeMask | KeyPressMask + xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask | ExposureMask | VisibilityChangeMask | StructureNotifyMask | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; xw.attrs.colormap = xw.cmap; - if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) - parent = XRootWindow(xw.dpy, xw.scr); xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, win.w, win.h, 0, xw.depth, InputOutput, xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity @@ -1130,8 +1132,7 @@ xinit(int cols, int rows) memset(&gcvalues, 0, sizeof(gcvalues)); gcvalues.graphics_exposures = False; xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); - dc.gc = XCreateGC(xw.dpy, (USE_ARGB) ? xw.buf: parent, - GCGraphicsExposures, &gcvalues); + dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); @@ -1142,22 +1143,7 @@ xinit(int cols, int rows) xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); /* input methods */ - if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { - XSetLocaleModifiers("@im=local"); - if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { - XSetLocaleModifiers("@im="); - if ((xw.xim = XOpenIM(xw.dpy, - NULL, NULL, NULL)) == NULL) { - die("XOpenIM failed. Could not open input" - " device.\n"); - } - } - } - xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing - | XIMStatusNothing, XNClientWindow, xw.win, - XNFocusWindow, xw.win, NULL); - if (xw.xic == NULL) - die("XCreateIC failed. Could not obtain input method.\n"); + ximopen(xw.dpy); /* white cursor, black outline */ cursor = XCreateFontCursor(xw.dpy, mouseshape); @@ -1299,13 +1285,10 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x fontpattern = FcFontSetMatch(0, fcsets, 1, fcpattern, &fcres); - /* - * Overwrite or create the new cache entry. - */ - if (frclen >= LEN(frc)) { - frclen = LEN(frc) - 1; - XftFontClose(xw.dpy, frc[frclen].font); - frc[frclen].unicodep = 0; + /* Allocate memory for the new cache entry. */ + if (frclen >= frccap) { + frccap += 16; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); } frc[frclen].font = XftFontOpenPattern(xw.dpy, @@ -1377,10 +1360,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]; - if (IS_SET(MODE_REVERSE)) { if (fg == &dc.col[defaultfg]) { fg = &dc.col[defaultbg]; @@ -1635,6 +1614,16 @@ xfinishdraw(void) defaultfg : defaultbg].pixel); } +void +xximspot(int x, int y) +{ + XPoint spot = { borderpx + x * win.cw, borderpx + (y + 1) * win.ch }; + XVaNestedList attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL); + + XSetICValues(xw.xic, XNPreeditAttributes, attr, NULL); + XFree(attr); +} + void expose(XEvent *ev) { @@ -1812,7 +1801,6 @@ kpress(XEvent *ev) ttywrite(buf, len, 1); } - void cmessage(XEvent *e) { @@ -2028,6 +2016,9 @@ main(int argc, char *argv[]) case 'a': allowaltscreen = 0; break; + case 'A': + opt_alpha = EARGF(usage()); + break; case 'c': opt_class = EARGF(usage()); break; @@ -2062,7 +2053,7 @@ main(int argc, char *argv[]) opt_embed = EARGF(usage()); break; case 'v': - die("%s " VERSION " (c) 2010-2016 st engineers\n", argv0); + die("%s " VERSION "\n", argv0); break; default: usage();