1
0
mirror of https://github.com/tiyn/st synced 2025-10-14 12:01:15 +02:00

1 Commits

Author SHA1 Message Date
b141801a78 added externalpipe patch 2023-11-08 15:08:49 +01:00
16 changed files with 383 additions and 1840 deletions

2
.gitignore vendored
View File

@@ -50,5 +50,3 @@ modules.order
Module.symvers
Mkfile.old
dkms.conf
st

View File

@@ -4,7 +4,7 @@
include config.mk
SRC = st.c x.c hb.c
SRC = st.c x.c
OBJ = $(SRC:.c=.o)
all: options st
@@ -22,8 +22,7 @@ config.h:
$(CC) $(STCFLAGS) -c $<
st.o: config.h st.h win.h
x.o: arg.h config.h st.h win.h hb.h
hb.o: st.h
x.o: arg.h config.h st.h win.h
$(OBJ): config.h config.mk

View File

@@ -1,43 +0,0 @@
# st
This is my patched version of st. The base version is directly from suckless.org.
This belongs to my larbs installation script, but does not directly depend on it.
It is supposed to work in the environment after the larbs-base-installation.
## Patches
The list below shows the currently applied patches to the master branch.
- st-alpha-20220206-0.8.5.diff (adds transparency)
- st-anysize-20220718-baa9357.diff (st leaves no gaps if the height/width doesn't match a multiple of
character height)
- st-ligatures-scrollback-20230105-0.9.diff (scrollback compatible addition of ligatures)
- st-scrollback-0.8.5.diff (add scrollback functionality)
## Hotkeys
There are various shortcuts and hotkeys used in this version. Included in my build are the following.
| ModKey | Shift | Key | Function |
| ------ | ----- | --------------- | --------------- |
| Alt | | Break | Send break |
| Alt | | Print | Toggle printer |
| Alt | | Print | Print screen |
| | Shift | Insert | Clipboard paste |
| Alt | | c | Clipboard copy |
| Alt | | v | Clipboard paste |
| Alt | | p | Selected paste |
| Alt | | NumLock | Toggle Numlock |
| Alt | | k | Scroll up |
| Alt | | j | Scroll down |
| | | MouseScrollUp | Scroll up |
| | | MouseScrollDown | Scroll down |
| Alt | Shift | u | Zoom in |
| Alt | Shift | i | Zoom out |
## Installation
The most basic way is to clone the repository and then invoke make.
- `git clone https://github.com/tiyn/st`
- `make clean install`

View File

@@ -5,7 +5,7 @@
*
* font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
*/
static char *font = "mono:pixelsize=16:antialias=true:autohint=true";
static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true";
static int borderpx = 2;
/*
@@ -93,33 +93,35 @@ char *termname = "st-256color";
*/
unsigned int tabspaces = 8;
/* bg opacity */
float alpha = 1.0;
/* Terminal colors (16 first used in escape sequence) */
static const char *colorname[] = {
/* 8 normal colors */
"#191919",
"#f44747",
"#608b4e",
"#dcdcaa",
"#569cd6",
"#c678dd",
"#56b6c2",
"#d4d4d4",
"#808080",
"#f44747",
"#608b4e",
"#dcdcaa",
"#569cd6",
"#c678dd",
"#56b6c2",
"#d4d4d4",
"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 */
"#191919", /* 256 -> bg */
"#d4d4d4", /* 257 -> fg */
"#a4a4a4", /* 258 -> cursor */
"#cccccc",
"#555555",
"gray90", /* default foreground colour */
"black", /* default background colour */
};
@@ -127,10 +129,10 @@ static const char *colorname[] = {
* Default colors (colorname index)
* foreground, background, cursor, reverse cursor
*/
unsigned int defaultfg = 257;
unsigned int defaultbg = 256;
unsigned int defaultcs = 258;
static unsigned int defaultrcs = 0;
unsigned int defaultfg = 258;
unsigned int defaultbg = 259;
unsigned int defaultcs = 256;
static unsigned int defaultrcs = 257;
/*
* Default shape of cursor
@@ -174,10 +176,11 @@ static uint forcemousemod = ShiftMask;
*/
static MouseShortcut mshortcuts[] = {
/* mask button function argument release */
{ Button4, XK_NO_MOD, "\031" },
{ Button5, XK_NO_MOD, "\005" },
{ XK_NO_MOD, Button4, kscrollup, {.i = 1} },
{ XK_NO_MOD, Button5, kscrolldown,{.i = 1} },
{ 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. */
@@ -186,19 +189,18 @@ static MouseShortcut mshortcuts[] = {
static Shortcut shortcuts[] = {
/* mask keysym function argument */
{ MODKEY, XK_Break, sendbreak, {.i = 0} },
{ MODKEY, XK_Print, toggleprinter, {.i = 0} },
{ MODKEY, XK_Print, printscreen, {.i = 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_k, kscrollup, {.i = 1} },
{ MODKEY, XK_j, kscrolldown, {.i = 1} },
{ MODKEY, XK_U, zoom, {.f = +1} },
{ MODKEY|ShiftMask, XK_U, zoom, {.f = +1} },
{ MODKEY|ShiftMask, XK_I, zoom, {.f = -1} },
{ 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} },
};
/*

View File

@@ -15,12 +15,10 @@ PKG_CONFIG = pkg-config
# includes and libs
INCS = -I$(X11INC) \
`$(PKG_CONFIG) --cflags fontconfig` \
`$(PKG_CONFIG) --cflags freetype2` \
`$(PKG_CONFIG) --cflags harfbuzz`
LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
`$(PKG_CONFIG) --cflags freetype2`
LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
`$(PKG_CONFIG) --libs fontconfig` \
`$(PKG_CONFIG) --libs freetype2` \
`$(PKG_CONFIG) --libs harfbuzz`
`$(PKG_CONFIG) --libs freetype2`
# flags
STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600

124
hb.c
View File

@@ -1,124 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <X11/Xft/Xft.h>
#include <X11/cursorfont.h>
#include <hb.h>
#include <hb-ft.h>
#include "st.h"
#include "hb.h"
#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start = HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END }
#define BUFFER_STEP 256
hb_font_t *hbfindfont(XftFont *match);
typedef struct {
XftFont *match;
hb_font_t *font;
} HbFontMatch;
typedef struct {
size_t capacity;
HbFontMatch *fonts;
} HbFontCache;
static HbFontCache hbfontcache = { 0, NULL };
typedef struct {
size_t capacity;
Rune *runes;
} RuneBuffer;
static RuneBuffer hbrunebuffer = { 0, NULL };
/*
* Poplulate the array with a list of font features, wrapped in FEATURE macro,
* e. g.
* FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
*/
hb_feature_t features[] = { };
void
hbunloadfonts()
{
for (int i = 0; i < hbfontcache.capacity; i++) {
hb_font_destroy(hbfontcache.fonts[i].font);
XftUnlockFace(hbfontcache.fonts[i].match);
}
if (hbfontcache.fonts != NULL) {
free(hbfontcache.fonts);
hbfontcache.fonts = NULL;
}
hbfontcache.capacity = 0;
}
hb_font_t *
hbfindfont(XftFont *match)
{
for (int i = 0; i < hbfontcache.capacity; i++) {
if (hbfontcache.fonts[i].match == match)
return hbfontcache.fonts[i].font;
}
/* Font not found in cache, caching it now. */
hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * (hbfontcache.capacity + 1));
FT_Face face = XftLockFace(match);
hb_font_t *font = hb_ft_font_create(face, NULL);
if (font == NULL)
die("Failed to load Harfbuzz font.");
hbfontcache.fonts[hbfontcache.capacity].match = match;
hbfontcache.fonts[hbfontcache.capacity].font = font;
hbfontcache.capacity += 1;
return font;
}
void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, int start, int length) {
ushort mode = USHRT_MAX;
unsigned int glyph_count;
int rune_idx, glyph_idx, end = start + length;
hb_font_t *font = hbfindfont(xfont);
if (font == NULL)
return;
hb_buffer_t *buffer = hb_buffer_create();
hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
/* Resize the buffer if required length is larger. */
if (hbrunebuffer.capacity < length) {
hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_STEP;
hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer.capacity * sizeof(Rune));
}
/* Fill buffer with codepoints. */
for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, rune_idx++) {
hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
mode = glyphs[glyph_idx].mode;
if (mode & ATTR_WDUMMY)
hbrunebuffer.runes[rune_idx] = 0x0020;
}
hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, length);
/* Shape the segment. */
hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t));
/* Get new glyph info. */
hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count);
hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &glyph_count);
/* Fill the output. */
data->buffer = buffer;
data->glyphs = info;
data->positions = pos;
data->count = glyph_count;
}
void hbcleanup(HbTransformData *data) {
hb_buffer_destroy(data->buffer);
memset(data, 0, sizeof(HbTransformData));
}

14
hb.h
View File

@@ -1,14 +0,0 @@
#include <X11/Xft/Xft.h>
#include <hb.h>
#include <hb-ft.h>
typedef struct {
hb_buffer_t *buffer;
hb_glyph_info_t *glyphs;
hb_glyph_position_t *positions;
unsigned int count;
} HbTransformData;
void hbunloadfonts();
void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
void hbcleanup(HbTransformData *);

View File

@@ -1,146 +0,0 @@
diff --git a/config.def.h b/config.def.h
index 91ab8ca..6af616e 100644
--- a/config.def.h
+++ b/config.def.h
@@ -93,6 +93,9 @@ char *termname = "st-256color";
*/
unsigned int tabspaces = 8;
+/* bg opacity */
+float alpha = 0.8;
+
/* Terminal colors (16 first used in escape sequence) */
static const char *colorname[] = {
/* 8 normal colors */
diff --git a/config.mk b/config.mk
index 4c4c5d5..0114bad 100644
--- a/config.mk
+++ b/config.mk
@@ -16,7 +16,7 @@ PKG_CONFIG = pkg-config
INCS = -I$(X11INC) \
`$(PKG_CONFIG) --cflags fontconfig` \
`$(PKG_CONFIG) --cflags freetype2`
-LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
+LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
`$(PKG_CONFIG) --libs fontconfig` \
`$(PKG_CONFIG) --libs freetype2`
diff --git a/st.h b/st.h
index 519b9bd..8bb533d 100644
--- a/st.h
+++ b/st.h
@@ -126,3 +126,4 @@ extern unsigned int tabspaces;
extern unsigned int defaultfg;
extern unsigned int defaultbg;
extern unsigned int defaultcs;
+extern float alpha;
diff --git a/x.c b/x.c
index 8a16faa..ddf4178 100644
--- a/x.c
+++ b/x.c
@@ -105,6 +105,7 @@ typedef struct {
XSetWindowAttributes attrs;
int scr;
int isfixed; /* is fixed geometry? */
+ int depth; /* bit depth */
int l, t; /* left and top offset */
int gm; /* geometry mask */
} XWindow;
@@ -243,6 +244,7 @@ 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;
@@ -736,7 +738,7 @@ xresize(int col, int row)
XFreePixmap(xw.dpy, xw.buf);
xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
- DefaultDepth(xw.dpy, xw.scr));
+ xw.depth);
XftDrawChange(xw.draw, xw.buf);
xclear(0, 0, win.w, win.h);
@@ -796,6 +798,13 @@ xloadcols(void)
else
die("could not allocate color %d\n", i);
}
+
+ /* set alpha value of bg color */
+ 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;
}
@@ -1118,11 +1127,23 @@ xinit(int cols, int rows)
Window parent;
pid_t thispid = getpid();
XColor xmousefg, xmousebg;
+ XWindowAttributes attr;
+ XVisualInfo vis;
if (!(xw.dpy = XOpenDisplay(NULL)))
die("can't open display\n");
xw.scr = XDefaultScreen(xw.dpy);
- xw.vis = XDefaultVisual(xw.dpy, xw.scr);
+
+ 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())
@@ -1132,7 +1153,7 @@ xinit(int cols, int rows)
xloadfonts(usedfont, 0);
/* colors */
- xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
+ xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None);
xloadcols();
/* adjust fixed window geometry */
@@ -1152,19 +1173,15 @@ xinit(int cols, int rows)
| 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, XDefaultDepth(xw.dpy, xw.scr), InputOutput,
+ win.w, win.h, 0, xw.depth, InputOutput,
xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
| CWEventMask | CWColormap, &xw.attrs);
memset(&gcvalues, 0, sizeof(gcvalues));
gcvalues.graphics_exposures = False;
- dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures,
- &gcvalues);
- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
- DefaultDepth(xw.dpy, xw.scr));
+ xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth);
+ 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);
@@ -2019,6 +2036,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;

View File

@@ -1,164 +0,0 @@
From 8dcdc4b21a73268e167d98aa30f24315c7f3b7ff Mon Sep 17 00:00:00 2001
From: Bakkeby <bakkeby@gmail.com>
Date: Mon, 18 Jul 2022 16:52:03 +0200
Subject: [PATCH] Adding anysize patch
---
x.c | 56 ++++++++++++++++++++++++++++++--------------------------
1 file changed, 30 insertions(+), 26 deletions(-)
diff --git a/x.c b/x.c
index 2a3bd38..f534347 100644
--- a/x.c
+++ b/x.c
@@ -81,6 +81,7 @@ typedef XftGlyphFontSpec GlyphFontSpec;
typedef struct {
int tw, th; /* tty width and height */
int w, h; /* window width and height */
+ int hborderpx, vborderpx;
int ch; /* char height */
int cw; /* char width */
int mode; /* window state/mode flags */
@@ -331,7 +332,7 @@ ttysend(const Arg *arg)
int
evcol(XEvent *e)
{
- int x = e->xbutton.x - borderpx;
+ int x = e->xbutton.x - win.hborderpx;
LIMIT(x, 0, win.tw - 1);
return x / win.cw;
}
@@ -339,7 +340,7 @@ evcol(XEvent *e)
int
evrow(XEvent *e)
{
- int y = e->xbutton.y - borderpx;
+ int y = e->xbutton.y - win.vborderpx;
LIMIT(y, 0, win.th - 1);
return y / win.ch;
}
@@ -739,6 +740,9 @@ cresize(int width, int height)
col = MAX(1, col);
row = MAX(1, row);
+ win.hborderpx = (win.w - col * win.cw) / 2;
+ win.vborderpx = (win.h - row * win.ch) / 2;
+
tresize(col, row);
xresize(col, row);
ttyresize(win.tw, win.th);
@@ -869,8 +873,8 @@ xhints(void)
sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize;
sizeh->height = win.h;
sizeh->width = win.w;
- sizeh->height_inc = win.ch;
- sizeh->width_inc = win.cw;
+ sizeh->height_inc = 1;
+ sizeh->width_inc = 1;
sizeh->base_height = 2 * borderpx;
sizeh->base_width = 2 * borderpx;
sizeh->min_height = win.ch + 2 * borderpx;
@@ -1152,8 +1156,8 @@ xinit(int cols, int rows)
xloadcols();
/* adjust fixed window geometry */
- win.w = 2 * borderpx + cols * win.cw;
- win.h = 2 * borderpx + rows * win.ch;
+ win.w = 2 * win.hborderpx + 2 * borderpx + cols * win.cw;
+ win.h = 2 * win.vborderpx + 2 * borderpx + rows * win.ch;
if (xw.gm & XNegative)
xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2;
if (xw.gm & YNegative)
@@ -1242,7 +1246,7 @@ xinit(int cols, int rows)
int
xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
{
- float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
+ float winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch, xp, yp;
ushort mode, prevmode = USHRT_MAX;
Font *font = &dc.font;
int frcflags = FRC_NORMAL;
@@ -1375,7 +1379,7 @@ void
xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
{
int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
- int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
+ int winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch,
width = charlen * win.cw;
Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
XRenderColor colfg, colbg;
@@ -1465,17 +1469,17 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
/* Intelligent cleaning up of the borders. */
if (x == 0) {
- xclear(0, (y == 0)? 0 : winy, borderpx,
+ xclear(0, (y == 0)? 0 : winy, win.hborderpx,
winy + win.ch +
- ((winy + win.ch >= borderpx + win.th)? win.h : 0));
+ ((winy + win.ch >= win.vborderpx + win.th)? win.h : 0));
}
- if (winx + width >= borderpx + win.tw) {
+ if (winx + width >= win.hborderpx + win.tw) {
xclear(winx + width, (y == 0)? 0 : winy, win.w,
- ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch)));
+ ((winy + win.ch >= win.vborderpx + win.th)? win.h : (winy + win.ch)));
}
if (y == 0)
- xclear(winx, 0, winx + width, borderpx);
- if (winy + win.ch >= borderpx + win.th)
+ xclear(winx, 0, winx + width, win.vborderpx);
+ if (winy + win.ch >= win.vborderpx + win.th)
xclear(winx, winy + win.ch, winx + width, win.h);
/* Clean up the region we want to draw to. */
@@ -1569,35 +1573,35 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
case 3: /* Blinking Underline */
case 4: /* Steady Underline */
XftDrawRect(xw.draw, &drawcol,
- borderpx + cx * win.cw,
- borderpx + (cy + 1) * win.ch - \
+ win.hborderpx + cx * win.cw,
+ win.vborderpx + (cy + 1) * win.ch - \
cursorthickness,
win.cw, cursorthickness);
break;
case 5: /* Blinking bar */
case 6: /* Steady bar */
XftDrawRect(xw.draw, &drawcol,
- borderpx + cx * win.cw,
- borderpx + cy * win.ch,
+ win.hborderpx + cx * win.cw,
+ win.vborderpx + cy * win.ch,
cursorthickness, win.ch);
break;
}
} else {
XftDrawRect(xw.draw, &drawcol,
- borderpx + cx * win.cw,
- borderpx + cy * win.ch,
+ win.hborderpx + cx * win.cw,
+ win.vborderpx + cy * win.ch,
win.cw - 1, 1);
XftDrawRect(xw.draw, &drawcol,
- borderpx + cx * win.cw,
- borderpx + cy * win.ch,
+ win.hborderpx + cx * win.cw,
+ win.vborderpx + cy * win.ch,
1, win.ch - 1);
XftDrawRect(xw.draw, &drawcol,
- borderpx + (cx + 1) * win.cw - 1,
- borderpx + cy * win.ch,
+ win.hborderpx + (cx + 1) * win.cw - 1,
+ win.vborderpx + cy * win.ch,
1, win.ch - 1);
XftDrawRect(xw.draw, &drawcol,
- borderpx + cx * win.cw,
- borderpx + (cy + 1) * win.ch - 1,
+ win.hborderpx + cx * win.cw,
+ win.vborderpx + (cy + 1) * win.ch - 1,
win.cw, 1);
}
}
--
2.37.1

View File

@@ -0,0 +1,92 @@
diff --git a/st.c b/st.c
index 76b7e0d..0e9a614 100644
--- a/st.c
+++ b/st.c
@@ -723,8 +723,14 @@ sigchld(int a)
if ((p = waitpid(pid, &stat, WNOHANG)) < 0)
die("waiting for pid %hd failed: %s\n", pid, strerror(errno));
- if (pid != p)
+ if (pid != p) {
+ if (p == 0 && wait(&stat) < 0)
+ die("wait: %s\n", strerror(errno));
+
+ /* reinstall sigchld handler */
+ signal(SIGCHLD, sigchld);
return;
+ }
if (WIFEXITED(stat) && WEXITSTATUS(stat))
die("child exited with status %d\n", WEXITSTATUS(stat));
@@ -1926,6 +1932,59 @@ strparse(void)
}
}
+void
+externalpipe(const Arg *arg)
+{
+ int to[2];
+ char buf[UTF_SIZ];
+ void (*oldsigpipe)(int);
+ Glyph *bp, *end;
+ int lastpos, n, newline;
+
+ if (pipe(to) == -1)
+ return;
+
+ switch (fork()) {
+ case -1:
+ close(to[0]);
+ close(to[1]);
+ return;
+ case 0:
+ dup2(to[0], STDIN_FILENO);
+ close(to[0]);
+ close(to[1]);
+ execvp(((char **)arg->v)[0], (char **)arg->v);
+ fprintf(stderr, "st: execvp %s\n", ((char **)arg->v)[0]);
+ perror("failed");
+ exit(0);
+ }
+
+ close(to[0]);
+ /* 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;
+ if (lastpos < 0)
+ break;
+ end = &bp[lastpos + 1];
+ for (; bp < end; ++bp)
+ if (xwrite(to[1], buf, utf8encode(bp->u, buf)) < 0)
+ break;
+ if ((newline = term.line[n][lastpos].mode & ATTR_WRAP))
+ continue;
+ if (xwrite(to[1], "\n", 1) < 0)
+ break;
+ newline = 0;
+ }
+ if (newline)
+ (void)xwrite(to[1], "\n", 1);
+ close(to[1]);
+ /* restore */
+ signal(SIGPIPE, oldsigpipe);
+}
+
void
strdump(void)
{
diff --git a/st.h b/st.h
index 3d351b6..392b64e 100644
--- a/st.h
+++ b/st.h
@@ -81,6 +81,7 @@ void die(const char *, ...);
void redraw(void);
void draw(void);
+void externalpipe(const Arg *);
void printscreen(const Arg *);
void printsel(const Arg *);
void sendbreak(const Arg *);

View File

@@ -1,611 +0,0 @@
diff --git a/Makefile b/Makefile
index 470ac86..38240da 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
include config.mk
-SRC = st.c x.c
+SRC = st.c x.c hb.c
OBJ = $(SRC:.c=.o)
all: options st
@@ -22,7 +22,8 @@ config.h:
$(CC) $(STCFLAGS) -c $<
st.o: config.h st.h win.h
-x.o: arg.h config.h st.h win.h
+x.o: arg.h config.h st.h win.h hb.h
+hb.o: st.h
$(OBJ): config.h config.mk
diff --git a/config.mk b/config.mk
index 1e306f8..3e13e53 100644
--- a/config.mk
+++ b/config.mk
@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
# includes and libs
INCS = -I$(X11INC) \
`$(PKG_CONFIG) --cflags fontconfig` \
- `$(PKG_CONFIG) --cflags freetype2`
+ `$(PKG_CONFIG) --cflags freetype2` \
+ `$(PKG_CONFIG) --cflags harfbuzz`
LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
`$(PKG_CONFIG) --libs fontconfig` \
- `$(PKG_CONFIG) --libs freetype2`
+ `$(PKG_CONFIG) --libs freetype2` \
+ `$(PKG_CONFIG) --libs harfbuzz`
# flags
STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
diff --git a/hb.c b/hb.c
new file mode 100644
index 0000000..528c040
--- /dev/null
+++ b/hb.c
@@ -0,0 +1,124 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <X11/Xft/Xft.h>
+#include <X11/cursorfont.h>
+#include <hb.h>
+#include <hb-ft.h>
+
+#include "st.h"
+#include "hb.h"
+
+#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start = HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END }
+#define BUFFER_STEP 256
+
+hb_font_t *hbfindfont(XftFont *match);
+
+typedef struct {
+ XftFont *match;
+ hb_font_t *font;
+} HbFontMatch;
+
+typedef struct {
+ size_t capacity;
+ HbFontMatch *fonts;
+} HbFontCache;
+
+static HbFontCache hbfontcache = { 0, NULL };
+
+typedef struct {
+ size_t capacity;
+ Rune *runes;
+} RuneBuffer;
+
+static RuneBuffer hbrunebuffer = { 0, NULL };
+
+/*
+ * Poplulate the array with a list of font features, wrapped in FEATURE macro,
+ * e. g.
+ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
+ */
+hb_feature_t features[] = { };
+
+void
+hbunloadfonts()
+{
+ for (int i = 0; i < hbfontcache.capacity; i++) {
+ hb_font_destroy(hbfontcache.fonts[i].font);
+ XftUnlockFace(hbfontcache.fonts[i].match);
+ }
+
+ if (hbfontcache.fonts != NULL) {
+ free(hbfontcache.fonts);
+ hbfontcache.fonts = NULL;
+ }
+ hbfontcache.capacity = 0;
+}
+
+hb_font_t *
+hbfindfont(XftFont *match)
+{
+ for (int i = 0; i < hbfontcache.capacity; i++) {
+ if (hbfontcache.fonts[i].match == match)
+ return hbfontcache.fonts[i].font;
+ }
+
+ /* Font not found in cache, caching it now. */
+ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * (hbfontcache.capacity + 1));
+ FT_Face face = XftLockFace(match);
+ hb_font_t *font = hb_ft_font_create(face, NULL);
+ if (font == NULL)
+ die("Failed to load Harfbuzz font.");
+
+ hbfontcache.fonts[hbfontcache.capacity].match = match;
+ hbfontcache.fonts[hbfontcache.capacity].font = font;
+ hbfontcache.capacity += 1;
+
+ return font;
+}
+
+void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, int start, int length) {
+ ushort mode = USHRT_MAX;
+ unsigned int glyph_count;
+ int rune_idx, glyph_idx, end = start + length;
+
+ hb_font_t *font = hbfindfont(xfont);
+ if (font == NULL)
+ return;
+
+ hb_buffer_t *buffer = hb_buffer_create();
+ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
+
+ /* Resize the buffer if required length is larger. */
+ if (hbrunebuffer.capacity < length) {
+ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_STEP;
+ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer.capacity * sizeof(Rune));
+ }
+
+ /* Fill buffer with codepoints. */
+ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, rune_idx++) {
+ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
+ mode = glyphs[glyph_idx].mode;
+ if (mode & ATTR_WDUMMY)
+ hbrunebuffer.runes[rune_idx] = 0x0020;
+ }
+ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, length);
+
+ /* Shape the segment. */
+ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t));
+
+ /* Get new glyph info. */
+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count);
+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &glyph_count);
+
+ /* Fill the output. */
+ data->buffer = buffer;
+ data->glyphs = info;
+ data->positions = pos;
+ data->count = glyph_count;
+}
+
+void hbcleanup(HbTransformData *data) {
+ hb_buffer_destroy(data->buffer);
+ memset(data, 0, sizeof(HbTransformData));
+}
diff --git a/hb.h b/hb.h
new file mode 100644
index 0000000..88de9bd
--- /dev/null
+++ b/hb.h
@@ -0,0 +1,14 @@
+#include <X11/Xft/Xft.h>
+#include <hb.h>
+#include <hb-ft.h>
+
+typedef struct {
+ hb_buffer_t *buffer;
+ hb_glyph_info_t *glyphs;
+ hb_glyph_position_t *positions;
+ unsigned int count;
+} HbTransformData;
+
+void hbunloadfonts();
+void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
+void hbcleanup(HbTransformData *);
diff --git a/st.c b/st.c
index 79ee9ba..7675db6 100644
--- a/st.c
+++ b/st.c
@@ -2711,7 +2711,8 @@ draw(void)
drawregion(0, 0, term.col, term.row);
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, term.ocy, term.line[term.ocy][term.ocx],
+ term.line[term.ocy], term.col);
term.ocx = cx;
term.ocy = term.c.y;
xfinishdraw();
diff --git a/st.h b/st.h
index 818a6f8..4e584b6 100644
--- a/st.h
+++ b/st.h
@@ -11,7 +11,8 @@
#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
#define DEFAULT(a, b) (a) = (a) ? (a) : (b)
#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \
+#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode & (~ATTR_WRAP)) || \
+ (a).fg != (b).fg || \
(a).bg != (b).bg)
#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
(t1.tv_nsec-t2.tv_nsec)/1E6)
diff --git a/win.h b/win.h
index 6de960d..94679e4 100644
--- a/win.h
+++ b/win.h
@@ -25,7 +25,7 @@ enum win_mode {
void xbell(void);
void xclipcopy(void);
-void xdrawcursor(int, int, Glyph, int, int, Glyph);
+void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
void xdrawline(Line, int, int, int);
void xfinishdraw(void);
void xloadcols(void);
diff --git a/x.c b/x.c
index 2a3bd38..e66cf0c 100644
--- a/x.c
+++ b/x.c
@@ -19,6 +19,7 @@ char *argv0;
#include "arg.h"
#include "st.h"
#include "win.h"
+#include "hb.h"
/* types used in config.h */
typedef struct {
@@ -141,6 +142,7 @@ typedef struct {
} DC;
static inline ushort sixd_to_16bit(int);
+static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
static void xdrawglyph(Glyph, int, int);
@@ -757,7 +759,7 @@ xresize(int col, int row)
xclear(0, 0, win.w, win.h);
/* resize to new width */
- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
+ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
}
ushort
@@ -1062,6 +1064,9 @@ xunloadfont(Font *f)
void
xunloadfonts(void)
{
+ /* Clear Harfbuzz font cache. */
+ hbunloadfonts();
+
/* Free the loaded fonts in the font cache. */
while (frclen > 0)
XftFontClose(xw.dpy, frc[--frclen].font);
@@ -1185,7 +1190,7 @@ xinit(int cols, int rows)
XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
/* font spec buffer */
- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
+ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
/* Xft rendering context */
xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
@@ -1239,6 +1244,22 @@ xinit(int cols, int rows)
xsel.xtarget = XA_STRING;
}
+void
+xresetfontsettings(ushort mode, Font **font, int *frcflags)
+{
+ *font = &dc.font;
+ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
+ *font = &dc.ibfont;
+ *frcflags = FRC_ITALICBOLD;
+ } else if (mode & ATTR_ITALIC) {
+ *font = &dc.ifont;
+ *frcflags = FRC_ITALIC;
+ } else if (mode & ATTR_BOLD) {
+ *font = &dc.bfont;
+ *frcflags = FRC_BOLD;
+ }
+}
+
int
xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
{
@@ -1253,121 +1274,151 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
FcPattern *fcpattern, *fontpattern;
FcFontSet *fcsets[] = { NULL };
FcCharSet *fccharset;
- int i, f, numspecs = 0;
+ int i, f, length = 0, start = 0, numspecs = 0;
+ float cluster_xp = xp, cluster_yp = yp;
+ HbTransformData shaped = { 0 };
+
+ /* Initial values. */
+ mode = prevmode = glyphs[0].mode;
+ xresetfontsettings(mode, &font, &frcflags);
for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
- /* Fetch rune and mode for current glyph. */
- rune = glyphs[i].u;
mode = glyphs[i].mode;
/* Skip dummy wide-character spacing. */
- if (mode == ATTR_WDUMMY)
+ if (mode & ATTR_WDUMMY && i < (len - 1))
continue;
- /* Determine font for glyph if different from previous glyph. */
- if (prevmode != mode) {
- prevmode = mode;
- font = &dc.font;
- frcflags = FRC_NORMAL;
- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f);
- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
- font = &dc.ibfont;
- frcflags = FRC_ITALICBOLD;
- } else if (mode & ATTR_ITALIC) {
- font = &dc.ifont;
- frcflags = FRC_ITALIC;
- } else if (mode & ATTR_BOLD) {
- font = &dc.bfont;
- frcflags = FRC_BOLD;
+ if (
+ prevmode != mode
+ || ATTRCMP(glyphs[start], glyphs[i])
+ || selected(x + i, y) != selected(x + start, y)
+ || i == (len - 1)
+ ) {
+ /* Handle 1-character wide segments and end of line */
+ length = i - start;
+ if (i == start) {
+ length = 1;
+ } else if (i == (len - 1)) {
+ length = (i - start + 1);
}
- yp = winy + font->ascent;
- }
-
- /* Lookup character index with default font. */
- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
- if (glyphidx) {
- specs[numspecs].font = font->match;
- specs[numspecs].glyph = glyphidx;
- specs[numspecs].x = (short)xp;
- specs[numspecs].y = (short)yp;
- xp += runewidth;
- numspecs++;
- continue;
- }
- /* Fallback on font cache, search the font cache for match. */
- for (f = 0; f < frclen; f++) {
- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
- /* Everything correct. */
- if (glyphidx && frc[f].flags == frcflags)
- break;
- /* We got a default font for a not found glyph. */
- if (!glyphidx && frc[f].flags == frcflags
- && frc[f].unicodep == rune) {
- break;
+ /* Shape the segment. */
+ hbtransform(&shaped, font->match, glyphs, start, length);
+ runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE) ? 2.0f : 1.0f);
+ cluster_xp = xp; cluster_yp = yp;
+ for (int code_idx = 0; code_idx < shaped.count; code_idx++) {
+ int idx = shaped.glyphs[code_idx].cluster;
+
+ if (glyphs[start + idx].mode & ATTR_WDUMMY)
+ continue;
+
+ /* Advance the drawing cursor if we've moved to a new cluster */
+ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
+ xp += runewidth;
+ cluster_xp = xp;
+ cluster_yp = yp;
+ runewidth = win.cw * ((glyphs[start + idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
+ }
+
+ if (shaped.glyphs[code_idx].codepoint != 0) {
+ /* If symbol is found, put it into the specs. */
+ specs[numspecs].font = font->match;
+ specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint;
+ specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
+ specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
+ cluster_xp += shaped.positions[code_idx].x_advance / 64.;
+ cluster_yp += shaped.positions[code_idx].y_advance / 64.;
+ numspecs++;
+ } else {
+ /* If it's not found, try to fetch it through the font cache. */
+ rune = glyphs[start + idx].u;
+ for (f = 0; f < frclen; f++) {
+ glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
+ /* Everything correct. */
+ if (glyphidx && frc[f].flags == frcflags)
+ break;
+ /* We got a default font for a not found glyph. */
+ if (!glyphidx && frc[f].flags == frcflags
+ && frc[f].unicodep == rune) {
+ break;
+ }
+ }
+
+ /* Nothing was found. Use fontconfig to find matching font. */
+ if (f >= frclen) {
+ if (!font->set)
+ font->set = FcFontSort(0, font->pattern,
+ 1, 0, &fcres);
+ fcsets[0] = font->set;
+
+ /*
+ * Nothing was found in the cache. Now use
+ * some dozen of Fontconfig calls to get the
+ * font for one single character.
+ *
+ * Xft and fontconfig are design failures.
+ */
+ fcpattern = FcPatternDuplicate(font->pattern);
+ fccharset = FcCharSetCreate();
+
+ FcCharSetAddChar(fccharset, rune);
+ FcPatternAddCharSet(fcpattern, FC_CHARSET,
+ fccharset);
+ FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
+
+ FcConfigSubstitute(0, fcpattern,
+ FcMatchPattern);
+ FcDefaultSubstitute(fcpattern);
+
+ fontpattern = FcFontSetMatch(0, fcsets, 1,
+ fcpattern, &fcres);
+
+ /* Allocate memory for the new cache entry. */
+ if (frclen >= frccap) {
+ frccap += 16;
+ frc = xrealloc(frc, frccap * sizeof(Fontcache));
+ }
+
+ frc[frclen].font = XftFontOpenPattern(xw.dpy,
+ fontpattern);
+ if (!frc[frclen].font)
+ die("XftFontOpenPattern failed seeking fallback font: %s\n",
+ strerror(errno));
+ frc[frclen].flags = frcflags;
+ frc[frclen].unicodep = rune;
+
+ glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
+
+ f = frclen;
+ frclen++;
+
+ FcPatternDestroy(fcpattern);
+ FcCharSetDestroy(fccharset);
+ }
+
+ specs[numspecs].font = frc[f].font;
+ specs[numspecs].glyph = glyphidx;
+ specs[numspecs].x = (short)xp;
+ specs[numspecs].y = (short)yp;
+ numspecs++;
+ }
}
- }
-
- /* Nothing was found. Use fontconfig to find matching font. */
- if (f >= frclen) {
- if (!font->set)
- font->set = FcFontSort(0, font->pattern,
- 1, 0, &fcres);
- fcsets[0] = font->set;
- /*
- * Nothing was found in the cache. Now use
- * some dozen of Fontconfig calls to get the
- * font for one single character.
- *
- * Xft and fontconfig are design failures.
- */
- fcpattern = FcPatternDuplicate(font->pattern);
- fccharset = FcCharSetCreate();
-
- FcCharSetAddChar(fccharset, rune);
- FcPatternAddCharSet(fcpattern, FC_CHARSET,
- fccharset);
- FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
+ /* Cleanup and get ready for next segment. */
+ hbcleanup(&shaped);
+ start = i;
- FcConfigSubstitute(0, fcpattern,
- FcMatchPattern);
- FcDefaultSubstitute(fcpattern);
-
- fontpattern = FcFontSetMatch(0, fcsets, 1,
- fcpattern, &fcres);
-
- /* Allocate memory for the new cache entry. */
- if (frclen >= frccap) {
- frccap += 16;
- frc = xrealloc(frc, frccap * sizeof(Fontcache));
+ /* Determine font for glyph if different from previous glyph. */
+ if (prevmode != mode) {
+ prevmode = mode;
+ xresetfontsettings(mode, &font, &frcflags);
+ yp = winy + font->ascent;
}
-
- frc[frclen].font = XftFontOpenPattern(xw.dpy,
- fontpattern);
- if (!frc[frclen].font)
- die("XftFontOpenPattern failed seeking fallback font: %s\n",
- strerror(errno));
- frc[frclen].flags = frcflags;
- frc[frclen].unicodep = rune;
-
- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
-
- f = frclen;
- frclen++;
-
- FcPatternDestroy(fcpattern);
- FcCharSetDestroy(fccharset);
}
-
- specs[numspecs].font = frc[f].font;
- specs[numspecs].glyph = glyphidx;
- specs[numspecs].x = (short)xp;
- specs[numspecs].y = (short)yp;
- xp += runewidth;
- numspecs++;
}
+ hbcleanup(&shaped);
return numspecs;
}
@@ -1517,14 +1568,17 @@ xdrawglyph(Glyph g, int x, int y)
}
void
-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len)
{
Color drawcol;
/* remove the old cursor */
if (selected(ox, oy))
og.mode ^= ATTR_REVERSE;
- xdrawglyph(og, ox, oy);
+
+ /* Redraw the line where cursor was previously.
+ * It will restore the ligatures broken by the cursor. */
+ xdrawline(line, 0, oy, len);
if (IS_SET(MODE_HIDE))
return;
@@ -1652,18 +1706,16 @@ xdrawline(Line line, int x1, int y1, int x2)
Glyph base, new;
XftGlyphFontSpec *specs = xw.specbuf;
- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
i = ox = 0;
- for (x = x1; x < x2 && i < numspecs; x++) {
+ for (x = x1; x < x2; x++) {
new = line[x];
if (new.mode == ATTR_WDUMMY)
continue;
if (selected(x, y1))
new.mode ^= ATTR_REVERSE;
- if (i > 0 && ATTRCMP(base, new)) {
- xdrawglyphfontspecs(specs, base, i, ox, y1);
- specs += i;
- numspecs -= i;
+ if ((i > 0) && ATTRCMP(base, new)) {
+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - ox, ox, y1);
+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
i = 0;
}
if (i == 0) {
@@ -1672,8 +1724,10 @@ xdrawline(Line line, int x1, int y1, int x2)
}
i++;
}
- if (i > 0)
- xdrawglyphfontspecs(specs, base, i, ox, y1);
+ if (i > 0) {
+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1);
+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
+ }
}
void

View File

@@ -1,350 +0,0 @@
diff --git a/config.def.h b/config.def.h
index 91ab8ca..e3b469b 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 51049ba..cd750f2 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 */
@@ -184,8 +191,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);
@@ -416,10 +423,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;
@@ -528,7 +535,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;
@@ -543,14 +550,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)))
@@ -571,14 +578,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;
}
@@ -609,13 +616,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;
@@ -851,6 +858,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);
@@ -1062,12 +1072,52 @@ 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);
@@ -1078,17 +1128,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);
@@ -1098,7 +1159,8 @@ tscrollup(int orig, int n)
term.line[i+n] = temp;
}
- selscroll(orig, -n);
+ if (term.scr == 0)
+ selscroll(orig, -n);
}
void
@@ -1127,7 +1189,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++;
}
@@ -1292,14 +1354,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
@@ -1736,11 +1798,11 @@ csihandle(void)
break;
case 'S': /* SU -- Scroll <n> line up */
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);
@@ -2330,7 +2392,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);
}
@@ -2343,7 +2405,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);
}
@@ -2557,7 +2619,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;
@@ -2594,6 +2656,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));
@@ -2652,7 +2722,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);
}
}
@@ -2673,8 +2743,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 519b9bd..da36b34 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 *);

181
st.c
View File

@@ -35,7 +35,6 @@
#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)
@@ -43,9 +42,6 @@
#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,
@@ -119,9 +115,6 @@ 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 */
@@ -192,8 +185,8 @@ static void tnewline(int);
static void tputtab(int);
static void tputc(Rune);
static void treset(void);
static void tscrollup(int, int, int);
static void tscrolldown(int, int, int);
static void tscrollup(int, int);
static void tscrolldown(int, int);
static void tsetattr(const int *, int);
static void tsetchar(Rune, const Glyph *, int, int);
static void tsetdirt(int, int);
@@ -416,10 +409,10 @@ tlinelen(int y)
{
int i = term.col;
if (TLINE(y)[i - 1].mode & ATTR_WRAP)
if (term.line[y][i - 1].mode & ATTR_WRAP)
return i;
while (i > 0 && TLINE(y)[i - 1].u == ' ')
while (i > 0 && term.line[y][i - 1].u == ' ')
--i;
return i;
@@ -528,7 +521,7 @@ selsnap(int *x, int *y, int direction)
* Snap around if the word wraps around at the end or
* beginning of a line.
*/
prevgp = &TLINE(*y)[*x];
prevgp = &term.line[*y][*x];
prevdelim = ISDELIM(prevgp->u);
for (;;) {
newx = *x + direction;
@@ -543,14 +536,14 @@ selsnap(int *x, int *y, int direction)
yt = *y, xt = *x;
else
yt = newy, xt = newx;
if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
if (!(term.line[yt][xt].mode & ATTR_WRAP))
break;
}
if (newx >= tlinelen(newy))
break;
gp = &TLINE(newy)[newx];
gp = &term.line[newy][newx];
delim = ISDELIM(gp->u);
if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
|| (delim && gp->u != prevgp->u)))
@@ -571,14 +564,14 @@ selsnap(int *x, int *y, int direction)
*x = (direction < 0) ? 0 : term.col - 1;
if (direction < 0) {
for (; *y > 0; *y += direction) {
if (!(TLINE(*y-1)[term.col-1].mode
if (!(term.line[*y-1][term.col-1].mode
& ATTR_WRAP)) {
break;
}
}
} else if (direction > 0) {
for (; *y < term.row-1; *y += direction) {
if (!(TLINE(*y)[term.col-1].mode
if (!(term.line[*y][term.col-1].mode
& ATTR_WRAP)) {
break;
}
@@ -609,13 +602,13 @@ getsel(void)
}
if (sel.type == SEL_RECTANGULAR) {
gp = &TLINE(y)[sel.nb.x];
gp = &term.line[y][sel.nb.x];
lastx = sel.ne.x;
} else {
gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0];
gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
}
last = &TLINE(y)[MIN(lastx, linelen-1)];
last = &term.line[y][MIN(lastx, linelen-1)];
while (last >= gp && last->u == ' ')
--last;
@@ -725,8 +718,14 @@ sigchld(int a)
if ((p = waitpid(pid, &stat, WNOHANG)) < 0)
die("waiting for pid %hd failed: %s\n", pid, strerror(errno));
if (pid != p)
if (pid != p) {
if (p == 0 && wait(&stat) < 0)
die("wait: %s\n", strerror(errno));
/* reinstall sigchld handler */
signal(SIGCHLD, sigchld);
return;
}
if (WIFEXITED(stat) && WEXITSTATUS(stat))
die("child exited with status %d\n", WEXITSTATUS(stat));
@@ -851,9 +850,6 @@ 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);
@@ -1065,52 +1061,12 @@ tswapscreen(void)
}
void
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)
tscrolldown(int orig, int n)
{
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);
@@ -1121,28 +1077,17 @@ tscrolldown(int orig, int n, int copyhist)
term.line[i-n] = temp;
}
if (term.scr == 0)
selscroll(orig, n);
}
void
tscrollup(int orig, int n, int copyhist)
tscrollup(int orig, int n)
{
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);
@@ -1152,7 +1097,6 @@ tscrollup(int orig, int n, int copyhist)
term.line[i+n] = temp;
}
if (term.scr == 0)
selscroll(orig, -n);
}
@@ -1182,7 +1126,7 @@ tnewline(int first_col)
int y = term.c.y;
if (y == term.bot) {
tscrollup(term.top, 1, 1);
tscrollup(term.top, 1);
} else {
y++;
}
@@ -1347,14 +1291,14 @@ void
tinsertblankline(int n)
{
if (BETWEEN(term.c.y, term.top, term.bot))
tscrolldown(term.c.y, n, 0);
tscrolldown(term.c.y, n);
}
void
tdeleteline(int n)
{
if (BETWEEN(term.c.y, term.top, term.bot))
tscrollup(term.c.y, n, 0);
tscrollup(term.c.y, n);
}
int32_t
@@ -1791,11 +1735,11 @@ csihandle(void)
break;
case 'S': /* SU -- Scroll <n> line up */
DEFAULT(csiescseq.arg[0], 1);
tscrollup(term.top, csiescseq.arg[0], 0);
tscrollup(term.top, csiescseq.arg[0]);
break;
case 'T': /* SD -- Scroll <n> line down */
DEFAULT(csiescseq.arg[0], 1);
tscrolldown(term.top, csiescseq.arg[0], 0);
tscrolldown(term.top, csiescseq.arg[0]);
break;
case 'L': /* IL -- Insert <n> blank lines */
DEFAULT(csiescseq.arg[0], 1);
@@ -2043,6 +1987,59 @@ strparse(void)
}
}
void
externalpipe(const Arg *arg)
{
int to[2];
char buf[UTF_SIZ];
void (*oldsigpipe)(int);
Glyph *bp, *end;
int lastpos, n, newline;
if (pipe(to) == -1)
return;
switch (fork()) {
case -1:
close(to[0]);
close(to[1]);
return;
case 0:
dup2(to[0], STDIN_FILENO);
close(to[0]);
close(to[1]);
execvp(((char **)arg->v)[0], (char **)arg->v);
fprintf(stderr, "st: execvp %s\n", ((char **)arg->v)[0]);
perror("failed");
exit(0);
}
close(to[0]);
/* 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;
if (lastpos < 0)
break;
end = &bp[lastpos + 1];
for (; bp < end; ++bp)
if (xwrite(to[1], buf, utf8encode(bp->u, buf)) < 0)
break;
if ((newline = term.line[n][lastpos].mode & ATTR_WRAP))
continue;
if (xwrite(to[1], "\n", 1) < 0)
break;
newline = 0;
}
if (newline)
(void)xwrite(to[1], "\n", 1);
close(to[1]);
/* restore */
signal(SIGPIPE, oldsigpipe);
}
void
strdump(void)
{
@@ -2358,7 +2355,7 @@ eschandle(uchar ascii)
return 0;
case 'D': /* IND -- Linefeed */
if (term.c.y == term.bot) {
tscrollup(term.top, 1, 1);
tscrollup(term.top, 1);
} else {
tmoveto(term.c.x, term.c.y+1);
}
@@ -2371,7 +2368,7 @@ eschandle(uchar ascii)
break;
case 'M': /* RI -- Reverse index */
if (term.c.y == term.top) {
tscrolldown(term.top, 1, 1);
tscrolldown(term.top, 1);
} else {
tmoveto(term.c.x, term.c.y-1);
}
@@ -2585,7 +2582,7 @@ twrite(const char *buf, int buflen, int show_ctrl)
void
tresize(int col, int row)
{
int i, j;
int i;
int minrow = MIN(row, term.row);
int mincol = MIN(col, term.col);
int *bp;
@@ -2622,14 +2619,6 @@ 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));
@@ -2688,7 +2677,7 @@ drawregion(int x1, int y1, int x2, int y2)
continue;
term.dirty[y] = 0;
xdrawline(TLINE(y), x1, y, x2);
xdrawline(term.line[y], x1, y, x2);
}
}
@@ -2709,10 +2698,8 @@ draw(void)
cx--;
drawregion(0, 0, term.col, term.row);
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.line[term.ocy], term.col);
term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
term.ocx = cx;
term.ocy = term.c.y;
xfinishdraw();

7
st.h
View File

@@ -11,8 +11,7 @@
#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
#define DEFAULT(a, b) (a) = (a) ? (a) : (b)
#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode & (~ATTR_WRAP)) || \
(a).fg != (b).fg || \
#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \
(a).bg != (b).bg)
#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
(t1.tv_nsec-t2.tv_nsec)/1E6)
@@ -82,8 +81,7 @@ void die(const char *, ...);
void redraw(void);
void draw(void);
void kscrolldown(const Arg *);
void kscrollup(const Arg *);
void externalpipe(const Arg *);
void printscreen(const Arg *);
void printsel(const Arg *);
void sendbreak(const Arg *);
@@ -127,4 +125,3 @@ extern unsigned int tabspaces;
extern unsigned int defaultfg;
extern unsigned int defaultbg;
extern unsigned int defaultcs;
extern float alpha;

2
win.h
View File

@@ -25,7 +25,7 @@ enum win_mode {
void xbell(void);
void xclipcopy(void);
void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
void xdrawcursor(int, int, Glyph, int, int, Glyph);
void xdrawline(Line, int, int, int);
void xfinishdraw(void);
void xloadcols(void);

240
x.c
View File

@@ -19,7 +19,6 @@ char *argv0;
#include "arg.h"
#include "st.h"
#include "win.h"
#include "hb.h"
/* types used in config.h */
typedef struct {
@@ -82,7 +81,6 @@ typedef XftGlyphFontSpec GlyphFontSpec;
typedef struct {
int tw, th; /* tty width and height */
int w, h; /* window width and height */
int hborderpx, vborderpx;
int ch; /* char height */
int cw; /* char width */
int mode; /* window state/mode flags */
@@ -107,7 +105,6 @@ typedef struct {
XSetWindowAttributes attrs;
int scr;
int isfixed; /* is fixed geometry? */
int depth; /* bit depth */
int l, t; /* left and top offset */
int gm; /* geometry mask */
} XWindow;
@@ -144,7 +141,6 @@ typedef struct {
} DC;
static inline ushort sixd_to_16bit(int);
static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
static void xdrawglyph(Glyph, int, int);
@@ -247,7 +243,6 @@ 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;
@@ -336,7 +331,7 @@ ttysend(const Arg *arg)
int
evcol(XEvent *e)
{
int x = e->xbutton.x - win.hborderpx;
int x = e->xbutton.x - borderpx;
LIMIT(x, 0, win.tw - 1);
return x / win.cw;
}
@@ -344,7 +339,7 @@ evcol(XEvent *e)
int
evrow(XEvent *e)
{
int y = e->xbutton.y - win.vborderpx;
int y = e->xbutton.y - borderpx;
LIMIT(y, 0, win.th - 1);
return y / win.ch;
}
@@ -744,9 +739,6 @@ cresize(int width, int height)
col = MAX(1, col);
row = MAX(1, row);
win.hborderpx = (win.w - col * win.cw) / 2;
win.vborderpx = (win.h - row * win.ch) / 2;
tresize(col, row);
xresize(col, row);
ttyresize(win.tw, win.th);
@@ -760,12 +752,12 @@ xresize(int col, int row)
XFreePixmap(xw.dpy, xw.buf);
xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
xw.depth);
DefaultDepth(xw.dpy, xw.scr));
XftDrawChange(xw.draw, xw.buf);
xclear(0, 0, win.w, win.h);
/* resize to new width */
xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
}
ushort
@@ -820,13 +812,6 @@ xloadcols(void)
else
die("could not allocate color %d\n", i);
}
/* set alpha value of bg color */
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;
}
@@ -884,8 +869,8 @@ xhints(void)
sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize;
sizeh->height = win.h;
sizeh->width = win.w;
sizeh->height_inc = 1;
sizeh->width_inc = 1;
sizeh->height_inc = win.ch;
sizeh->width_inc = win.cw;
sizeh->base_height = 2 * borderpx;
sizeh->base_width = 2 * borderpx;
sizeh->min_height = win.ch + 2 * borderpx;
@@ -1077,9 +1062,6 @@ xunloadfont(Font *f)
void
xunloadfonts(void)
{
/* Clear Harfbuzz font cache. */
hbunloadfonts();
/* Free the loaded fonts in the font cache. */
while (frclen > 0)
XftFontClose(xw.dpy, frc[--frclen].font);
@@ -1152,23 +1134,11 @@ xinit(int cols, int rows)
Window parent;
pid_t thispid = getpid();
XColor xmousefg, xmousebg;
XWindowAttributes attr;
XVisualInfo vis;
if (!(xw.dpy = XOpenDisplay(NULL)))
die("can't open display\n");
xw.scr = XDefaultScreen(xw.dpy);
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;
xw.vis = XDefaultVisual(xw.dpy, xw.scr);
/* font */
if (!FcInit())
@@ -1178,12 +1148,12 @@ xinit(int cols, int rows)
xloadfonts(usedfont, 0);
/* colors */
xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None);
xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
xloadcols();
/* adjust fixed window geometry */
win.w = 2 * win.hborderpx + 2 * borderpx + cols * win.cw;
win.h = 2 * win.vborderpx + 2 * borderpx + rows * win.ch;
win.w = 2 * borderpx + cols * win.cw;
win.h = 2 * borderpx + rows * win.ch;
if (xw.gm & XNegative)
xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2;
if (xw.gm & YNegative)
@@ -1198,20 +1168,24 @@ xinit(int cols, int rows)
| 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,
win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput,
xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
| CWEventMask | CWColormap, &xw.attrs);
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, xw.buf, GCGraphicsExposures, &gcvalues);
dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures,
&gcvalues);
xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
DefaultDepth(xw.dpy, xw.scr));
XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
/* font spec buffer */
xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
/* Xft rendering context */
xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
@@ -1265,26 +1239,10 @@ xinit(int cols, int rows)
xsel.xtarget = XA_STRING;
}
void
xresetfontsettings(ushort mode, Font **font, int *frcflags)
{
*font = &dc.font;
if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
*font = &dc.ibfont;
*frcflags = FRC_ITALICBOLD;
} else if (mode & ATTR_ITALIC) {
*font = &dc.ifont;
*frcflags = FRC_ITALIC;
} else if (mode & ATTR_BOLD) {
*font = &dc.bfont;
*frcflags = FRC_BOLD;
}
}
int
xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
{
float winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch, xp, yp;
float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
ushort mode, prevmode = USHRT_MAX;
Font *font = &dc.font;
int frcflags = FRC_NORMAL;
@@ -1295,65 +1253,49 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
FcPattern *fcpattern, *fontpattern;
FcFontSet *fcsets[] = { NULL };
FcCharSet *fccharset;
int i, f, length = 0, start = 0, numspecs = 0;
float cluster_xp = xp, cluster_yp = yp;
HbTransformData shaped = { 0 };
/* Initial values. */
mode = prevmode = glyphs[0].mode;
xresetfontsettings(mode, &font, &frcflags);
int i, f, numspecs = 0;
for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
/* Fetch rune and mode for current glyph. */
rune = glyphs[i].u;
mode = glyphs[i].mode;
/* Skip dummy wide-character spacing. */
if (mode & ATTR_WDUMMY && i < (len - 1))
if (mode == ATTR_WDUMMY)
continue;
if (
prevmode != mode
|| ATTRCMP(glyphs[start], glyphs[i])
|| selected(x + i, y) != selected(x + start, y)
|| i == (len - 1)
) {
/* Handle 1-character wide segments and end of line */
length = i - start;
if (i == start) {
length = 1;
} else if (i == (len - 1)) {
length = (i - start + 1);
/* Determine font for glyph if different from previous glyph. */
if (prevmode != mode) {
prevmode = mode;
font = &dc.font;
frcflags = FRC_NORMAL;
runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f);
if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
font = &dc.ibfont;
frcflags = FRC_ITALICBOLD;
} else if (mode & ATTR_ITALIC) {
font = &dc.ifont;
frcflags = FRC_ITALIC;
} else if (mode & ATTR_BOLD) {
font = &dc.bfont;
frcflags = FRC_BOLD;
}
yp = winy + font->ascent;
}
/* Shape the segment. */
hbtransform(&shaped, font->match, glyphs, start, length);
runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE) ? 2.0f : 1.0f);
cluster_xp = xp; cluster_yp = yp;
for (int code_idx = 0; code_idx < shaped.count; code_idx++) {
int idx = shaped.glyphs[code_idx].cluster;
if (glyphs[start + idx].mode & ATTR_WDUMMY)
continue;
/* Advance the drawing cursor if we've moved to a new cluster */
if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
xp += runewidth;
cluster_xp = xp;
cluster_yp = yp;
runewidth = win.cw * ((glyphs[start + idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
}
if (shaped.glyphs[code_idx].codepoint != 0) {
/* If symbol is found, put it into the specs. */
/* Lookup character index with default font. */
glyphidx = XftCharIndex(xw.dpy, font->match, rune);
if (glyphidx) {
specs[numspecs].font = font->match;
specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint;
specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
cluster_xp += shaped.positions[code_idx].x_advance / 64.;
cluster_yp += shaped.positions[code_idx].y_advance / 64.;
specs[numspecs].glyph = glyphidx;
specs[numspecs].x = (short)xp;
specs[numspecs].y = (short)yp;
xp += runewidth;
numspecs++;
} else {
/* If it's not found, try to fetch it through the font cache. */
rune = glyphs[start + idx].u;
continue;
}
/* Fallback on font cache, search the font cache for match. */
for (f = 0; f < frclen; f++) {
glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
/* Everything correct. */
@@ -1422,24 +1364,10 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
specs[numspecs].glyph = glyphidx;
specs[numspecs].x = (short)xp;
specs[numspecs].y = (short)yp;
xp += runewidth;
numspecs++;
}
}
/* Cleanup and get ready for next segment. */
hbcleanup(&shaped);
start = i;
/* Determine font for glyph if different from previous glyph. */
if (prevmode != mode) {
prevmode = mode;
xresetfontsettings(mode, &font, &frcflags);
yp = winy + font->ascent;
}
}
}
hbcleanup(&shaped);
return numspecs;
}
@@ -1447,7 +1375,7 @@ void
xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
{
int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
int winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch,
int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
width = charlen * win.cw;
Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
XRenderColor colfg, colbg;
@@ -1537,17 +1465,17 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
/* Intelligent cleaning up of the borders. */
if (x == 0) {
xclear(0, (y == 0)? 0 : winy, win.hborderpx,
xclear(0, (y == 0)? 0 : winy, borderpx,
winy + win.ch +
((winy + win.ch >= win.vborderpx + win.th)? win.h : 0));
((winy + win.ch >= borderpx + win.th)? win.h : 0));
}
if (winx + width >= win.hborderpx + win.tw) {
if (winx + width >= borderpx + win.tw) {
xclear(winx + width, (y == 0)? 0 : winy, win.w,
((winy + win.ch >= win.vborderpx + win.th)? win.h : (winy + win.ch)));
((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch)));
}
if (y == 0)
xclear(winx, 0, winx + width, win.vborderpx);
if (winy + win.ch >= win.vborderpx + win.th)
xclear(winx, 0, winx + width, borderpx);
if (winy + win.ch >= borderpx + win.th)
xclear(winx, winy + win.ch, winx + width, win.h);
/* Clean up the region we want to draw to. */
@@ -1589,17 +1517,14 @@ xdrawglyph(Glyph g, int x, int y)
}
void
xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len)
xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
{
Color drawcol;
/* remove the old cursor */
if (selected(ox, oy))
og.mode ^= ATTR_REVERSE;
/* Redraw the line where cursor was previously.
* It will restore the ligatures broken by the cursor. */
xdrawline(line, 0, oy, len);
xdrawglyph(og, ox, oy);
if (IS_SET(MODE_HIDE))
return;
@@ -1644,35 +1569,35 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int le
case 3: /* Blinking Underline */
case 4: /* Steady Underline */
XftDrawRect(xw.draw, &drawcol,
win.hborderpx + cx * win.cw,
win.vborderpx + (cy + 1) * win.ch - \
borderpx + cx * win.cw,
borderpx + (cy + 1) * win.ch - \
cursorthickness,
win.cw, cursorthickness);
break;
case 5: /* Blinking bar */
case 6: /* Steady bar */
XftDrawRect(xw.draw, &drawcol,
win.hborderpx + cx * win.cw,
win.vborderpx + cy * win.ch,
borderpx + cx * win.cw,
borderpx + cy * win.ch,
cursorthickness, win.ch);
break;
}
} else {
XftDrawRect(xw.draw, &drawcol,
win.hborderpx + cx * win.cw,
win.vborderpx + cy * win.ch,
borderpx + cx * win.cw,
borderpx + cy * win.ch,
win.cw - 1, 1);
XftDrawRect(xw.draw, &drawcol,
win.hborderpx + cx * win.cw,
win.vborderpx + cy * win.ch,
borderpx + cx * win.cw,
borderpx + cy * win.ch,
1, win.ch - 1);
XftDrawRect(xw.draw, &drawcol,
win.hborderpx + (cx + 1) * win.cw - 1,
win.vborderpx + cy * win.ch,
borderpx + (cx + 1) * win.cw - 1,
borderpx + cy * win.ch,
1, win.ch - 1);
XftDrawRect(xw.draw, &drawcol,
win.hborderpx + cx * win.cw,
win.vborderpx + (cy + 1) * win.ch - 1,
borderpx + cx * win.cw,
borderpx + (cy + 1) * win.ch - 1,
win.cw, 1);
}
}
@@ -1727,16 +1652,18 @@ xdrawline(Line line, int x1, int y1, int x2)
Glyph base, new;
XftGlyphFontSpec *specs = xw.specbuf;
numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
i = ox = 0;
for (x = x1; x < x2; x++) {
for (x = x1; x < x2 && i < numspecs; x++) {
new = line[x];
if (new.mode == ATTR_WDUMMY)
continue;
if (selected(x, y1))
new.mode ^= ATTR_REVERSE;
if ((i > 0) && ATTRCMP(base, new)) {
numspecs = xmakeglyphfontspecs(specs, &line[ox], x - ox, ox, y1);
xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
if (i > 0 && ATTRCMP(base, new)) {
xdrawglyphfontspecs(specs, base, i, ox, y1);
specs += i;
numspecs -= i;
i = 0;
}
if (i == 0) {
@@ -1745,10 +1672,8 @@ xdrawline(Line line, int x1, int y1, int x2)
}
i++;
}
if (i > 0) {
numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1);
xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
}
if (i > 0)
xdrawglyphfontspecs(specs, base, i, ox, y1);
}
void
@@ -2110,9 +2035,6 @@ main(int argc, char *argv[])
case 'a':
allowaltscreen = 0;
break;
case 'A':
opt_alpha = EARGF(usage());
break;
case 'c':
opt_class = EARGF(usage());
break;