nageyqma·git
HTTPS https://git.nageyqma.icu/nageyqma/st.git SSH git@git.nageyqma.icu:nageyqma/st.git

HTTPS 链接可直接 git clone;推送代码请使用 SSH。

仅展示默认分支 main 最近 100 条提交。

remove stupid llm configJean Hu · 2026-10-29 · 4e703c9
查看变更
.gitignore | 2 ++
 1 file changed, 2 insertions(+)
diff --git a/.gitignore b/.gitignore
index e399328..1d1230a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,5 @@
 st
 *-protocol.c
 *-protocol.h
+.qoder/
+AGENT.md
tried to fix wired boarderJean Hu · 2026-10-28 · 731ab3f
查看变更
wl.c | 41 ++++++++++++++++++++---------------------
 1 file changed, 20 insertions(+), 21 deletions(-)
diff --git a/wl.c b/wl.c
index 2264aef..1a89d71 100644
--- a/wl.c
+++ b/wl.c
@@ -1240,7 +1240,7 @@ drawglyphspecs(const GlyphSpec *specs, Glyph base, int len, int x, int y)
 	int winy = win.vborderpx + y * win.ch;
 	int width = charlen * win.cw;
 	uint32_t fg, bg, temp;
-	int by1, by2, i;
+	int i;
 
 	/* Fallback on color display for attributes not supported by the font */
 	if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) {
@@ -1287,26 +1287,15 @@ drawglyphspecs(const GlyphSpec *specs, Glyph base, int len, int x, int y)
 	if (base.mode & ATTR_INVISIBLE)
 		fg = bg;
 
-	/* Intelligent cleaning up of the borders. */
-	if (x == 0) {
-		by1 = y ? winy : 0;
-		by2 = winy + win.ch;
-		if (winy + win.ch >= win.vborderpx + win.th)
-			by2 = win.h;
-		clearrect(0, by1, win.hborderpx, by2 - by1, bg);
-	}
-	if (winx + width >= win.hborderpx + win.tw) {
-		by1 = y ? winy : 0;
-		by2 = winy + win.ch >= win.vborderpx + win.th ?
-		      win.h : winy + win.ch;
-		clearrect(winx + width, by1, win.w - (winx + width),
-		          by2 - by1, bg);
-	}
-	if (y == 0)
-		clearrect(winx, 0, width, win.vborderpx, bg);
-	if (winy + win.ch >= win.vborderpx + win.th)
-		clearrect(winx, winy + win.ch, width,
-		          win.h - (winy + win.ch), bg);
+	/* Intelligent cleaning up of the borders, horizontally only: the top and
+	 * the bottom padding always belong to the default background, see
+	 * startdraw(), so that a coloured block on the first or the last row does
+	 * not bleed out of its own row. */
+	if (x == 0)
+		clearrect(0, winy, win.hborderpx, win.ch, bg);
+	if (winx + width >= win.hborderpx + win.tw)
+		clearrect(winx + width, winy, win.w - (winx + width),
+		          win.ch, bg);
 
 	/* Clean up the region we want to draw to. */
 	clearrect(winx, winy, width, win.ch, bg);
@@ -1461,6 +1450,8 @@ drawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line,
 int
 startdraw(void)
 {
+	uint32_t padcol;
+
 	if (!fb.data[0] || !IS_SET(MODE_VISIBLE))
 		return 0;
 
@@ -1475,6 +1466,14 @@ startdraw(void)
 	fb.drawn = 0;
 	draw_pending = 0;
 	resetclip();
+
+	/* The padding the character grid does not fill keeps the default
+	 * background, even under reverse video.  Rows must not paint into it,
+	 * a coloured first or last row would otherwise look too tall. */
+	padcol = dc.col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg];
+	clearrect(0, 0, win.w, win.vborderpx, padcol);
+	clearrect(0, win.vborderpx + win.th, win.w,
+	          win.h - (win.vborderpx + win.th), padcol);
 	tfulldirt();
 	return 1;
 }
cjk fontnageyqma · 2026-10-27 · d97040a
查看变更
README |   4 ++++
 st.1   |   2 ++
 wl.c   | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------------------------------------
 3 files changed, 85 insertions(+), 59 deletions(-)
diff --git a/README b/README
index dee82a3..358f6b7 100644
--- a/README
+++ b/README
@@ -63,6 +63,10 @@ Notes
   complete frame.  This is required because some compositors (KWin) keep
   the current buffer alive until it is replaced and therefore never send
   wl_buffer.release for a single-buffer scheme.
+- Font fallback: a glyph the configured font does not carry (CJK, for
+  instance) is drawn with the first fontconfig candidate whose face really
+  contains it.  The family in config.h stays the primary font; fallback only
+  fills the gaps.
 
 Credits
 -------
diff --git a/st.1 b/st.1
index ff6fd79..5c4ef2f 100644
--- a/st.1
+++ b/st.1
@@ -65,6 +65,8 @@ defines the application id (default $TERM).
 defines the
 .I font
 to use when st is run (a fontconfig pattern).
+Characters it has no glyph for are taken from the first installed font that
+does cover them.
 .TP
 .BI \-g " geometry"
 sets the initial size of the terminal in columns and rows.
diff --git a/wl.c b/wl.c
index 02a6ed9..2264aef 100644
--- a/wl.c
+++ b/wl.c
@@ -108,7 +108,6 @@ typedef struct {
 typedef struct {
 	FT_Face face;
 	FcPattern *pattern;
-	FcFontSet *set;
 	int height, width;
 	int ascent, descent;
 	int badslant, badweight;
@@ -286,6 +285,7 @@ static int sixd_to_16bit(int);
 static void setclip(int, int, int, int);
 static void resetclip(void);
 static void drawglyphbitmap(FT_Face, uint32_t, int, int, uint32_t);
+static FT_Face loadfallbackface(Font *, Rune, FT_UInt *);
 static int makeglyphspecs(GlyphSpec *, const Glyph *, int, int, int);
 static void drawglyphspecs(const GlyphSpec *, Glyph, int, int, int);
 static void drawglyph(Glyph, int, int);
@@ -789,7 +789,6 @@ loadfont(Font *f, FcPattern *pattern, double px)
 
 	f->face = face;
 	f->pattern = configured;
-	f->set = NULL;
 
 	FcPatternDestroy(match);
 	return 0;
@@ -864,8 +863,6 @@ unloadfont(Font *f)
 		FT_Done_Face(f->face);
 	if (f->pattern)
 		FcPatternDestroy(f->pattern);
-	if (f->set)
-		FcFontSetDestroy(f->set);
 	memset(f, 0, sizeof(*f));
 }
 
@@ -1062,6 +1059,66 @@ enum {
 	FRC_ITALICBOLD
 };
 
+/* Face for a rune the configured font does not carry, or NULL when nothing on
+ * the system does.  *glyphidx is its index for that face (0 when not found).
+ *
+ * fontconfig's cached charsets over-report: the best match it offers may have
+ * no cmap entry at all for the rune, which is how CJK came out blank.  Only
+ * FreeType knows what a loaded face really contains, so walk the sorted
+ * candidates and ask it.  Capped because the set is the whole font cache and a
+ * rune nobody covers must not turn a redraw into a file scan. */
+static FT_Face
+loadfallbackface(Font *f, Rune rune, FT_UInt *glyphidx)
+{
+	FcPattern *pattern;
+	FcCharSet *charset;
+	FcFontSet *set;
+	FcResult result;
+	FcChar8 *file;
+	FT_Face face = NULL;
+	int i, index;
+
+	charset = FcCharSetCreate();
+	pattern = FcPatternDuplicate(f->pattern);
+	if (!charset || !pattern)
+		die("out of memory looking up a fallback font\n");
+
+	FcCharSetAddChar(charset, rune);
+	FcPatternAddCharSet(pattern, FC_CHARSET, charset);
+	FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
+	FcConfigSubstitute(NULL, pattern, FcMatchPattern);
+	FcDefaultSubstitute(pattern);
+
+	*glyphidx = 0;
+	set = FcFontSort(NULL, pattern, FcTrue, NULL, &result);
+	for (i = 0; set && i < set->nfont && i < 32; i++) {
+		if (FcPatternGetString(set->fonts[i], FC_FILE, 0, &file) !=
+		    FcResultMatch ||
+		    FcPatternGetInteger(set->fonts[i], FC_INDEX, 0, &index) !=
+		    FcResultMatch)
+			continue;
+		if (FT_New_Face(ftlib, (const char *)file, index, &face))
+			continue;
+
+		FT_Select_Charmap(face, FT_ENCODING_UNICODE);
+		FT_Set_Char_Size(face, 0, (FT_F26Dot6)(f->pxsize * 64 + 0.5),
+		                 72, 72);
+		*glyphidx = FT_Get_Char_Index(face, rune);
+		if (*glyphidx)
+			break;
+		FT_Done_Face(face);
+	}
+
+	if (set)
+		FcFontSetDestroy(set);
+	FcPatternDestroy(pattern);
+	FcCharSetDestroy(charset);
+
+	if (!*glyphidx)
+		return NULL;
+	return face;
+}
+
 /* Turn a run of glyphs into font specs.  Exactly one spec is appended per
  * non-dummy glyph so that hbtransform() can stay index-aligned. */
 static int
@@ -1075,13 +1132,8 @@ makeglyphspecs(GlyphSpec *specs, const Glyph *glyphs, int len, int x, int y)
 	double runewidth = win.cw;
 	Rune rune;
 	FT_UInt glyphidx;
-	FcResult fcres;
-	FcPattern *fcpattern, *fontpattern;
-	FcFontSet *fcsets[] = { NULL };
-	FcCharSet *fccharset;
-	FcChar8 *file;
 	FT_Face face;
-	int f, index, numspecs = 0, i;
+	int f, numspecs = 0, i;
 
 	for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
 		rune = glyphs[i].u;
@@ -1126,9 +1178,11 @@ makeglyphspecs(GlyphSpec *specs, const Glyph *glyphs, int len, int x, int y)
 			continue;
 		}
 
-		/* Fallback on font cache, search the font cache for match. */
+		/* Fallback on font cache, search the font cache for match.  A NULL
+		 * face is a rune nothing on the system covers, see below. */
 		for (f = 0; f < frclen; f++) {
-			glyphidx = FT_Get_Char_Index(frc[f].face, rune);
+			glyphidx = frc[f].face ?
+			           FT_Get_Char_Index(frc[f].face, rune) : 0;
 			/* Everything correct. */
 			if (glyphidx && frc[f].flags == frcflags)
 				break;
@@ -1138,57 +1192,23 @@ makeglyphspecs(GlyphSpec *specs, const Glyph *glyphs, int len, int x, int y)
 				break;
 		}
 
-		/* Nothing was found. Use fontconfig to find a matching font. */
+		/* Nothing was found.  Ask fontconfig for a face carrying the rune
+		 * and cache the answer either way, so a rune is looked up once and
+		 * an uncovered one does not rescan the fonts on every redraw. */
 		if (f >= frclen) {
-			fcpattern = FcPatternDuplicate(font->pattern);
-			fccharset = FcCharSetCreate();
-			if (!fcpattern || !fccharset)
-				die("out of memory looking up a fallback font\n");
-
-			FcCharSetAddChar(fccharset, rune);
-			FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
-			FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
-			FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
-			FcDefaultSubstitute(fcpattern);
-
-			if (!font->set)
-				font->set = FcFontSort(NULL, font->pattern, 1,
-				                       NULL, &fcres);
-			fcsets[0] = font->set;
-			fontpattern = FcFontSetMatch(NULL, fcsets, 1, fcpattern,
-			                             &fcres);
-
-			f = -1;
-			glyphidx = 0;
-			face = font->face;
-			if (fontpattern &&
-			    FcPatternGetString(fontpattern, FC_FILE, 0, &file) ==
-			    FcResultMatch &&
-			    FcPatternGetInteger(fontpattern, FC_INDEX, 0,
-			                        &index) == FcResultMatch &&
-			    !FT_New_Face(ftlib, (const char *)file, index, &face)) {
-				FT_Select_Charmap(face, FT_ENCODING_UNICODE);
-				FT_Set_Char_Size(face, 0,
-				                 (FT_F26Dot6)(font->pxsize * 64 + 0.5),
-				                 72, 72);
-				glyphidx = FT_Get_Char_Index(face, rune);
-
-				if (frclen >= frccap) {
-					frccap += 16;
-					frc = xrealloc(frc,
-					               frccap * sizeof(Fontcache));
-				}
-				frc[frclen].face = face;
-				frc[frclen].flags = frcflags;
-				frc[frclen].unicodep = rune;
-				f = frclen++;
-			}
+			face = loadfallbackface(font, rune, &glyphidx);
 
-			FcPatternDestroy(fcpattern);
-			FcCharSetDestroy(fccharset);
+			if (frclen >= frccap) {
+				frccap += 16;
+				frc = xrealloc(frc, frccap * sizeof(Fontcache));
+			}
+			frc[frclen].face = face;
+			frc[frclen].flags = frcflags;
+			frc[frclen].unicodep = rune;
+			f = frclen++;
 		}
 
-		specs[numspecs].font = f >= 0 ? frc[f].face : face;
+		specs[numspecs].font = frc[f].face ? frc[f].face : font->face;
 		specs[numspecs].glyph = glyphidx;
 		specs[numspecs].x = (short)xp;
 		specs[numspecs].y = (short)yp;
early wayland supportnageyqma · 2026-10-23 · b5c3fba
查看变更
.gitignore |    2 +
 Makefile   |   83 ++-
 README     |   41 +-
 boxdraw.c  |   94 ++--
 config.h   |  472 +++++++++---------
 config.mk  |   46 +-
 hb.c       |  125 +++--
 hb.h       |    9 +-
 render.h   |   28 ++
 st.1       |   91 ++--
 st.c       |  150 +++---
 st.h       |   19 +-
 win.h      |   76 ++-
 wl.c       | 3848 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 x.c        | 2146 ------------------------------------------------------------------------------
 15 files changed, 4557 insertions(+), 2673 deletions(-)
diff --git a/.gitignore b/.gitignore
index ddd21cd..e399328 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@
 *.rej
 *.o
 st
+*-protocol.c
+*-protocol.h
diff --git a/Makefile b/Makefile
index 2ffd3c8..bc7bb78 100644
--- a/Makefile
+++ b/Makefile
@@ -4,11 +4,36 @@
 
 include config.mk
 
-SRC = st.c x.c boxdraw.c hb.c
+PROTOHDR = xdg-shell-client-protocol.h \
+           fractional-scale-client-protocol.h \
+           viewporter-client-protocol.h \
+           cursor-shape-client-protocol.h \
+           system-bell-client-protocol.h \
+           primary-selection-client-protocol.h \
+           text-input-client-protocol.h \
+           tablet-v2-client-protocol.h
+PROTOC = xdg-shell-protocol.c \
+         fractional-scale-protocol.c \
+         viewporter-protocol.c \
+         cursor-shape-protocol.c \
+         system-bell-protocol.c \
+         primary-selection-protocol.c \
+         text-input-protocol.c \
+         tablet-v2-protocol.c
+SRC = st.c wl.c boxdraw.c hb.c $(PROTOC)
 OBJ = $(SRC:.c=.o)
 
 all: options st
 
+# wayland-protocols is only needed to (re)generate the protocol bindings, so
+# clean and dist keep working without it; anything else fails with a hint
+# instead of make looking for /stable/xdg-shell/xdg-shell.xml.
+ifeq ($(WLPROTO),)
+ifeq ($(filter clean dist uninstall,$(MAKECMDGOALS)),)
+$(error wayland-protocols not found: install it or run make WLPROTO=/path/to/wayland-protocols)
+endif
+endif
+
 options:
 	@echo st build options:
 	@echo "CFLAGS  = $(STCFLAGS)"
@@ -22,23 +47,65 @@ 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
-boxdraw.o: config.h st.h boxdraw_data.h
+wl.o: arg.h config.h st.h win.h render.h hb.h $(PROTOHDR)
+hb.o: st.h render.h hb.h
+boxdraw.o: config.h st.h render.h boxdraw_data.h
+$(PROTOC:.c=.o): $(PROTOHDR)
 
 $(OBJ): config.h config.mk
 
 st: $(OBJ)
 	$(CC) -o $@ $(OBJ) $(STLDFLAGS)
 
+# Wayland protocol bindings (generated, not committed)
+xdg-shell-client-protocol.h: $(XDG_SHELL_XML)
+	$(WAYLAND_SCANNER) client-header $< $@
+xdg-shell-protocol.c: $(XDG_SHELL_XML)
+	$(WAYLAND_SCANNER) private-code $< $@
+
+fractional-scale-client-protocol.h: $(FRACTIONAL_XML)
+	$(WAYLAND_SCANNER) client-header $< $@
+fractional-scale-protocol.c: $(FRACTIONAL_XML)
+	$(WAYLAND_SCANNER) private-code $< $@
+
+viewporter-client-protocol.h: $(VIEWPORTER_XML)
+	$(WAYLAND_SCANNER) client-header $< $@
+viewporter-protocol.c: $(VIEWPORTER_XML)
+	$(WAYLAND_SCANNER) private-code $< $@
+
+cursor-shape-client-protocol.h: $(CURSOR_SHAPE_XML)
+	$(WAYLAND_SCANNER) client-header $< $@
+cursor-shape-protocol.c: $(CURSOR_SHAPE_XML)
+	$(WAYLAND_SCANNER) private-code $< $@
+
+system-bell-client-protocol.h: $(SYSTEM_BELL_XML)
+	$(WAYLAND_SCANNER) client-header $< $@
+system-bell-protocol.c: $(SYSTEM_BELL_XML)
+	$(WAYLAND_SCANNER) private-code $< $@
+
+primary-selection-client-protocol.h: $(PRIMARY_XML)
+	$(WAYLAND_SCANNER) client-header $< $@
+primary-selection-protocol.c: $(PRIMARY_XML)
+	$(WAYLAND_SCANNER) private-code $< $@
+
+text-input-client-protocol.h: $(TEXT_INPUT_XML)
+	$(WAYLAND_SCANNER) client-header $< $@
+text-input-protocol.c: $(TEXT_INPUT_XML)
+	$(WAYLAND_SCANNER) private-code $< $@
+
+tablet-v2-client-protocol.h: $(TABLET_XML)
+	$(WAYLAND_SCANNER) client-header $< $@
+tablet-v2-protocol.c: $(TABLET_XML)
+	$(WAYLAND_SCANNER) private-code $< $@
+
 clean:
-	rm -f st $(OBJ) st-$(VERSION).tar.gz
+	rm -f st $(OBJ) $(PROTOC) $(PROTOHDR) st-$(VERSION).tar.gz
 
 dist: clean
 	mkdir -p st-$(VERSION)
-	cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\
-		config.def.h st.info st.1 arg.h st.h win.h $(SRC)\
-		st-$(VERSION)
+	cp -R AGENT.md LEGACY LICENSE Makefile README config.mk config.h\
+		st.info st.1 arg.h st.h wl.c hb.h hb.c boxdraw.c boxdraw_data.h\
+		render.h st.c st-$(VERSION)
 	tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz
 	rm -rf st-$(VERSION)
 
diff --git a/README b/README
index 6a846ed..dee82a3 100644
--- a/README
+++ b/README
@@ -1,11 +1,28 @@
 st - simple terminal
 --------------------
-st is a simple terminal emulator for X which sucks less.
+st is a simple terminal emulator for Wayland which sucks less.
+
+It is a port of suckless st: the terminal core (st.c) is unchanged, the
+old X11 backend has been replaced by a small Wayland backend (wl.c) that
+draws an ARGB8888 wl_shm buffer.  There is no X11 code path and no
+XWayland fallback.
 
 
 Requirements
 ------------
-In order to build st you need the Xlib header files.
+In order to build st you need:
+
+  - wayland-client and wayland-protocols (wayland-scanner)
+  - xkbcommon
+  - fontconfig and freetype2
+  - harfbuzz
+
+The protocol bindings are generated at build time and are not committed.
+The XMLs are found through pkg-config, then under $XDG_DATA_HOME (or
+~/.local/share), /usr/local/share and /usr/share.  If wayland-protocols
+lives somewhere else, point WLPROTO at it:
+
+    make WLPROTO=/opt/wayland-protocols
 
 
 Installation
@@ -21,6 +38,12 @@ necessary as root):
 
 Running st
 ----------
+st runs on any Wayland compositor that implements xdg-shell and wl_shm.
+The following protocols are used when the compositor provides them and
+silently skipped otherwise: fractional-scale/viewporter for crisp
+fractional scaling, text-input-v3 for input methods, primary-selection,
+cursor-shape and xdg-system-bell.
+
 If you did not install st with make clean install, you must compile
 the st terminfo entry with the following command:
 
@@ -28,7 +51,19 @@ the st terminfo entry with the following command:
 
 See the man page for additional details.
 
+Notes
+-----
+- Wayland does not let clients place their windows.  -g only selects the
+  initial number of columns and rows; offsets are ignored with a warning.
+- -n and -w are gone: X11 instance names and XEmbed do not exist here.
+- Scaling: the surface is rendered at the compositor's preferred scale and
+  mapped through wp_viewport, so 1.5x and other fractional scales stay
+  crisp.
+- The window is drawn with two rotating buffers; every commit contains the
+  complete frame.  This is required because some compositors (KWin) keep
+  the current buffer alive until it is replaced and therefore never send
+  wl_buffer.release for a single-buffer scheme.
+
 Credits
 -------
 Based on Aurélien APTEL <aurelien dot aptel at gmail dot com> bt source code.
-
diff --git a/boxdraw.c b/boxdraw.c
index 28a92d0..ce14012 100644
--- a/boxdraw.c
+++ b/boxdraw.c
@@ -3,29 +3,18 @@
  * MIT/X Consortium License
  */
 
-#include <X11/Xft/Xft.h>
 #include "st.h"
+#include "render.h"
 #include "boxdraw_data.h"
 
 /* Rounded non-negative integers division of n / d  */
 #define DIV(n, d) (((n) + (d) / 2) / (d))
 
-static Display *xdpy;
-static Colormap xcmap;
-static XftDraw *xd;
-static Visual *xvis;
-
-static void drawbox(int, int, int, int, XftColor *, XftColor *, ushort);
-static void drawboxlines(int, int, int, int, XftColor *, ushort);
+static void drawbox(int, int, int, int, uint32_t, uint32_t, ushort);
+static void drawboxlines(int, int, int, int, uint32_t, ushort);
 
 /* public API */
 
-void
-boxdraw_xinit(Display *dpy, Colormap cmap, XftDraw *draw, Visual *vis)
-{
-	xdpy = dpy; xcmap = cmap; xd = draw, xvis = vis;
-}
-
 int
 isboxdraw(Rune u)
 {
@@ -46,8 +35,8 @@ boxdrawindex(const Glyph *g)
 }
 
 void
-drawboxes(int x, int y, int cw, int ch, XftColor *fg, XftColor *bg,
-          const XftGlyphFontSpec *specs, int len)
+drawboxes(int x, int y, int cw, int ch, uint32_t fg, uint32_t bg,
+          const GlyphSpec *specs, int len)
 {
 	for ( ; len-- > 0; x += cw, specs++)
 		drawbox(x, y, cw, ch, fg, bg, (ushort)specs->glyph);
@@ -56,7 +45,7 @@ drawboxes(int x, int y, int cw, int ch, XftColor *fg, XftColor *bg,
 /* implementation */
 
 void
-drawbox(int x, int y, int w, int h, XftColor *fg, XftColor *bg, ushort bd)
+drawbox(int x, int y, int w, int h, uint32_t fg, uint32_t bg, ushort bd)
 {
 	ushort cat = bd & ~(BDB | 0xff);  /* mask out bold and data */
 	if (bd & (BDL | BDA)) {
@@ -66,66 +55,61 @@ drawbox(int x, int y, int w, int h, XftColor *fg, XftColor *bg, ushort bd)
 	} else if (cat == BBD) {
 		/* lower (8-X)/8 block */
 		int d = DIV((uint8_t)bd * h, 8);
-		XftDrawRect(xd, fg, x, y + d, w, h - d);
+		blendrect(x, y + d, w, h - d, fg);
 
 	} else if (cat == BBU) {
 		/* upper X/8 block */
-		XftDrawRect(xd, fg, x, y, w, DIV((uint8_t)bd * h, 8));
+		blendrect(x, y, w, DIV((uint8_t)bd * h, 8), fg);
 
 	} else if (cat == BBL) {
 		/* left X/8 block */
-		XftDrawRect(xd, fg, x, y, DIV((uint8_t)bd * w, 8), h);
+		blendrect(x, y, DIV((uint8_t)bd * w, 8), h, fg);
 
 	} else if (cat == BBR) {
 		/* right (8-X)/8 block */
 		int d = DIV((uint8_t)bd * w, 8);
-		XftDrawRect(xd, fg, x + d, y, w - d, h);
+		blendrect(x + d, y, w - d, h, fg);
 
 	} else if (cat == BBQ) {
 		/* Quadrants */
 		int w2 = DIV(w, 2), h2 = DIV(h, 2);
 		if (bd & TL)
-			XftDrawRect(xd, fg, x, y, w2, h2);
+			blendrect(x, y, w2, h2, fg);
 		if (bd & TR)
-			XftDrawRect(xd, fg, x + w2, y, w - w2, h2);
+			blendrect(x + w2, y, w - w2, h2, fg);
 		if (bd & BL)
-			XftDrawRect(xd, fg, x, y + h2, w2, h - h2);
+			blendrect(x, y + h2, w2, h - h2, fg);
 		if (bd & BR)
-			XftDrawRect(xd, fg, x + w2, y + h2, w - w2, h - h2);
+			blendrect(x + w2, y + h2, w - w2, h - h2, fg);
 
 	} else if (bd & BBS) {
 		/* Shades - data is 1/2/3 for 25%/50%/75% alpha, respectively */
 		int d = (uint8_t)bd;
-		XftColor xfc;
-		XRenderColor xrc = { .alpha = 0xffff };
-
-		xrc.red = DIV(fg->color.red * d + bg->color.red * (4 - d), 4);
-		xrc.green = DIV(fg->color.green * d + bg->color.green * (4 - d), 4);
-		xrc.blue = DIV(fg->color.blue * d + bg->color.blue * (4 - d), 4);
+		uint32_t r = DIV(((fg >> 16) & 0xff) * d + ((bg >> 16) & 0xff) * (4 - d), 4);
+		uint32_t g = DIV(((fg >> 8) & 0xff) * d + ((bg >> 8) & 0xff) * (4 - d), 4);
+		uint32_t b = DIV((fg & 0xff) * d + (bg & 0xff) * (4 - d), 4);
 
-		XftColorAllocValue(xdpy, xvis, xcmap, &xrc, &xfc);
-		XftDrawRect(xd, &xfc, x, y, w, h);
-		XftColorFree(xdpy, xvis, xcmap, &xfc);
+		blendrect(x, y, w, h, 0xff000000 | r << 16 | g << 8 | b);
 
 	} else if (cat == BRL) {
 		/* braille, each data bit corresponds to one dot at 2x4 grid */
 		int w1 = DIV(w, 2);
 		int h1 = DIV(h, 4), h2 = DIV(h, 2), h3 = DIV(3 * h, 4);
 
-		if (bd & 1)   XftDrawRect(xd, fg, x, y, w1, h1);
-		if (bd & 2)   XftDrawRect(xd, fg, x, y + h1, w1, h2 - h1);
-		if (bd & 4)   XftDrawRect(xd, fg, x, y + h2, w1, h3 - h2);
-		if (bd & 8)   XftDrawRect(xd, fg, x + w1, y, w - w1, h1);
-		if (bd & 16)  XftDrawRect(xd, fg, x + w1, y + h1, w - w1, h2 - h1);
-		if (bd & 32)  XftDrawRect(xd, fg, x + w1, y + h2, w - w1, h3 - h2);
-		if (bd & 64)  XftDrawRect(xd, fg, x, y + h3, w1, h - h3);
-		if (bd & 128) XftDrawRect(xd, fg, x + w1, y + h3, w - w1, h - h3);
+		if (bd & 1)   blendrect(x, y, w1, h1, fg);
+		if (bd & 2)   blendrect(x, y + h1, w1, h2 - h1, fg);
+		if (bd & 4)   blendrect(x, y + h2, w1, h3 - h2, fg);
+		if (bd & 8)   blendrect(x + w1, y, w - w1, h1, fg);
+		if (bd & 16)  blendrect(x + w1, y + h1, w - w1, h2 - h1, fg);
+		if (bd & 32)  blendrect(x + w1, y + h2, w - w1, h3 - h2, fg);
+		if (bd & 64)  blendrect(x, y + h3, w1, h - h3, fg);
+		if (bd & 128) blendrect(x + w1, y + h3, w - w1, h - h3, fg);
 
 	}
 }
 
 void
-drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd)
+drawboxlines(int x, int y, int w, int h, uint32_t fg, ushort bd)
 {
 	/* s: stem thickness. width/8 roughly matches underscore thickness. */
 	/* We draw bold as 1.5 * normal-stem and at least 1px thicker.      */
@@ -151,13 +135,13 @@ drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd)
 		int d = arc || (multi_double && !multi_light) ? -s : 0;
 
 		if (bd & LL)
-			XftDrawRect(xd, fg, x, y + h2, w2 + s + d, s);
+			blendrect(x, y + h2, w2 + s + d, s, fg);
 		if (bd & LU)
-			XftDrawRect(xd, fg, x + w2, y, s, h2 + s + d);
+			blendrect(x + w2, y, s, h2 + s + d, fg);
 		if (bd & LR)
-			XftDrawRect(xd, fg, x + w2 - d, y + h2, w - w2 + d, s);
+			blendrect(x + w2 - d, y + h2, w - w2 + d, s, fg);
 		if (bd & LD)
-			XftDrawRect(xd, fg, x + w2, y + h2 - d, s, h - h2 + d);
+			blendrect(x + w2, y + h2 - d, s, h - h2 + d, fg);
 	}
 
 	/* double lines - also align with light to form heavy when combined */
@@ -172,23 +156,23 @@ drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd)
 		int dl = bd & DL, du = bd & DU, dr = bd & DR, dd = bd & DD;
 		if (dl) {
 			int p = dd ? -s : 0, n = du ? -s : dd ? s : 0;
-			XftDrawRect(xd, fg, x, y + h2 + s, w2 + s + p, s);
-			XftDrawRect(xd, fg, x, y + h2 - s, w2 + s + n, s);
+			blendrect(x, y + h2 + s, w2 + s + p, s, fg);
+			blendrect(x, y + h2 - s, w2 + s + n, s, fg);
 		}
 		if (du) {
 			int p = dl ? -s : 0, n = dr ? -s : dl ? s : 0;
-			XftDrawRect(xd, fg, x + w2 - s, y, s, h2 + s + p);
-			XftDrawRect(xd, fg, x + w2 + s, y, s, h2 + s + n);
+			blendrect(x + w2 - s, y, s, h2 + s + p, fg);
+			blendrect(x + w2 + s, y, s, h2 + s + n, fg);
 		}
 		if (dr) {
 			int p = du ? -s : 0, n = dd ? -s : du ? s : 0;
-			XftDrawRect(xd, fg, x + w2 - p, y + h2 - s, w - w2 + p, s);
-			XftDrawRect(xd, fg, x + w2 - n, y + h2 + s, w - w2 + n, s);
+			blendrect(x + w2 - p, y + h2 - s, w - w2 + p, s, fg);
+			blendrect(x + w2 - n, y + h2 + s, w - w2 + n, s, fg);
 		}
 		if (dd) {
 			int p = dr ? -s : 0, n = dl ? -s : dr ? s : 0;
-			XftDrawRect(xd, fg, x + w2 + s, y + h2 - p, s, h - h2 + p);
-			XftDrawRect(xd, fg, x + w2 - s, y + h2 - n, s, h - h2 + n);
+			blendrect(x + w2 + s, y + h2 - p, s, h - h2 + p, fg);
+			blendrect(x + w2 - s, y + h2 - n, s, h - h2 + n, fg);
 		}
 	}
 }
diff --git a/config.h b/config.h
index ecab61e..3a6852f 100644
--- a/config.h
+++ b/config.h
@@ -7,7 +7,7 @@
  */
 //static char *font = "JetBrainsMono Nerd Font Mono:pixelsize=26:antialias=true:autohint=true";
 //static char *font = "SauceCodePro Nerd Font Mono:pixelsize=26:antialias=true:autohint=true";
-static char *font = "SauceCodePro Nerd Font Mono:pixelsize=20:antialias=true:autohint=true";
+static char *font = "SauceCodePro Nerd Font Mono:pixelsize=26:antialias=true:autohint=true";
 //static char *font = "Source Code Pro:pixelsize=26:antialias=true:autohint=true";
 static int borderpx = 1;
 
@@ -171,11 +171,11 @@ static unsigned int cols = 80;
 static unsigned int rows = 24;
 
 /*
- * Default colour and shape of the mouse cursor
+ * Default shape of the mouse cursor.  Wayland compositors own the cursor
+ * theme, so this is a cursor name ("text", "default", "pointer", ...).
+ * The XC_* aliases in win.h cover the common X cursor names.
  */
-static unsigned int mouseshape = XC_xterm;
-static unsigned int mousefg = 7;
-static unsigned int mousebg = 0;
+static char *mouseshape = XC_xterm;
 
 /*
  * Color used to display font attributes when fontconfig selected a font which
@@ -200,36 +200,36 @@ static uint forcemousemod = ShiftMask;
  */
 static MouseShortcut mshortcuts[] = {
 	/* mask                 button   function        argument       release */
-	{ XK_ANY_MOD,           Button2, selpaste,       {.i = 0},      1 },
-	{ XK_NO_MOD,            Button4, kscrollup,      {.i = 1}         },
-	{ XK_NO_MOD,            Button5, kscrolldown,    {.i = 1}         },
+	{ MOD_ANY,           Button2, selpaste,       {.i = 0},      1 },
+	{ MOD_NONE,            Button4, kscrollup,      {.i = 1}         },
+	{ MOD_NONE,            Button5, kscrolldown,    {.i = 1}         },
 	{ ControlMask,          Button4, kscrollup,      {.i = -1}        },
 	{ ControlMask,          Button5, kscrolldown,    {.i = -1}        },
 };
 
 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} },
-	{ ControlMask,          XK_equal,       zoom,           {.f = +1} },
-	{ ControlMask,          XK_minus,       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} },
- 	{ TERMMOD,              XK_S,           keyboard_select,{.i =  0} },
+	{ MOD_ANY,           XKB_KEY_Break,       sendbreak,      {.i =  0} },
+	{ ControlMask,          XKB_KEY_Print,       toggleprinter,  {.i =  0} },
+	{ ShiftMask,            XKB_KEY_Print,       printscreen,    {.i =  0} },
+	{ MOD_ANY,           XKB_KEY_Print,       printsel,       {.i =  0} },
+	{ ControlMask,          XKB_KEY_equal,       zoom,           {.f = +1} },
+	{ ControlMask,          XKB_KEY_minus,       zoom,           {.f = -1} },
+	{ TERMMOD,              XKB_KEY_Home,        zoomreset,      {.f =  0} },
+	{ TERMMOD,              XKB_KEY_C,           clipcopy,       {.i =  0} },
+	{ TERMMOD,              XKB_KEY_V,           clippaste,      {.i =  0} },
+	{ TERMMOD,              XKB_KEY_Y,           selpaste,       {.i =  0} },
+	{ ShiftMask,            XKB_KEY_Insert,      selpaste,       {.i =  0} },
+	{ TERMMOD,              XKB_KEY_Num_Lock,    numlock,        {.i =  0} },
+ 	{ TERMMOD,              XKB_KEY_S,           keyboard_select,{.i =  0} },
 };
 
 /*
  * Special keys (change & recompile st.info accordingly)
  *
  * Mask value:
- * * Use XK_ANY_MOD to match the key no matter modifiers state
- * * Use XK_NO_MOD to match the key alone (no modifiers)
+ * * Use MOD_ANY to match the key no matter modifiers state
+ * * Use MOD_NONE to match the key alone (no modifiers)
  * appkey value:
  * * 0: no value
  * * > 0: keypad application mode enabled
@@ -241,21 +241,21 @@ static Shortcut shortcuts[] = {
  * * < 0: cursor application mode disabled
  *
  * Be careful with the order of the definitions because st searches in
- * this table sequentially, so any XK_ANY_MOD must be in the last
+ * this table sequentially, so any MOD_ANY must be in the last
  * position for a key.
  */
 
 /*
- * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF)
+ * If you want keys other than the function keys (0xFD00 - 0xFFFF)
  * to be mapped below, add them to this array.
  */
 static KeySym mappedkeys[] = { -1 };
 
 /*
  * State bits to ignore when matching key or button events.  By default,
- * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored.
+ * numlock (Mod2Mask) and keyboard layout (MOD_SWITCH) are ignored.
  */
-static uint ignoremod = Mod2Mask|XK_SWITCH_MOD;
+static uint ignoremod = Mod2Mask|MOD_SWITCH;
 
 /*
  * This is the huge key array which defines all compatibility to the Linux
@@ -263,215 +263,215 @@ static uint ignoremod = Mod2Mask|XK_SWITCH_MOD;
  */
 static Key key[] = {
 	/* keysym           mask            string      appkey appcursor */
-	{ XK_KP_Home,       ShiftMask,      "\033[2J",       0,   -1},
-	{ XK_KP_Home,       ShiftMask,      "\033[1;2H",     0,   +1},
-	{ XK_KP_Home,       XK_ANY_MOD,     "\033[H",        0,   -1},
-	{ XK_KP_Home,       XK_ANY_MOD,     "\033[1~",       0,   +1},
-	{ XK_KP_Up,         XK_ANY_MOD,     "\033Ox",       +1,    0},
-	{ XK_KP_Up,         XK_ANY_MOD,     "\033[A",        0,   -1},
-	{ XK_KP_Up,         XK_ANY_MOD,     "\033OA",        0,   +1},
-	{ XK_KP_Down,       XK_ANY_MOD,     "\033Or",       +1,    0},
-	{ XK_KP_Down,       XK_ANY_MOD,     "\033[B",        0,   -1},
-	{ XK_KP_Down,       XK_ANY_MOD,     "\033OB",        0,   +1},
-	{ XK_KP_Left,       XK_ANY_MOD,     "\033Ot",       +1,    0},
-	{ XK_KP_Left,       XK_ANY_MOD,     "\033[D",        0,   -1},
-	{ XK_KP_Left,       XK_ANY_MOD,     "\033OD",        0,   +1},
-	{ XK_KP_Right,      XK_ANY_MOD,     "\033Ov",       +1,    0},
-	{ XK_KP_Right,      XK_ANY_MOD,     "\033[C",        0,   -1},
-	{ XK_KP_Right,      XK_ANY_MOD,     "\033OC",        0,   +1},
-	{ XK_KP_Prior,      ShiftMask,      "\033[5;2~",     0,    0},
-	{ XK_KP_Prior,      XK_ANY_MOD,     "\033[5~",       0,    0},
-	{ XK_KP_Begin,      XK_ANY_MOD,     "\033[E",        0,    0},
-	{ XK_KP_End,        ControlMask,    "\033[J",       -1,    0},
-	{ XK_KP_End,        ControlMask,    "\033[1;5F",    +1,    0},
-	{ XK_KP_End,        ShiftMask,      "\033[K",       -1,    0},
-	{ XK_KP_End,        ShiftMask,      "\033[1;2F",    +1,    0},
-	{ XK_KP_End,        XK_ANY_MOD,     "\033[4~",       0,    0},
-	{ XK_KP_Next,       ShiftMask,      "\033[6;2~",     0,    0},
-	{ XK_KP_Next,       XK_ANY_MOD,     "\033[6~",       0,    0},
-	{ XK_KP_Insert,     ShiftMask,      "\033[2;2~",    +1,    0},
-	{ XK_KP_Insert,     ShiftMask,      "\033[4l",      -1,    0},
-	{ XK_KP_Insert,     ControlMask,    "\033[L",       -1,    0},
-	{ XK_KP_Insert,     ControlMask,    "\033[2;5~",    +1,    0},
-	{ XK_KP_Insert,     XK_ANY_MOD,     "\033[4h",      -1,    0},
-	{ XK_KP_Insert,     XK_ANY_MOD,     "\033[2~",      +1,    0},
-	{ XK_KP_Delete,     ControlMask,    "\033[M",       -1,    0},
-	{ XK_KP_Delete,     ControlMask,    "\033[3;5~",    +1,    0},
-	{ XK_KP_Delete,     ShiftMask,      "\033[2K",      -1,    0},
-	{ XK_KP_Delete,     ShiftMask,      "\033[3;2~",    +1,    0},
-	{ XK_KP_Delete,     XK_ANY_MOD,     "\033[P",       -1,    0},
-	{ XK_KP_Delete,     XK_ANY_MOD,     "\033[3~",      +1,    0},
-	{ XK_KP_Multiply,   XK_ANY_MOD,     "\033Oj",       +2,    0},
-	{ XK_KP_Add,        XK_ANY_MOD,     "\033Ok",       +2,    0},
-	{ XK_KP_Enter,      XK_ANY_MOD,     "\033OM",       +2,    0},
-	{ XK_KP_Enter,      XK_ANY_MOD,     "\r",           -1,    0},
-	{ XK_KP_Subtract,   XK_ANY_MOD,     "\033Om",       +2,    0},
-	{ XK_KP_Decimal,    XK_ANY_MOD,     "\033On",       +2,    0},
-	{ XK_KP_Divide,     XK_ANY_MOD,     "\033Oo",       +2,    0},
-	{ XK_KP_0,          XK_ANY_MOD,     "\033Op",       +2,    0},
-	{ XK_KP_1,          XK_ANY_MOD,     "\033Oq",       +2,    0},
-	{ XK_KP_2,          XK_ANY_MOD,     "\033Or",       +2,    0},
-	{ XK_KP_3,          XK_ANY_MOD,     "\033Os",       +2,    0},
-	{ XK_KP_4,          XK_ANY_MOD,     "\033Ot",       +2,    0},
-	{ XK_KP_5,          XK_ANY_MOD,     "\033Ou",       +2,    0},
-	{ XK_KP_6,          XK_ANY_MOD,     "\033Ov",       +2,    0},
-	{ XK_KP_7,          XK_ANY_MOD,     "\033Ow",       +2,    0},
-	{ XK_KP_8,          XK_ANY_MOD,     "\033Ox",       +2,    0},
-	{ XK_KP_9,          XK_ANY_MOD,     "\033Oy",       +2,    0},
-	{ XK_Up,            ShiftMask,      "\033[1;2A",     0,    0},
-	{ XK_Up,            Mod1Mask,       "\033[1;3A",     0,    0},
-	{ XK_Up,         ShiftMask|Mod1Mask,"\033[1;4A",     0,    0},
-	{ XK_Up,            ControlMask,    "\033[1;5A",     0,    0},
-	{ XK_Up,      ShiftMask|ControlMask,"\033[1;6A",     0,    0},
-	{ XK_Up,       ControlMask|Mod1Mask,"\033[1;7A",     0,    0},
-	{ XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A",  0,    0},
-	{ XK_Up,            XK_ANY_MOD,     "\033[A",        0,   -1},
-	{ XK_Up,            XK_ANY_MOD,     "\033OA",        0,   +1},
-	{ XK_Down,          ShiftMask,      "\033[1;2B",     0,    0},
-	{ XK_Down,          Mod1Mask,       "\033[1;3B",     0,    0},
-	{ XK_Down,       ShiftMask|Mod1Mask,"\033[1;4B",     0,    0},
-	{ XK_Down,          ControlMask,    "\033[1;5B",     0,    0},
-	{ XK_Down,    ShiftMask|ControlMask,"\033[1;6B",     0,    0},
-	{ XK_Down,     ControlMask|Mod1Mask,"\033[1;7B",     0,    0},
-	{ XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0,    0},
-	{ XK_Down,          XK_ANY_MOD,     "\033[B",        0,   -1},
-	{ XK_Down,          XK_ANY_MOD,     "\033OB",        0,   +1},
-	{ XK_Left,          ShiftMask,      "\033[1;2D",     0,    0},
-	{ XK_Left,          Mod1Mask,       "\033[1;3D",     0,    0},
-	{ XK_Left,       ShiftMask|Mod1Mask,"\033[1;4D",     0,    0},
-	{ XK_Left,          ControlMask,    "\033[1;5D",     0,    0},
-	{ XK_Left,    ShiftMask|ControlMask,"\033[1;6D",     0,    0},
-	{ XK_Left,     ControlMask|Mod1Mask,"\033[1;7D",     0,    0},
-	{ XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0,    0},
-	{ XK_Left,          XK_ANY_MOD,     "\033[D",        0,   -1},
-	{ XK_Left,          XK_ANY_MOD,     "\033OD",        0,   +1},
-	{ XK_Right,         ShiftMask,      "\033[1;2C",     0,    0},
-	{ XK_Right,         Mod1Mask,       "\033[1;3C",     0,    0},
-	{ XK_Right,      ShiftMask|Mod1Mask,"\033[1;4C",     0,    0},
-	{ XK_Right,         ControlMask,    "\033[1;5C",     0,    0},
-	{ XK_Right,   ShiftMask|ControlMask,"\033[1;6C",     0,    0},
-	{ XK_Right,    ControlMask|Mod1Mask,"\033[1;7C",     0,    0},
-	{ XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0,   0},
-	{ XK_Right,         XK_ANY_MOD,     "\033[C",        0,   -1},
-	{ XK_Right,         XK_ANY_MOD,     "\033OC",        0,   +1},
-	{ XK_ISO_Left_Tab,  ShiftMask,      "\033[Z",        0,    0},
-	{ XK_Return,        Mod1Mask,       "\033\r",        0,    0},
-	{ XK_Return,        XK_ANY_MOD,     "\r",            0,    0},
-	{ XK_Insert,        ShiftMask,      "\033[4l",      -1,    0},
-	{ XK_Insert,        ShiftMask,      "\033[2;2~",    +1,    0},
-	{ XK_Insert,        ControlMask,    "\033[L",       -1,    0},
-	{ XK_Insert,        ControlMask,    "\033[2;5~",    +1,    0},
-	{ XK_Insert,        XK_ANY_MOD,     "\033[4h",      -1,    0},
-	{ XK_Insert,        XK_ANY_MOD,     "\033[2~",      +1,    0},
-	{ XK_Delete,        ControlMask,    "\033[M",       -1,    0},
-	{ XK_Delete,        ControlMask,    "\033[3;5~",    +1,    0},
-	{ XK_Delete,        ShiftMask,      "\033[2K",      -1,    0},
-	{ XK_Delete,        ShiftMask,      "\033[3;2~",    +1,    0},
-	{ XK_Delete,        XK_ANY_MOD,     "\033[P",       -1,    0},
-	{ XK_Delete,        XK_ANY_MOD,     "\033[3~",      +1,    0},
-	{ XK_BackSpace,     XK_NO_MOD,      "\177",          0,    0},
-	{ XK_BackSpace,     Mod1Mask,       "\033\177",      0,    0},
-	{ XK_Home,          ShiftMask,      "\033[2J",       0,   -1},
-	{ XK_Home,          ShiftMask,      "\033[1;2H",     0,   +1},
-	{ XK_Home,          XK_ANY_MOD,     "\033[H",        0,   -1},
-	{ XK_Home,          XK_ANY_MOD,     "\033[1~",       0,   +1},
-	{ XK_End,           ControlMask,    "\033[J",       -1,    0},
-	{ XK_End,           ControlMask,    "\033[1;5F",    +1,    0},
-	{ XK_End,           ShiftMask,      "\033[K",       -1,    0},
-	{ XK_End,           ShiftMask,      "\033[1;2F",    +1,    0},
-	{ XK_End,           XK_ANY_MOD,     "\033[4~",       0,    0},
-	{ XK_Prior,         ControlMask,    "\033[5;5~",     0,    0},
-	{ XK_Prior,         ShiftMask,      "\033[5;2~",     0,    0},
-	{ XK_Prior,         XK_ANY_MOD,     "\033[5~",       0,    0},
-	{ XK_Next,          ControlMask,    "\033[6;5~",     0,    0},
-	{ XK_Next,          ShiftMask,      "\033[6;2~",     0,    0},
-	{ XK_Next,          XK_ANY_MOD,     "\033[6~",       0,    0},
-	{ XK_F1,            XK_NO_MOD,      "\033OP" ,       0,    0},
-	{ XK_F1, /* F13 */  ShiftMask,      "\033[1;2P",     0,    0},
-	{ XK_F1, /* F25 */  ControlMask,    "\033[1;5P",     0,    0},
-	{ XK_F1, /* F37 */  Mod4Mask,       "\033[1;6P",     0,    0},
-	{ XK_F1, /* F49 */  Mod1Mask,       "\033[1;3P",     0,    0},
-	{ XK_F1, /* F61 */  Mod3Mask,       "\033[1;4P",     0,    0},
-	{ XK_F2,            XK_NO_MOD,      "\033OQ" ,       0,    0},
-	{ XK_F2, /* F14 */  ShiftMask,      "\033[1;2Q",     0,    0},
-	{ XK_F2, /* F26 */  ControlMask,    "\033[1;5Q",     0,    0},
-	{ XK_F2, /* F38 */  Mod4Mask,       "\033[1;6Q",     0,    0},
-	{ XK_F2, /* F50 */  Mod1Mask,       "\033[1;3Q",     0,    0},
-	{ XK_F2, /* F62 */  Mod3Mask,       "\033[1;4Q",     0,    0},
-	{ XK_F3,            XK_NO_MOD,      "\033OR" ,       0,    0},
-	{ XK_F3, /* F15 */  ShiftMask,      "\033[1;2R",     0,    0},
-	{ XK_F3, /* F27 */  ControlMask,    "\033[1;5R",     0,    0},
-	{ XK_F3, /* F39 */  Mod4Mask,       "\033[1;6R",     0,    0},
-	{ XK_F3, /* F51 */  Mod1Mask,       "\033[1;3R",     0,    0},
-	{ XK_F3, /* F63 */  Mod3Mask,       "\033[1;4R",     0,    0},
-	{ XK_F4,            XK_NO_MOD,      "\033OS" ,       0,    0},
-	{ XK_F4, /* F16 */  ShiftMask,      "\033[1;2S",     0,    0},
-	{ XK_F4, /* F28 */  ControlMask,    "\033[1;5S",     0,    0},
-	{ XK_F4, /* F40 */  Mod4Mask,       "\033[1;6S",     0,    0},
-	{ XK_F4, /* F52 */  Mod1Mask,       "\033[1;3S",     0,    0},
-	{ XK_F5,            XK_NO_MOD,      "\033[15~",      0,    0},
-	{ XK_F5, /* F17 */  ShiftMask,      "\033[15;2~",    0,    0},
-	{ XK_F5, /* F29 */  ControlMask,    "\033[15;5~",    0,    0},
-	{ XK_F5, /* F41 */  Mod4Mask,       "\033[15;6~",    0,    0},
-	{ XK_F5, /* F53 */  Mod1Mask,       "\033[15;3~",    0,    0},
-	{ XK_F6,            XK_NO_MOD,      "\033[17~",      0,    0},
-	{ XK_F6, /* F18 */  ShiftMask,      "\033[17;2~",    0,    0},
-	{ XK_F6, /* F30 */  ControlMask,    "\033[17;5~",    0,    0},
-	{ XK_F6, /* F42 */  Mod4Mask,       "\033[17;6~",    0,    0},
-	{ XK_F6, /* F54 */  Mod1Mask,       "\033[17;3~",    0,    0},
-	{ XK_F7,            XK_NO_MOD,      "\033[18~",      0,    0},
-	{ XK_F7, /* F19 */  ShiftMask,      "\033[18;2~",    0,    0},
-	{ XK_F7, /* F31 */  ControlMask,    "\033[18;5~",    0,    0},
-	{ XK_F7, /* F43 */  Mod4Mask,       "\033[18;6~",    0,    0},
-	{ XK_F7, /* F55 */  Mod1Mask,       "\033[18;3~",    0,    0},
-	{ XK_F8,            XK_NO_MOD,      "\033[19~",      0,    0},
-	{ XK_F8, /* F20 */  ShiftMask,      "\033[19;2~",    0,    0},
-	{ XK_F8, /* F32 */  ControlMask,    "\033[19;5~",    0,    0},
-	{ XK_F8, /* F44 */  Mod4Mask,       "\033[19;6~",    0,    0},
-	{ XK_F8, /* F56 */  Mod1Mask,       "\033[19;3~",    0,    0},
-	{ XK_F9,            XK_NO_MOD,      "\033[20~",      0,    0},
-	{ XK_F9, /* F21 */  ShiftMask,      "\033[20;2~",    0,    0},
-	{ XK_F9, /* F33 */  ControlMask,    "\033[20;5~",    0,    0},
-	{ XK_F9, /* F45 */  Mod4Mask,       "\033[20;6~",    0,    0},
-	{ XK_F9, /* F57 */  Mod1Mask,       "\033[20;3~",    0,    0},
-	{ XK_F10,           XK_NO_MOD,      "\033[21~",      0,    0},
-	{ XK_F10, /* F22 */ ShiftMask,      "\033[21;2~",    0,    0},
-	{ XK_F10, /* F34 */ ControlMask,    "\033[21;5~",    0,    0},
-	{ XK_F10, /* F46 */ Mod4Mask,       "\033[21;6~",    0,    0},
-	{ XK_F10, /* F58 */ Mod1Mask,       "\033[21;3~",    0,    0},
-	{ XK_F11,           XK_NO_MOD,      "\033[23~",      0,    0},
-	{ XK_F11, /* F23 */ ShiftMask,      "\033[23;2~",    0,    0},
-	{ XK_F11, /* F35 */ ControlMask,    "\033[23;5~",    0,    0},
-	{ XK_F11, /* F47 */ Mod4Mask,       "\033[23;6~",    0,    0},
-	{ XK_F11, /* F59 */ Mod1Mask,       "\033[23;3~",    0,    0},
-	{ XK_F12,           XK_NO_MOD,      "\033[24~",      0,    0},
-	{ XK_F12, /* F24 */ ShiftMask,      "\033[24;2~",    0,    0},
-	{ XK_F12, /* F36 */ ControlMask,    "\033[24;5~",    0,    0},
-	{ XK_F12, /* F48 */ Mod4Mask,       "\033[24;6~",    0,    0},
-	{ XK_F12, /* F60 */ Mod1Mask,       "\033[24;3~",    0,    0},
-	{ XK_F13,           XK_NO_MOD,      "\033[1;2P",     0,    0},
-	{ XK_F14,           XK_NO_MOD,      "\033[1;2Q",     0,    0},
-	{ XK_F15,           XK_NO_MOD,      "\033[1;2R",     0,    0},
-	{ XK_F16,           XK_NO_MOD,      "\033[1;2S",     0,    0},
-	{ XK_F17,           XK_NO_MOD,      "\033[15;2~",    0,    0},
-	{ XK_F18,           XK_NO_MOD,      "\033[17;2~",    0,    0},
-	{ XK_F19,           XK_NO_MOD,      "\033[18;2~",    0,    0},
-	{ XK_F20,           XK_NO_MOD,      "\033[19;2~",    0,    0},
-	{ XK_F21,           XK_NO_MOD,      "\033[20;2~",    0,    0},
-	{ XK_F22,           XK_NO_MOD,      "\033[21;2~",    0,    0},
-	{ XK_F23,           XK_NO_MOD,      "\033[23;2~",    0,    0},
-	{ XK_F24,           XK_NO_MOD,      "\033[24;2~",    0,    0},
-	{ XK_F25,           XK_NO_MOD,      "\033[1;5P",     0,    0},
-	{ XK_F26,           XK_NO_MOD,      "\033[1;5Q",     0,    0},
-	{ XK_F27,           XK_NO_MOD,      "\033[1;5R",     0,    0},
-	{ XK_F28,           XK_NO_MOD,      "\033[1;5S",     0,    0},
-	{ XK_F29,           XK_NO_MOD,      "\033[15;5~",    0,    0},
-	{ XK_F30,           XK_NO_MOD,      "\033[17;5~",    0,    0},
-	{ XK_F31,           XK_NO_MOD,      "\033[18;5~",    0,    0},
-	{ XK_F32,           XK_NO_MOD,      "\033[19;5~",    0,    0},
-	{ XK_F33,           XK_NO_MOD,      "\033[20;5~",    0,    0},
-	{ XK_F34,           XK_NO_MOD,      "\033[21;5~",    0,    0},
-	{ XK_F35,           XK_NO_MOD,      "\033[23;5~",    0,    0},
+	{ XKB_KEY_KP_Home,       ShiftMask,      "\033[2J",       0,   -1},
+	{ XKB_KEY_KP_Home,       ShiftMask,      "\033[1;2H",     0,   +1},
+	{ XKB_KEY_KP_Home,       MOD_ANY,     "\033[H",        0,   -1},
+	{ XKB_KEY_KP_Home,       MOD_ANY,     "\033[1~",       0,   +1},
+	{ XKB_KEY_KP_Up,         MOD_ANY,     "\033Ox",       +1,    0},
+	{ XKB_KEY_KP_Up,         MOD_ANY,     "\033[A",        0,   -1},
+	{ XKB_KEY_KP_Up,         MOD_ANY,     "\033OA",        0,   +1},
+	{ XKB_KEY_KP_Down,       MOD_ANY,     "\033Or",       +1,    0},
+	{ XKB_KEY_KP_Down,       MOD_ANY,     "\033[B",        0,   -1},
+	{ XKB_KEY_KP_Down,       MOD_ANY,     "\033OB",        0,   +1},
+	{ XKB_KEY_KP_Left,       MOD_ANY,     "\033Ot",       +1,    0},
+	{ XKB_KEY_KP_Left,       MOD_ANY,     "\033[D",        0,   -1},
+	{ XKB_KEY_KP_Left,       MOD_ANY,     "\033OD",        0,   +1},
+	{ XKB_KEY_KP_Right,      MOD_ANY,     "\033Ov",       +1,    0},
+	{ XKB_KEY_KP_Right,      MOD_ANY,     "\033[C",        0,   -1},
+	{ XKB_KEY_KP_Right,      MOD_ANY,     "\033OC",        0,   +1},
+	{ XKB_KEY_KP_Prior,      ShiftMask,      "\033[5;2~",     0,    0},
+	{ XKB_KEY_KP_Prior,      MOD_ANY,     "\033[5~",       0,    0},
+	{ XKB_KEY_KP_Begin,      MOD_ANY,     "\033[E",        0,    0},
+	{ XKB_KEY_KP_End,        ControlMask,    "\033[J",       -1,    0},
+	{ XKB_KEY_KP_End,        ControlMask,    "\033[1;5F",    +1,    0},
+	{ XKB_KEY_KP_End,        ShiftMask,      "\033[K",       -1,    0},
+	{ XKB_KEY_KP_End,        ShiftMask,      "\033[1;2F",    +1,    0},
+	{ XKB_KEY_KP_End,        MOD_ANY,     "\033[4~",       0,    0},
+	{ XKB_KEY_KP_Next,       ShiftMask,      "\033[6;2~",     0,    0},
+	{ XKB_KEY_KP_Next,       MOD_ANY,     "\033[6~",       0,    0},
+	{ XKB_KEY_KP_Insert,     ShiftMask,      "\033[2;2~",    +1,    0},
+	{ XKB_KEY_KP_Insert,     ShiftMask,      "\033[4l",      -1,    0},
+	{ XKB_KEY_KP_Insert,     ControlMask,    "\033[L",       -1,    0},
+	{ XKB_KEY_KP_Insert,     ControlMask,    "\033[2;5~",    +1,    0},
+	{ XKB_KEY_KP_Insert,     MOD_ANY,     "\033[4h",      -1,    0},
+	{ XKB_KEY_KP_Insert,     MOD_ANY,     "\033[2~",      +1,    0},
+	{ XKB_KEY_KP_Delete,     ControlMask,    "\033[M",       -1,    0},
+	{ XKB_KEY_KP_Delete,     ControlMask,    "\033[3;5~",    +1,    0},
+	{ XKB_KEY_KP_Delete,     ShiftMask,      "\033[2K",      -1,    0},
+	{ XKB_KEY_KP_Delete,     ShiftMask,      "\033[3;2~",    +1,    0},
+	{ XKB_KEY_KP_Delete,     MOD_ANY,     "\033[P",       -1,    0},
+	{ XKB_KEY_KP_Delete,     MOD_ANY,     "\033[3~",      +1,    0},
+	{ XKB_KEY_KP_Multiply,   MOD_ANY,     "\033Oj",       +2,    0},
+	{ XKB_KEY_KP_Add,        MOD_ANY,     "\033Ok",       +2,    0},
+	{ XKB_KEY_KP_Enter,      MOD_ANY,     "\033OM",       +2,    0},
+	{ XKB_KEY_KP_Enter,      MOD_ANY,     "\r",           -1,    0},
+	{ XKB_KEY_KP_Subtract,   MOD_ANY,     "\033Om",       +2,    0},
+	{ XKB_KEY_KP_Decimal,    MOD_ANY,     "\033On",       +2,    0},
+	{ XKB_KEY_KP_Divide,     MOD_ANY,     "\033Oo",       +2,    0},
+	{ XKB_KEY_KP_0,          MOD_ANY,     "\033Op",       +2,    0},
+	{ XKB_KEY_KP_1,          MOD_ANY,     "\033Oq",       +2,    0},
+	{ XKB_KEY_KP_2,          MOD_ANY,     "\033Or",       +2,    0},
+	{ XKB_KEY_KP_3,          MOD_ANY,     "\033Os",       +2,    0},
+	{ XKB_KEY_KP_4,          MOD_ANY,     "\033Ot",       +2,    0},
+	{ XKB_KEY_KP_5,          MOD_ANY,     "\033Ou",       +2,    0},
+	{ XKB_KEY_KP_6,          MOD_ANY,     "\033Ov",       +2,    0},
+	{ XKB_KEY_KP_7,          MOD_ANY,     "\033Ow",       +2,    0},
+	{ XKB_KEY_KP_8,          MOD_ANY,     "\033Ox",       +2,    0},
+	{ XKB_KEY_KP_9,          MOD_ANY,     "\033Oy",       +2,    0},
+	{ XKB_KEY_Up,            ShiftMask,      "\033[1;2A",     0,    0},
+	{ XKB_KEY_Up,            Mod1Mask,       "\033[1;3A",     0,    0},
+	{ XKB_KEY_Up,         ShiftMask|Mod1Mask,"\033[1;4A",     0,    0},
+	{ XKB_KEY_Up,            ControlMask,    "\033[1;5A",     0,    0},
+	{ XKB_KEY_Up,      ShiftMask|ControlMask,"\033[1;6A",     0,    0},
+	{ XKB_KEY_Up,       ControlMask|Mod1Mask,"\033[1;7A",     0,    0},
+	{ XKB_KEY_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A",  0,    0},
+	{ XKB_KEY_Up,            MOD_ANY,     "\033[A",        0,   -1},
+	{ XKB_KEY_Up,            MOD_ANY,     "\033OA",        0,   +1},
+	{ XKB_KEY_Down,          ShiftMask,      "\033[1;2B",     0,    0},
+	{ XKB_KEY_Down,          Mod1Mask,       "\033[1;3B",     0,    0},
+	{ XKB_KEY_Down,       ShiftMask|Mod1Mask,"\033[1;4B",     0,    0},
+	{ XKB_KEY_Down,          ControlMask,    "\033[1;5B",     0,    0},
+	{ XKB_KEY_Down,    ShiftMask|ControlMask,"\033[1;6B",     0,    0},
+	{ XKB_KEY_Down,     ControlMask|Mod1Mask,"\033[1;7B",     0,    0},
+	{ XKB_KEY_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0,    0},
+	{ XKB_KEY_Down,          MOD_ANY,     "\033[B",        0,   -1},
+	{ XKB_KEY_Down,          MOD_ANY,     "\033OB",        0,   +1},
+	{ XKB_KEY_Left,          ShiftMask,      "\033[1;2D",     0,    0},
+	{ XKB_KEY_Left,          Mod1Mask,       "\033[1;3D",     0,    0},
+	{ XKB_KEY_Left,       ShiftMask|Mod1Mask,"\033[1;4D",     0,    0},
+	{ XKB_KEY_Left,          ControlMask,    "\033[1;5D",     0,    0},
+	{ XKB_KEY_Left,    ShiftMask|ControlMask,"\033[1;6D",     0,    0},
+	{ XKB_KEY_Left,     ControlMask|Mod1Mask,"\033[1;7D",     0,    0},
+	{ XKB_KEY_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0,    0},
+	{ XKB_KEY_Left,          MOD_ANY,     "\033[D",        0,   -1},
+	{ XKB_KEY_Left,          MOD_ANY,     "\033OD",        0,   +1},
+	{ XKB_KEY_Right,         ShiftMask,      "\033[1;2C",     0,    0},
+	{ XKB_KEY_Right,         Mod1Mask,       "\033[1;3C",     0,    0},
+	{ XKB_KEY_Right,      ShiftMask|Mod1Mask,"\033[1;4C",     0,    0},
+	{ XKB_KEY_Right,         ControlMask,    "\033[1;5C",     0,    0},
+	{ XKB_KEY_Right,   ShiftMask|ControlMask,"\033[1;6C",     0,    0},
+	{ XKB_KEY_Right,    ControlMask|Mod1Mask,"\033[1;7C",     0,    0},
+	{ XKB_KEY_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0,   0},
+	{ XKB_KEY_Right,         MOD_ANY,     "\033[C",        0,   -1},
+	{ XKB_KEY_Right,         MOD_ANY,     "\033OC",        0,   +1},
+	{ XKB_KEY_ISO_Left_Tab,  ShiftMask,      "\033[Z",        0,    0},
+	{ XKB_KEY_Return,        Mod1Mask,       "\033\r",        0,    0},
+	{ XKB_KEY_Return,        MOD_ANY,     "\r",            0,    0},
+	{ XKB_KEY_Insert,        ShiftMask,      "\033[4l",      -1,    0},
+	{ XKB_KEY_Insert,        ShiftMask,      "\033[2;2~",    +1,    0},
+	{ XKB_KEY_Insert,        ControlMask,    "\033[L",       -1,    0},
+	{ XKB_KEY_Insert,        ControlMask,    "\033[2;5~",    +1,    0},
+	{ XKB_KEY_Insert,        MOD_ANY,     "\033[4h",      -1,    0},
+	{ XKB_KEY_Insert,        MOD_ANY,     "\033[2~",      +1,    0},
+	{ XKB_KEY_Delete,        ControlMask,    "\033[M",       -1,    0},
+	{ XKB_KEY_Delete,        ControlMask,    "\033[3;5~",    +1,    0},
+	{ XKB_KEY_Delete,        ShiftMask,      "\033[2K",      -1,    0},
+	{ XKB_KEY_Delete,        ShiftMask,      "\033[3;2~",    +1,    0},
+	{ XKB_KEY_Delete,        MOD_ANY,     "\033[P",       -1,    0},
+	{ XKB_KEY_Delete,        MOD_ANY,     "\033[3~",      +1,    0},
+	{ XKB_KEY_BackSpace,     MOD_NONE,      "\177",          0,    0},
+	{ XKB_KEY_BackSpace,     Mod1Mask,       "\033\177",      0,    0},
+	{ XKB_KEY_Home,          ShiftMask,      "\033[2J",       0,   -1},
+	{ XKB_KEY_Home,          ShiftMask,      "\033[1;2H",     0,   +1},
+	{ XKB_KEY_Home,          MOD_ANY,     "\033[H",        0,   -1},
+	{ XKB_KEY_Home,          MOD_ANY,     "\033[1~",       0,   +1},
+	{ XKB_KEY_End,           ControlMask,    "\033[J",       -1,    0},
+	{ XKB_KEY_End,           ControlMask,    "\033[1;5F",    +1,    0},
+	{ XKB_KEY_End,           ShiftMask,      "\033[K",       -1,    0},
+	{ XKB_KEY_End,           ShiftMask,      "\033[1;2F",    +1,    0},
+	{ XKB_KEY_End,           MOD_ANY,     "\033[4~",       0,    0},
+	{ XKB_KEY_Prior,         ControlMask,    "\033[5;5~",     0,    0},
+	{ XKB_KEY_Prior,         ShiftMask,      "\033[5;2~",     0,    0},
+	{ XKB_KEY_Prior,         MOD_ANY,     "\033[5~",       0,    0},
+	{ XKB_KEY_Next,          ControlMask,    "\033[6;5~",     0,    0},
+	{ XKB_KEY_Next,          ShiftMask,      "\033[6;2~",     0,    0},
+	{ XKB_KEY_Next,          MOD_ANY,     "\033[6~",       0,    0},
+	{ XKB_KEY_F1,            MOD_NONE,      "\033OP" ,       0,    0},
+	{ XKB_KEY_F1, /* F13 */  ShiftMask,      "\033[1;2P",     0,    0},
+	{ XKB_KEY_F1, /* F25 */  ControlMask,    "\033[1;5P",     0,    0},
+	{ XKB_KEY_F1, /* F37 */  Mod4Mask,       "\033[1;6P",     0,    0},
+	{ XKB_KEY_F1, /* F49 */  Mod1Mask,       "\033[1;3P",     0,    0},
+	{ XKB_KEY_F1, /* F61 */  Mod3Mask,       "\033[1;4P",     0,    0},
+	{ XKB_KEY_F2,            MOD_NONE,      "\033OQ" ,       0,    0},
+	{ XKB_KEY_F2, /* F14 */  ShiftMask,      "\033[1;2Q",     0,    0},
+	{ XKB_KEY_F2, /* F26 */  ControlMask,    "\033[1;5Q",     0,    0},
+	{ XKB_KEY_F2, /* F38 */  Mod4Mask,       "\033[1;6Q",     0,    0},
+	{ XKB_KEY_F2, /* F50 */  Mod1Mask,       "\033[1;3Q",     0,    0},
+	{ XKB_KEY_F2, /* F62 */  Mod3Mask,       "\033[1;4Q",     0,    0},
+	{ XKB_KEY_F3,            MOD_NONE,      "\033OR" ,       0,    0},
+	{ XKB_KEY_F3, /* F15 */  ShiftMask,      "\033[1;2R",     0,    0},
+	{ XKB_KEY_F3, /* F27 */  ControlMask,    "\033[1;5R",     0,    0},
+	{ XKB_KEY_F3, /* F39 */  Mod4Mask,       "\033[1;6R",     0,    0},
+	{ XKB_KEY_F3, /* F51 */  Mod1Mask,       "\033[1;3R",     0,    0},
+	{ XKB_KEY_F3, /* F63 */  Mod3Mask,       "\033[1;4R",     0,    0},
+	{ XKB_KEY_F4,            MOD_NONE,      "\033OS" ,       0,    0},
+	{ XKB_KEY_F4, /* F16 */  ShiftMask,      "\033[1;2S",     0,    0},
+	{ XKB_KEY_F4, /* F28 */  ControlMask,    "\033[1;5S",     0,    0},
+	{ XKB_KEY_F4, /* F40 */  Mod4Mask,       "\033[1;6S",     0,    0},
+	{ XKB_KEY_F4, /* F52 */  Mod1Mask,       "\033[1;3S",     0,    0},
+	{ XKB_KEY_F5,            MOD_NONE,      "\033[15~",      0,    0},
+	{ XKB_KEY_F5, /* F17 */  ShiftMask,      "\033[15;2~",    0,    0},
+	{ XKB_KEY_F5, /* F29 */  ControlMask,    "\033[15;5~",    0,    0},
+	{ XKB_KEY_F5, /* F41 */  Mod4Mask,       "\033[15;6~",    0,    0},
+	{ XKB_KEY_F5, /* F53 */  Mod1Mask,       "\033[15;3~",    0,    0},
+	{ XKB_KEY_F6,            MOD_NONE,      "\033[17~",      0,    0},
+	{ XKB_KEY_F6, /* F18 */  ShiftMask,      "\033[17;2~",    0,    0},
+	{ XKB_KEY_F6, /* F30 */  ControlMask,    "\033[17;5~",    0,    0},
+	{ XKB_KEY_F6, /* F42 */  Mod4Mask,       "\033[17;6~",    0,    0},
+	{ XKB_KEY_F6, /* F54 */  Mod1Mask,       "\033[17;3~",    0,    0},
+	{ XKB_KEY_F7,            MOD_NONE,      "\033[18~",      0,    0},
+	{ XKB_KEY_F7, /* F19 */  ShiftMask,      "\033[18;2~",    0,    0},
+	{ XKB_KEY_F7, /* F31 */  ControlMask,    "\033[18;5~",    0,    0},
+	{ XKB_KEY_F7, /* F43 */  Mod4Mask,       "\033[18;6~",    0,    0},
+	{ XKB_KEY_F7, /* F55 */  Mod1Mask,       "\033[18;3~",    0,    0},
+	{ XKB_KEY_F8,            MOD_NONE,      "\033[19~",      0,    0},
+	{ XKB_KEY_F8, /* F20 */  ShiftMask,      "\033[19;2~",    0,    0},
+	{ XKB_KEY_F8, /* F32 */  ControlMask,    "\033[19;5~",    0,    0},
+	{ XKB_KEY_F8, /* F44 */  Mod4Mask,       "\033[19;6~",    0,    0},
+	{ XKB_KEY_F8, /* F56 */  Mod1Mask,       "\033[19;3~",    0,    0},
+	{ XKB_KEY_F9,            MOD_NONE,      "\033[20~",      0,    0},
+	{ XKB_KEY_F9, /* F21 */  ShiftMask,      "\033[20;2~",    0,    0},
+	{ XKB_KEY_F9, /* F33 */  ControlMask,    "\033[20;5~",    0,    0},
+	{ XKB_KEY_F9, /* F45 */  Mod4Mask,       "\033[20;6~",    0,    0},
+	{ XKB_KEY_F9, /* F57 */  Mod1Mask,       "\033[20;3~",    0,    0},
+	{ XKB_KEY_F10,           MOD_NONE,      "\033[21~",      0,    0},
+	{ XKB_KEY_F10, /* F22 */ ShiftMask,      "\033[21;2~",    0,    0},
+	{ XKB_KEY_F10, /* F34 */ ControlMask,    "\033[21;5~",    0,    0},
+	{ XKB_KEY_F10, /* F46 */ Mod4Mask,       "\033[21;6~",    0,    0},
+	{ XKB_KEY_F10, /* F58 */ Mod1Mask,       "\033[21;3~",    0,    0},
+	{ XKB_KEY_F11,           MOD_NONE,      "\033[23~",      0,    0},
+	{ XKB_KEY_F11, /* F23 */ ShiftMask,      "\033[23;2~",    0,    0},
+	{ XKB_KEY_F11, /* F35 */ ControlMask,    "\033[23;5~",    0,    0},
+	{ XKB_KEY_F11, /* F47 */ Mod4Mask,       "\033[23;6~",    0,    0},
+	{ XKB_KEY_F11, /* F59 */ Mod1Mask,       "\033[23;3~",    0,    0},
+	{ XKB_KEY_F12,           MOD_NONE,      "\033[24~",      0,    0},
+	{ XKB_KEY_F12, /* F24 */ ShiftMask,      "\033[24;2~",    0,    0},
+	{ XKB_KEY_F12, /* F36 */ ControlMask,    "\033[24;5~",    0,    0},
+	{ XKB_KEY_F12, /* F48 */ Mod4Mask,       "\033[24;6~",    0,    0},
+	{ XKB_KEY_F12, /* F60 */ Mod1Mask,       "\033[24;3~",    0,    0},
+	{ XKB_KEY_F13,           MOD_NONE,      "\033[1;2P",     0,    0},
+	{ XKB_KEY_F14,           MOD_NONE,      "\033[1;2Q",     0,    0},
+	{ XKB_KEY_F15,           MOD_NONE,      "\033[1;2R",     0,    0},
+	{ XKB_KEY_F16,           MOD_NONE,      "\033[1;2S",     0,    0},
+	{ XKB_KEY_F17,           MOD_NONE,      "\033[15;2~",    0,    0},
+	{ XKB_KEY_F18,           MOD_NONE,      "\033[17;2~",    0,    0},
+	{ XKB_KEY_F19,           MOD_NONE,      "\033[18;2~",    0,    0},
+	{ XKB_KEY_F20,           MOD_NONE,      "\033[19;2~",    0,    0},
+	{ XKB_KEY_F21,           MOD_NONE,      "\033[20;2~",    0,    0},
+	{ XKB_KEY_F22,           MOD_NONE,      "\033[21;2~",    0,    0},
+	{ XKB_KEY_F23,           MOD_NONE,      "\033[23;2~",    0,    0},
+	{ XKB_KEY_F24,           MOD_NONE,      "\033[24;2~",    0,    0},
+	{ XKB_KEY_F25,           MOD_NONE,      "\033[1;5P",     0,    0},
+	{ XKB_KEY_F26,           MOD_NONE,      "\033[1;5Q",     0,    0},
+	{ XKB_KEY_F27,           MOD_NONE,      "\033[1;5R",     0,    0},
+	{ XKB_KEY_F28,           MOD_NONE,      "\033[1;5S",     0,    0},
+	{ XKB_KEY_F29,           MOD_NONE,      "\033[15;5~",    0,    0},
+	{ XKB_KEY_F30,           MOD_NONE,      "\033[17;5~",    0,    0},
+	{ XKB_KEY_F31,           MOD_NONE,      "\033[18;5~",    0,    0},
+	{ XKB_KEY_F32,           MOD_NONE,      "\033[19;5~",    0,    0},
+	{ XKB_KEY_F33,           MOD_NONE,      "\033[20;5~",    0,    0},
+	{ XKB_KEY_F34,           MOD_NONE,      "\033[21;5~",    0,    0},
+	{ XKB_KEY_F35,           MOD_NONE,      "\033[23;5~",    0,    0},
 };
 
 /*
diff --git a/config.mk b/config.mk
index 294614f..77930db 100644
--- a/config.mk
+++ b/config.mk
@@ -7,35 +7,39 @@ VERSION = 0.9.0.1
 PREFIX = /usr/local
 MANPREFIX = $(PREFIX)/share/man
 
-X11INC = /usr/X11R6/include
-X11LIB = /usr/X11R6/lib
-
 PKG_CONFIG = pkg-config
+WAYLAND_SCANNER = wayland-scanner
+
+# Wayland protocol XMLs (wayland-protocols).  pkg-config only reports a
+# system-wide install, so also look in the usual data dirs (per user and
+# under /usr/local).  Override with: make WLPROTO=/path/to/wayland-protocols
+WLPROTO_PC = $(shell $(PKG_CONFIG) --variable=pkgdatadir wayland-protocols 2>/dev/null)
+WLPROTO_USER = $(or $(XDG_DATA_HOME),$(HOME)/.local/share)/wayland-protocols
+WLPROTO_DIRS = $(WLPROTO_PC) $(WLPROTO_USER)
+WLPROTO_DIRS += /usr/local/share/wayland-protocols
+WLPROTO_DIRS += /usr/share/wayland-protocols
+WLPROTO ?= $(firstword $(foreach d,$(WLPROTO_DIRS),$(if $(wildcard $(d)/stable/xdg-shell/xdg-shell.xml),$(d))))
+XDG_SHELL_XML = $(WLPROTO)/stable/xdg-shell/xdg-shell.xml
+VIEWPORTER_XML = $(WLPROTO)/stable/viewporter/viewporter.xml
+FRACTIONAL_XML = $(WLPROTO)/staging/fractional-scale/fractional-scale-v1.xml
+CURSOR_SHAPE_XML = $(WLPROTO)/staging/cursor-shape/cursor-shape-v1.xml
+SYSTEM_BELL_XML = $(WLPROTO)/staging/xdg-system-bell/xdg-system-bell-v1.xml
+PRIMARY_XML = $(WLPROTO)/unstable/primary-selection/primary-selection-unstable-v1.xml
+TEXT_INPUT_XML = $(WLPROTO)/unstable/text-input/text-input-unstable-v3.xml
+# cursor-shape-v1 references zwp_tablet_tool_v2, so its interface must exist
+TABLET_XML = $(WLPROTO)/stable/tablet/tablet-v2.xml
 
 # 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) --libs fontconfig` \
-       `$(PKG_CONFIG) --libs freetype2` \
-       `$(PKG_CONFIG) --libs harfbuzz`
+INCS = `$(PKG_CONFIG) --cflags wayland-client xkbcommon fontconfig freetype2 harfbuzz`
+LIBS = -lm -lrt -lutil \
+       `$(PKG_CONFIG) --libs wayland-client xkbcommon fontconfig freetype2 harfbuzz`
 
 # flags
-#CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L ${XINERAMAFLAGS}
-CPUFLAGS = -march=native
-CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L
-CFLAGS   = -std=c99 -pedantic -Wall -Wno-deprecated-declarations ${CPUFLAGS} -Ofast --lto ${INCS} ${CPPFLAGS}
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -D_GNU_SOURCE
+CFLAGS   = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -O2
 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/hb.c b/hb.c
index 467bcac..63d6b28 100644
--- a/hb.c
+++ b/hb.c
@@ -1,17 +1,19 @@
+/* See LICENSE for license details. */
+
 #include <stdlib.h>
 #include <stdio.h>
-#include <math.h>
-#include <X11/Xft/Xft.h>
 #include <hb.h>
 #include <hb-ft.h>
 
 #include "st.h"
+#include "render.h"
+#include "hb.h"
 
-void hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length);
-hb_font_t *hbfindfont(XftFont *match);
+void hbtransformsegment(FT_Face, const Glyph *, hb_codepoint_t *, int, int);
+hb_font_t *hbfindfont(FT_Face);
 
 typedef struct {
-	XftFont *match;
+	FT_Face face;
 	hb_font_t *font;
 } HbFontMatch;
 
@@ -19,105 +21,120 @@ static int hbfontslen = 0;
 static HbFontMatch *hbfontcache = NULL;
 
 void
-hbunloadfonts()
+hbunloadfonts(void)
 {
-	for (int i = 0; i < hbfontslen; i++) {
+	int i;
+
+	for (i = 0; i < hbfontslen; i++)
 		hb_font_destroy(hbfontcache[i].font);
-		XftUnlockFace(hbfontcache[i].match);
-	}
 
-	if (hbfontcache != NULL) {
-		free(hbfontcache);
-		hbfontcache = NULL;
-	}
+	free(hbfontcache);
+	hbfontcache = NULL;
 	hbfontslen = 0;
 }
 
 hb_font_t *
-hbfindfont(XftFont *match)
+hbfindfont(FT_Face face)
 {
-	for (int i = 0; i < hbfontslen; i++) {
-		if (hbfontcache[i].match == match)
+	int i;
+	hb_font_t *font;
+
+	for (i = 0; i < hbfontslen; i++)
+		if (hbfontcache[i].face == face)
 			return hbfontcache[i].font;
-	}
 
-	/* Font not found in cache, caching it now. */
-	hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 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 = realloc(hbfontcache,
+	                      sizeof(HbFontMatch) * (hbfontslen + 1));
+	if (!hbfontcache)
+		die("realloc: failed to grow HarfBuzz font cache\n");
+
+	font = hb_ft_font_create(face, NULL);
+	if (!font)
+		die("Failed to load Harfbuzz font.\n");
 
-	hbfontcache[hbfontslen].match = match;
+	hbfontcache[hbfontslen].face = face;
 	hbfontcache[hbfontslen].font = font;
-	hbfontslen += 1;
+	hbfontslen++;
 
 	return font;
 }
 
 void
-hbtransform(XftGlyphFontSpec *specs, const Glyph *glyphs, size_t len, int x, int y)
+hbtransform(GlyphSpec *specs, const Glyph *glyphs, size_t len, int x, int y)
 {
 	int start = 0, length = 1, gstart = 0;
-	hb_codepoint_t *codepoints = calloc(len, sizeof(hb_codepoint_t));
+	hb_codepoint_t *codepoints;
+	int idx, specidx;
 
-	for (int idx = 1, specidx = 1; idx < len; idx++) {
+	codepoints = calloc(len, sizeof(hb_codepoint_t));
+	if (!codepoints)
+		die("calloc: failed to allocate HarfBuzz buffer\n");
+
+	for (idx = 1, specidx = 1; idx < (int)len; idx++) {
 		if (glyphs[idx].mode & ATTR_WDUMMY) {
-			length += 1;
+			length++;
 			continue;
 		}
 
-		if (specs[specidx].font != specs[start].font || ATTRCMP(glyphs[gstart], glyphs[idx]) || selected(x + idx, y) != selected(x + gstart, y)) {
-			hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length);
+		if (specs[specidx].font != specs[start].font ||
+		    ATTRCMP(glyphs[gstart], glyphs[idx]) ||
+		    selected(x + idx, y) != selected(x + gstart, y)) {
+			hbtransformsegment((FT_Face)specs[start].font, glyphs,
+			                   codepoints, gstart, length);
 
-			/* Reset the sequence. */
 			length = 1;
 			start = specidx;
 			gstart = idx;
 		} else {
-			length += 1;
+			length++;
 		}
 
 		specidx++;
 	}
 
 	/* EOL. */
-	hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length);
+	hbtransformsegment((FT_Face)specs[start].font, glyphs, codepoints,
+	                   gstart, length);
 
 	/* Apply the transformation to glyph specs. */
-	for (int i = 0, specidx = 0; i < len; i++) {
-		if (glyphs[i].mode & ATTR_WDUMMY)
+	for (idx = 0, specidx = 0; idx < (int)len; idx++) {
+		if (glyphs[idx].mode & ATTR_WDUMMY)
 			continue;
-		if (glyphs[i].mode & ATTR_BOXDRAW) {
+		if (glyphs[idx].mode & ATTR_BOXDRAW) {
 			specidx++;
 			continue;
 		}
 
-		if (codepoints[i] != specs[specidx].glyph)
-			((Glyph *)glyphs)[i].mode |= ATTR_LIGA;
+		if (codepoints[idx] != specs[specidx].glyph)
+			((Glyph *)glyphs)[idx].mode |= ATTR_LIGA;
 
-		specs[specidx++].glyph = codepoints[i];
+		specs[specidx++].glyph = codepoints[idx];
 	}
 
 	free(codepoints);
 }
 
 void
-hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length)
+hbtransformsegment(FT_Face face, const Glyph *string,
+                   hb_codepoint_t *codepoints, int start, int length)
 {
-	hb_font_t *font = hbfindfont(xfont);
+	hb_font_t *font = hbfindfont(face);
+	hb_buffer_t *buffer;
+	hb_glyph_info_t *info;
+	unsigned int count, i;
+	Rune rune;
+	ushort mode;
+
 	if (font == NULL)
 		return;
 
-	Rune rune;
-	ushort mode = USHRT_MAX;
-	hb_buffer_t *buffer = hb_buffer_create();
+	buffer = hb_buffer_create();
 	hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
 
 	/* Fill buffer with codepoints. */
-	for (int i = start; i < (start+length); i++) {
-		rune = string[i].u;
-		mode = string[i].mode;
+	for (i = 0; i < (unsigned)length; i++) {
+		rune = string[start + i].u;
+		mode = string[start + i].mode;
 		if (mode & ATTR_WDUMMY)
 			rune = 0x0020;
 		hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1);
@@ -127,14 +144,12 @@ hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoin
 	hb_shape(font, buffer, NULL, 0);
 
 	/* Get new glyph info. */
-	hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, NULL);
+	info = hb_buffer_get_glyph_infos(buffer, &count);
 
-	/* Write new codepoints. */
-	for (int i = 0; i < length; i++) {
-		hb_codepoint_t gid = info[i].codepoint;
-		codepoints[start+i] = gid;
-	}
+	/* Write new codepoints.  A ligature can collapse several cells into
+	 * one glyph; leave the remaining cells with glyph 0, which wl.c skips. */
+	for (i = 0; i < (unsigned)length; i++)
+		codepoints[start + i] = i < count ? info[i].codepoint : 0;
 
-	/* Cleanup. */
 	hb_buffer_destroy(buffer);
 }
diff --git a/hb.h b/hb.h
index b3e02d0..ae96ec9 100644
--- a/hb.h
+++ b/hb.h
@@ -1,7 +1,4 @@
-#include <X11/Xft/Xft.h>
-#include <hb.h>
-#include <hb-ft.h>
-
-void hbunloadfonts();
-void hbtransform(XftGlyphFontSpec *, const Glyph *, size_t, int, int);
+#include "render.h"
 
+void hbunloadfonts(void);
+void hbtransform(GlyphSpec *, const Glyph *, size_t, int, int);
diff --git a/render.h b/render.h
new file mode 100644
index 0000000..5edf6ae
--- /dev/null
+++ b/render.h
@@ -0,0 +1,28 @@
+/* See LICENSE for license details. */
+
+#ifndef RENDER_H
+#define RENDER_H
+
+#include <stdint.h>
+#include "st.h"
+
+/* A glyph ready to be rasterized.  font is an FT_Face owned by wl.c. */
+typedef struct {
+	void *font;
+	uint32_t glyph;
+	short x;	/* buffer pixel, left edge of the cell run */
+	short y;	/* buffer pixel, baseline */
+} GlyphSpec;
+
+/* Replace a rectangle (used for backgrounds; alpha is preserved). */
+void clearrect(int, int, int, int, uint32_t);
+
+/* Alpha-blend a rectangle (text decorations, box drawing, cursor). */
+void blendrect(int, int, int, int, uint32_t);
+
+int isboxdraw(Rune);
+ushort boxdrawindex(const Glyph *);
+void drawboxes(int, int, int, int, uint32_t, uint32_t,
+               const GlyphSpec *, int);
+
+#endif
diff --git a/st.1 b/st.1
index 39120b4..ff6fd79 100644
--- a/st.1
+++ b/st.1
@@ -4,14 +4,14 @@ st \- simple terminal
 .SH SYNOPSIS
 .B st
 .RB [ \-aiv ]
+.RB [ \-A
+.IR alpha ]
 .RB [ \-c
 .IR class ]
 .RB [ \-f
 .IR font ]
 .RB [ \-g
 .IR geometry ]
-.RB [ \-n
-.IR name ]
 .RB [ \-o
 .IR iofile ]
 .RB [ \-T
@@ -20,65 +20,69 @@ st \- simple terminal
 .IR title ]
 .RB [ \-l
 .IR line ]
-.RB [ \-w
-.IR windowid ]
 .RB [[ \-e ]
 .IR command
 .RI [ arguments ...]]
 .PP
 .B st
 .RB [ \-aiv ]
+.RB [ \-A
+.IR alpha ]
 .RB [ \-c
 .IR class ]
 .RB [ \-f
 .IR font ]
 .RB [ \-g
 .IR geometry ]
-.RB [ \-n
-.IR name ]
 .RB [ \-o
 .IR iofile ]
 .RB [ \-T
 .IR title ]
 .RB [ \-t
 .IR title ]
-.RB [ \-w
-.IR windowid ]
 .RB \-l
 .IR line
 .RI [ stty_args ...]
 .SH DESCRIPTION
 .B st
-is a simple terminal emulator.
+is a simple terminal emulator for Wayland.
+It uses xdg-shell and wl_shm directly; there is no X11 backend.
 .SH OPTIONS
 .TP
 .B \-a
 disable alternate screens in terminal
 .TP
+.BI \-A " alpha"
+set the opacity of the terminal background (0.0 to 1.0).
+This overrides the
+.I alpha
+value from config.h.
+.TP
 .BI \-c " class"
-defines the window class (default $TERM).
+defines the application id (default $TERM).
 .TP
 .BI \-f " font"
 defines the
 .I font
-to use when st is run.
+to use when st is run (a fontconfig pattern).
 .TP
 .BI \-g " geometry"
-defines the X11 geometry string.
-The form is [=][<cols>{xX}<rows>][{+-}<xoffset>{+-}<yoffset>]. See
-.BR XParseGeometry (3)
-for further details.
+sets the initial size of the terminal in columns and rows.
+The form is [<cols>][x<rows>].
+Wayland does not allow clients to position their windows, so any
+.I xoffset
+or
+.I yoffset
+is ignored with a warning.
 .TP
 .B \-i
-will fixate the position given with the -g option.
-.TP
-.BI \-n " name"
-defines the window instance name (default $TERM).
+fix the window to its initial size (the compositor is asked to keep the
+minimum and maximum size equal).
 .TP
 .BI \-o " iofile"
 writes all the I/O to
-.I iofile.
-This feature is useful when recording st sessions. A value of "-" means
+.IR iofile .
+This feature is useful when recording st sessions.  A value of "-" means
 standard output.
 .TP
 .BI \-T " title"
@@ -87,10 +91,6 @@ defines the window title (default 'st').
 .BI \-t " title"
 defines the window title (default 'st').
 .TP
-.BI \-w " windowid"
-embeds st within the window identified by
-.I windowid
-.TP
 .BI \-l " line"
 use a tty
 .I line
@@ -100,16 +100,12 @@ should be a (pseudo-)serial device (e.g. /dev/ttyS0 on Linux for serial port
 0).
 When this flag is given
 remaining arguments are used as flags for
-.BR stty(1).
+.BR stty (1).
 By default st initializes the serial line to 8 bits, no parity, 1 stop bit
-and a 38400 baud rate. The speed is set by appending it as last argument
-(e.g. 'st -l /dev/ttyS0 115200'). Arguments before the last one are
-.BR stty(1)
-flags. If you want to set odd parity on 115200 baud use for example 'st -l
-/dev/ttyS0 parenb parodd 115200'. Set the number of bits by using for
-example 'st -l /dev/ttyS0 cs7 115200'. See
-.BR stty(1)
-for more arguments and cases.
+and a 38400 baud rate.  The speed is set by appending it as last argument
+(e.g. 'st -l /dev/ttyS0 115200').  Arguments before the last one are
+.BR stty (1)
+flags.
 .TP
 .B \-v
 prints version information to stderr, then exits.
@@ -127,8 +123,6 @@ even without it.
 .TP
 .B Break
 Send a break in the serial line.
-Break key is obtained in PC keyboards
-pressing at the same time control and pause.
 .TP
 .B Ctrl-Print Screen
 Toggle if st should print to the
@@ -142,36 +136,39 @@ Print the full screen to the
 Print the selection to the
 .I iofile.
 .TP
-.B Ctrl-Shift-Page Up
+.B Ctrl-=
 Increase font size.
 .TP
-.B Ctrl-Shift-Page Down
+.B Ctrl-Minus
 Decrease font size.
 .TP
 .B Ctrl-Shift-Home
-Reset to default font size.
-.TP
-.B Ctrl-Shift-y
-Paste from primary selection (middle mouse button).
+Reset to the default font size.
 .TP
 .B Ctrl-Shift-c
 Copy the selected text to the clipboard selection.
 .TP
 .B Ctrl-Shift-v
 Paste from the clipboard selection.
+.TP
+.B Ctrl-Shift-y
+Paste from the primary selection (middle mouse button).
+.TP
+.B Shift-Insert
+Paste from the primary selection.
+.TP
+.B Ctrl-Shift-s
+Toggle keyboard selection mode.
 .SH CUSTOMIZATION
 .B st
-can be customized by creating a custom config.h and (re)compiling the source
-code. This keeps it fast, secure and simple.
+is customized by editing config.h and recompiling the source code.
+This keeps it fast, secure and simple.
 .SH AUTHORS
 See the LICENSE file for the authors.
 .SH LICENSE
 See the LICENSE file for the terms of redistribution.
 .SH SEE ALSO
-.BR tabbed (1),
-.BR utmp (1),
 .BR stty (1),
 .BR scroll (1)
 .SH BUGS
 See the TODO file in the distribution.
-
diff --git a/st.c b/st.c
index 487319a..bb29a8a 100644
--- a/st.c
+++ b/st.c
@@ -16,8 +16,6 @@
 #include <termios.h>
 #include <unistd.h>
 #include <wchar.h>
-#include <X11/keysym.h>
-#include <X11/X.h>
 
 #include "st.h"
 #include "win.h"
@@ -202,7 +200,7 @@ static void tsetscroll(int, int);
 static void tswapscreen(void);
 static void tsetmode(int, int, int *, int);
 static int twrite(const char *, int, int);
-static void tfulldirt(void);
+void tfulldirt(void);
 static void tcontrolcode(uchar );
 static void tdectest(char );
 static void tdefutf8(char);
@@ -1541,10 +1539,10 @@ tsetmode(int priv, int set, int *args, int narg)
 		if (priv) {
 			switch (*args) {
 			case 1: /* DECCKM -- Cursor key */
-				xsetmode(set, MODE_APPCURSOR);
+				setmode(set, MODE_APPCURSOR);
 				break;
 			case 5: /* DECSCNM -- Reverse video */
-				xsetmode(set, MODE_REVERSE);
+				setmode(set, MODE_REVERSE);
 				break;
 			case 6: /* DECOM -- Origin */
 				MODBIT(term.c.state, set, CURSOR_ORIGIN);
@@ -1564,36 +1562,36 @@ tsetmode(int priv, int set, int *args, int narg)
 			case 12: /* att610 -- Start blinking cursor (IGNORED) */
 				break;
 			case 25: /* DECTCEM -- Text Cursor Enable Mode */
-				xsetmode(!set, MODE_HIDE);
+				setmode(!set, MODE_HIDE);
 				break;
 			case 9:    /* X10 mouse compatibility mode */
-				xsetpointermotion(0);
-				xsetmode(0, MODE_MOUSE);
-				xsetmode(set, MODE_MOUSEX10);
+				setpointermotion(0);
+				setmode(0, MODE_MOUSE);
+				setmode(set, MODE_MOUSEX10);
 				break;
 			case 1000: /* 1000: report button press */
-				xsetpointermotion(0);
-				xsetmode(0, MODE_MOUSE);
-				xsetmode(set, MODE_MOUSEBTN);
+				setpointermotion(0);
+				setmode(0, MODE_MOUSE);
+				setmode(set, MODE_MOUSEBTN);
 				break;
 			case 1002: /* 1002: report motion on button press */
-				xsetpointermotion(0);
-				xsetmode(0, MODE_MOUSE);
-				xsetmode(set, MODE_MOUSEMOTION);
+				setpointermotion(0);
+				setmode(0, MODE_MOUSE);
+				setmode(set, MODE_MOUSEMOTION);
 				break;
 			case 1003: /* 1003: enable all mouse motions */
-				xsetpointermotion(set);
-				xsetmode(0, MODE_MOUSE);
-				xsetmode(set, MODE_MOUSEMANY);
+				setpointermotion(set);
+				setmode(0, MODE_MOUSE);
+				setmode(set, MODE_MOUSEMANY);
 				break;
 			case 1004: /* 1004: send focus events to tty */
-				xsetmode(set, MODE_FOCUS);
+				setmode(set, MODE_FOCUS);
 				break;
 			case 1006: /* 1006: extended reporting mode */
-				xsetmode(set, MODE_MOUSESGR);
+				setmode(set, MODE_MOUSESGR);
 				break;
 			case 1034:
-				xsetmode(set, MODE_8BIT);
+				setmode(set, MODE_8BIT);
 				break;
 			case 10

…… diff 过长已截断 ……

some old changes i made 2023nageyqma · 2026-10-22 · 1273786
查看变更
config.h | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/config.h b/config.h
index 9abd9e8..ecab61e 100644
--- a/config.h
+++ b/config.h
@@ -6,7 +6,8 @@
  * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
  */
 //static char *font = "JetBrainsMono Nerd Font Mono:pixelsize=26:antialias=true:autohint=true";
-static char *font = "SauceCodePro Nerd Font Mono:pixelsize=26:antialias=true:autohint=true";
+//static char *font = "SauceCodePro Nerd Font Mono:pixelsize=26:antialias=true:autohint=true";
+static char *font = "SauceCodePro Nerd Font Mono:pixelsize=20:antialias=true:autohint=true";
 //static char *font = "Source Code Pro:pixelsize=26:antialias=true:autohint=true";
 static int borderpx = 1;
 
@@ -82,8 +83,9 @@ const int boxdraw_bold = 1;
 const int boxdraw_braille = 1;
 
 /* bg opacity */
-//float alpha = 0.60;
-float alpha = 0.55;
+float alpha = 0.75;
+//float alpha = 0.55;
+
 //float alpha = 0.45;
 //float alpha = 0.0;
 
w3m supportLew1s777 · 2023-07-22 · 4054cbb
查看变更
config.h                  |  8 +++++---
 config.mk                 |  3 +--
 patches/st-w3m-0.8.3.diff | 42 ++++++++++++++++++++++++++++++++++++++++++
 x.c                       |  2 ++
 4 files changed, 50 insertions(+), 5 deletions(-)
diff --git a/config.h b/config.h
index e3b5a55..9abd9e8 100644
--- a/config.h
+++ b/config.h
@@ -82,8 +82,9 @@ const int boxdraw_bold = 1;
 const int boxdraw_braille = 1;
 
 /* bg opacity */
-//float alpha = 0.65;
-float alpha = 0.45;
+//float alpha = 0.60;
+float alpha = 0.55;
+//float alpha = 0.45;
 //float alpha = 0.0;
 
 /*
@@ -138,7 +139,8 @@ static const char *colorname[] = {
 //[256] = "#222D31", /* background */
   [256] = "#282a36", /* background */
 //[256] = "#000000", /* background */
-  [257] = "#B5B5B5", /* foreground */
+//  [257] = "#B5B5B5", /* foreground */
+  [257] = "#BD93F9", /* foreground */
 };
 
 /*
diff --git a/config.mk b/config.mk
index 4f920d1..294614f 100644
--- a/config.mk
+++ b/config.mk
@@ -24,10 +24,9 @@ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
 
 # flags
 #CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L ${XINERAMAFLAGS}
-#CPUFLAGS = -march=x86-64-v3
 CPUFLAGS = -march=native
 CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L
-CFLAGS   = -std=c99 -pedantic -Wall -Wno-deprecated-declarations ${CPUFLAGS} -O3 --lto ${INCS} ${CPPFLAGS}
+CFLAGS   = -std=c99 -pedantic -Wall -Wno-deprecated-declarations ${CPUFLAGS} -Ofast --lto ${INCS} ${CPPFLAGS}
 STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
 STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
 STLDFLAGS = $(LIBS) $(LDFLAGS)
diff --git a/patches/st-w3m-0.8.3.diff b/patches/st-w3m-0.8.3.diff
new file mode 100644
index 0000000..a4e104b
--- /dev/null
+++ b/patches/st-w3m-0.8.3.diff
@@ -0,0 +1,42 @@
+From 69cffc587b54b0a9cd81adb87abad8e526d5b25b Mon Sep 17 00:00:00 2001
+From: "Avi Halachmi (:avih)" <avihpit@yahoo.com>
+Date: Thu, 4 Jun 2020 17:35:08 +0300
+Subject: [PATCH] support w3m images
+
+w3m images are a hack which renders on top of the terminal's drawable,
+which didn't work in st because when using double buffering, the front
+buffer (on which w3m draws its images) is ignored, and st draws only
+on the back buffer, which is then copied to the front buffer.
+
+There's a patch to make it work at the FAQ already, but that patch
+canceles double-buffering, which can have negative side effects on
+some cases such as flickering.
+
+This patch achieves the same goal but instead of canceling the double
+buffer it first copies the front buffer to the back buffer.
+
+This has the same issues as the FAQ patch in that the cursor line is
+deleted at the image (because st renders always full lines), but
+otherwise it's simpler and does keeps double buffering.
+---
+ x.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/x.c b/x.c
+index e5f1737..b6ae162 100644
+--- a/x.c
++++ b/x.c
+@@ -1594,6 +1594,8 @@ xsettitle(char *p)
+ int
+ xstartdraw(void)
+ {
++	if (IS_SET(MODE_VISIBLE))
++		XCopyArea(xw.dpy, xw.win, xw.buf, dc.gc, 0, 0, win.w, win.h, 0, 0);
+ 	return IS_SET(MODE_VISIBLE);
+ }
+ 
+
+base-commit: 43a395ae91f7d67ce694e65edeaa7bbc720dd027
+-- 
+2.17.1
+
diff --git a/x.c b/x.c
index 2997537..e70014c 100644
--- a/x.c
+++ b/x.c
@@ -1665,6 +1665,8 @@ xsettitle(char *p)
 int
 xstartdraw(void)
 {
+	if (IS_SET(MODE_VISIBLE))
+		XCopyArea(xw.dpy, xw.win, xw.buf, dc.gc, 0, 0, win.w, win.h, 0, 0);
 	return IS_SET(MODE_VISIBLE);
 }
 
nerd font supportLew1s777 · 2023-04-22 · e3e8eec
查看变更
config.h                                 | 10 ++++++----
 config.mk                                |  5 +++--
 patches/st-dynamic-cursor-color-0.9.diff | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 x.c                                      | 17 ++++++++++++++---
 4 files changed, 73 insertions(+), 9 deletions(-)
diff --git a/config.h b/config.h
index 0a8fef0..e3b5a55 100644
--- a/config.h
+++ b/config.h
@@ -6,8 +6,8 @@
  * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
  */
 //static char *font = "JetBrainsMono Nerd Font Mono:pixelsize=26:antialias=true:autohint=true";
-//static char *font = "SauceCodePro Nerd Font Mono:pixelsize=26:antialias=true:autohint=true";
-static char *font = "Source Code Pro:pixelsize=26:antialias=true:autohint=true";
+static char *font = "SauceCodePro Nerd Font Mono:pixelsize=26:antialias=true:autohint=true";
+//static char *font = "Source Code Pro:pixelsize=26:antialias=true:autohint=true";
 static int borderpx = 1;
 
 /*
@@ -82,8 +82,9 @@ const int boxdraw_bold = 1;
 const int boxdraw_braille = 1;
 
 /* bg opacity */
-float alpha = 0.65;
-//float alpha = 0.5;                    //better visual if u r using a dark wallpaper
+//float alpha = 0.65;
+float alpha = 0.45;
+//float alpha = 0.0;
 
 /*
  * bell volume. It must be a value between -100 and 100. Use 0 for disabling
@@ -136,6 +137,7 @@ static const char *colorname[] = {
   /* special colors */
 //[256] = "#222D31", /* background */
   [256] = "#282a36", /* background */
+//[256] = "#000000", /* background */
   [257] = "#B5B5B5", /* foreground */
 };
 
diff --git a/config.mk b/config.mk
index 0a6749c..4f920d1 100644
--- a/config.mk
+++ b/config.mk
@@ -24,9 +24,10 @@ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
 
 # flags
 #CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L ${XINERAMAFLAGS}
-CPUFLAGS = -march=x86-64-v3
+#CPUFLAGS = -march=x86-64-v3
+CPUFLAGS = -march=native
 CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L
-CFLAGS   = -std=c99 -pedantic -Wall -Wno-deprecated-declarations ${CPUFLAGS} -O3 ${INCS} ${CPPFLAGS}
+CFLAGS   = -std=c99 -pedantic -Wall -Wno-deprecated-declarations ${CPUFLAGS} -O3 --lto ${INCS} ${CPPFLAGS}
 STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
 STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
 STLDFLAGS = $(LIBS) $(LDFLAGS)
diff --git a/patches/st-dynamic-cursor-color-0.9.diff b/patches/st-dynamic-cursor-color-0.9.diff
new file mode 100644
index 0000000..1034595
--- /dev/null
+++ b/patches/st-dynamic-cursor-color-0.9.diff
@@ -0,0 +1,50 @@
+From 215ec30d6b5fe3319f88f1c9d16a37b6e14e5a53 Mon Sep 17 00:00:00 2001
+From: Bakkeby <bakkeby@gmail.com>
+Date: Mon, 19 Dec 2022 10:20:47 +0100
+Subject: [PATCH] dynamic cursor color: cursor color taken from current
+ character
+
+---
+ x.c | 17 ++++++++++++++---
+ 1 file changed, 14 insertions(+), 3 deletions(-)
+
+diff --git a/x.c b/x.c
+index 2a3bd38..21aadce 100644
+--- a/x.c
++++ b/x.c
+@@ -1520,6 +1520,7 @@ void
+ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
+ {
+ 	Color drawcol;
++	XRenderColor colbg;
+ 
+ 	/* remove the old cursor */
+ 	if (selected(ox, oy))
+@@ -1548,11 +1549,21 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
+ 		if (selected(cx, cy)) {
+ 			g.fg = defaultfg;
+ 			g.bg = defaultrcs;
++		} else if (!(og.mode & ATTR_REVERSE)) {
++			unsigned long col = g.bg;
++			g.bg = g.fg;
++			g.fg = col;
++		}
++
++		if (IS_TRUECOL(g.bg)) {
++			colbg.alpha = 0xffff;
++			colbg.red = TRUERED(g.bg);
++			colbg.green = TRUEGREEN(g.bg);
++			colbg.blue = TRUEBLUE(g.bg);
++			XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &drawcol);
+ 		} else {
+-			g.fg = defaultbg;
+-			g.bg = defaultcs;
++			drawcol = dc.col[g.bg];
+ 		}
+-		drawcol = dc.col[g.bg];
+ 	}
+ 
+ 	/* draw the new one */
+-- 
+2.38.1
+
diff --git a/x.c b/x.c
index 734966c..2997537 100644
--- a/x.c
+++ b/x.c
@@ -1544,6 +1544,7 @@ void
 xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len)
 {
 	Color drawcol;
+	XRenderColor colbg;
 
 	/* remove the old cursor */
 	if (selected(ox, oy))
@@ -1575,11 +1576,21 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int le
 		if (selected(cx, cy)) {
 			g.fg = defaultfg;
 			g.bg = defaultrcs;
+		} else if (!(og.mode & ATTR_REVERSE)) {
+			unsigned long col = g.bg;
+			g.bg = g.fg;
+			g.fg = col;
+		}
+
+		if (IS_TRUECOL(g.bg)) {
+			colbg.alpha = 0xffff;
+			colbg.red = TRUERED(g.bg);
+			colbg.green = TRUEGREEN(g.bg);
+			colbg.blue = TRUEBLUE(g.bg);
+			XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &drawcol);
 		} else {
-			g.fg = defaultbg;
-			g.bg = defaultcs;
+			drawcol = dc.col[g.bg];
 		}
-		drawcol = dc.col[g.bg];
 	}
 
 	/* draw the new one */
cpu flags addedLew1s777 · 2022-12-31 · ec4ccb6
查看变更
config.mk | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/config.mk b/config.mk
index 1741840..0a6749c 100644
--- a/config.mk
+++ b/config.mk
@@ -1,5 +1,5 @@
 # st version
-VERSION = 0.8.4
+VERSION = 0.9.0.1
 
 # Customize below to fit your system
 
@@ -23,6 +23,10 @@ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
        `$(PKG_CONFIG) --libs harfbuzz`
 
 # flags
+#CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L ${XINERAMAFLAGS}
+CPUFLAGS = -march=x86-64-v3
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L
+CFLAGS   = -std=c99 -pedantic -Wall -Wno-deprecated-declarations ${CPUFLAGS} -O3 ${INCS} ${CPPFLAGS}
 STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
 STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
 STLDFLAGS = $(LIBS) $(LDFLAGS)
remap zoomLew1s777 · 2022-12-29 · 27d9c33
查看变更
config.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/config.h b/config.h
index ac97f09..0a8fef0 100644
--- a/config.h
+++ b/config.h
@@ -207,8 +207,8 @@ static Shortcut shortcuts[] = {
 	{ 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} },
+	{ ControlMask,          XK_equal,       zoom,           {.f = +1} },
+	{ ControlMask,          XK_minus,       zoom,           {.f = -1} },
 	{ TERMMOD,              XK_Home,        zoomreset,      {.f =  0} },
 	{ TERMMOD,              XK_C,           clipcopy,       {.i =  0} },
 	{ TERMMOD,              XK_V,           clippaste,      {.i =  0} },
rebuildLew1s777 · 2022-11-26 · b51d608
查看变更
.gitignore                                       |    4 +
 LEGACY                                           |   17 +
 LICENSE                                          |   34 ++
 Makefile                                         |   59 +++
 README                                           |   34 ++
 arg.h                                            |   50 ++
 boxdraw.c                                        |  194 +++++++
 boxdraw_data.h                                   |  214 ++++++++
 config.h                                         |  489 ++++++++++++++++++
 config.mk                                        |   37 ++
 hb.c                                             |  140 +++++
 hb.h                                             |    7 +
 patches/st-alpha-0.8.2.diff                      |  163 ++++++
 patches/st-anysize-0.8.1.diff                    |  152 ++++++
 patches/st-boxdraw_v2-0.8.3.diff                 |  600 +++++++++++++++++++++
 patches/st-copyout                               |   12 +
 patches/st-hidecursor-0.8.1.diff                 |   88 ++++
 patches/st-keyboard_select-20200617-9ba7ecf.diff |  319 ++++++++++++
 patches/st-ligatures-boxdraw-20200430-0.8.3.diff |  311 +++++++++++
 patches/st-scrollback-20190331-21367a0.diff      |  335 ++++++++++++
 st.1                                             |  177 +++++++
 st.c                                             | 2891 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 st.h                                             |  141 +++++
 st.info                                          |  239 +++++++++
 win.h                                            |   42 ++
 x.c                                              | 2133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 26 files changed, 8882 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ddd21cd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.orig
+*.rej
+*.o
+st
diff --git a/LEGACY b/LEGACY
new file mode 100644
index 0000000..bf28b1e
--- /dev/null
+++ b/LEGACY
@@ -0,0 +1,17 @@
+A STATEMENT ON LEGACY SUPPORT
+
+In the terminal world there is much cruft that comes from old and unsup‐
+ported terminals that inherit incompatible modes  and  escape  sequences
+which noone is able to know, except when he/she comes from that time and
+developed a graphical vt100 emulator at that time.
+
+One  goal  of  st is to only support what is really needed. When you en‐
+counter a sequence which you really need, implement it.  But  while  you
+are  at it,  do not add the other cruft you might encounter while sneek‐
+ing at other terminal emulators. History has bloated them and  there  is
+no real evidence that most of the sequences are used today.
+
+
+Christoph Lohmann <20h@r-36.net>
+2012-09-13T07:00:36.081271045+02:00
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d80eb47
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,34 @@
+MIT/X Consortium License
+
+© 2014-2020 Hiltjo Posthuma <hiltjo at codemadness dot org>
+© 2018 Devin J. Pohly <djpohly at gmail dot com>
+© 2014-2017 Quentin Rameau <quinq at fifth dot space>
+© 2009-2012 Aurélien APTEL <aurelien dot aptel at gmail dot com>
+© 2008-2017 Anselm R Garbe <garbeam at gmail dot com>
+© 2012-2017 Roberto E. Vargas Caballero <k0ga at shike2 dot com>
+© 2012-2016 Christoph Lohmann <20h at r-36 dot net>
+© 2013 Eon S. Jeon <esjeon at hyunmu dot am>
+© 2013 Alexander Sedov <alex0player at gmail dot com>
+© 2013 Mark Edgar <medgar123 at gmail dot com>
+© 2013-2014 Eric Pruitt <eric.pruitt at gmail dot com>
+© 2013 Michael Forney <mforney at mforney dot org>
+© 2013-2014 Markus Teich <markus dot teich at stusta dot mhn dot de>
+© 2014-2015 Laslo Hunhold <dev at frign dot de>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2ffd3c8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,59 @@
+# st - simple terminal
+# See LICENSE file for copyright and license details.
+.POSIX:
+
+include config.mk
+
+SRC = st.c x.c boxdraw.c hb.c
+OBJ = $(SRC:.c=.o)
+
+all: options st
+
+options:
+	@echo st build options:
+	@echo "CFLAGS  = $(STCFLAGS)"
+	@echo "LDFLAGS = $(STLDFLAGS)"
+	@echo "CC      = $(CC)"
+
+config.h:
+	cp config.def.h config.h
+
+.c.o:
+	$(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
+boxdraw.o: config.h st.h boxdraw_data.h
+
+$(OBJ): config.h config.mk
+
+st: $(OBJ)
+	$(CC) -o $@ $(OBJ) $(STLDFLAGS)
+
+clean:
+	rm -f st $(OBJ) st-$(VERSION).tar.gz
+
+dist: clean
+	mkdir -p st-$(VERSION)
+	cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\
+		config.def.h st.info st.1 arg.h st.h win.h $(SRC)\
+		st-$(VERSION)
+	tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz
+	rm -rf st-$(VERSION)
+
+install: st
+	mkdir -p $(DESTDIR)$(PREFIX)/bin
+	cp -f st $(DESTDIR)$(PREFIX)/bin
+	chmod 755 $(DESTDIR)$(PREFIX)/bin/st
+	mkdir -p $(DESTDIR)$(MANPREFIX)/man1
+	sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1
+	chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1
+	tic -sx st.info
+	@echo Please see the README file regarding the terminfo entry of st.
+
+uninstall:
+	rm -f $(DESTDIR)$(PREFIX)/bin/st
+	rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1
+
+.PHONY: all options clean dist install uninstall
diff --git a/README b/README
new file mode 100644
index 0000000..6a846ed
--- /dev/null
+++ b/README
@@ -0,0 +1,34 @@
+st - simple terminal
+--------------------
+st is a simple terminal emulator for X which sucks less.
+
+
+Requirements
+------------
+In order to build st you need the Xlib header files.
+
+
+Installation
+------------
+Edit config.mk to match your local setup (st is installed into
+the /usr/local namespace by default).
+
+Afterwards enter the following command to build and install st (if
+necessary as root):
+
+    make clean install
+
+
+Running st
+----------
+If you did not install st with make clean install, you must compile
+the st terminfo entry with the following command:
+
+    tic -sx st.info
+
+See the man page for additional details.
+
+Credits
+-------
+Based on Aurélien APTEL <aurelien dot aptel at gmail dot com> bt source code.
+
diff --git a/arg.h b/arg.h
new file mode 100644
index 0000000..a22e019
--- /dev/null
+++ b/arg.h
@@ -0,0 +1,50 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef ARG_H__
+#define ARG_H__
+
+extern char *argv0;
+
+/* use main(int argc, char *argv[]) */
+#define ARGBEGIN	for (argv0 = *argv, argv++, argc--;\
+					argv[0] && argv[0][0] == '-'\
+					&& argv[0][1];\
+					argc--, argv++) {\
+				char argc_;\
+				char **argv_;\
+				int brk_;\
+				if (argv[0][1] == '-' && argv[0][2] == '\0') {\
+					argv++;\
+					argc--;\
+					break;\
+				}\
+				int i_;\
+				for (i_ = 1, brk_ = 0, argv_ = argv;\
+						argv[0][i_] && !brk_;\
+						i_++) {\
+					if (argv_ != argv)\
+						break;\
+					argc_ = argv[0][i_];\
+					switch (argc_)
+
+#define ARGEND			}\
+			}
+
+#define ARGC()		argc_
+
+#define EARGF(x)	((argv[0][i_+1] == '\0' && argv[1] == NULL)?\
+				((x), abort(), (char *)0) :\
+				(brk_ = 1, (argv[0][i_+1] != '\0')?\
+					(&argv[0][i_+1]) :\
+					(argc--, argv++, argv[0])))
+
+#define ARGF()		((argv[0][i_+1] == '\0' && argv[1] == NULL)?\
+				(char *)0 :\
+				(brk_ = 1, (argv[0][i_+1] != '\0')?\
+					(&argv[0][i_+1]) :\
+					(argc--, argv++, argv[0])))
+
+#endif
diff --git a/boxdraw.c b/boxdraw.c
new file mode 100644
index 0000000..28a92d0
--- /dev/null
+++ b/boxdraw.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih
+ * MIT/X Consortium License
+ */
+
+#include <X11/Xft/Xft.h>
+#include "st.h"
+#include "boxdraw_data.h"
+
+/* Rounded non-negative integers division of n / d  */
+#define DIV(n, d) (((n) + (d) / 2) / (d))
+
+static Display *xdpy;
+static Colormap xcmap;
+static XftDraw *xd;
+static Visual *xvis;
+
+static void drawbox(int, int, int, int, XftColor *, XftColor *, ushort);
+static void drawboxlines(int, int, int, int, XftColor *, ushort);
+
+/* public API */
+
+void
+boxdraw_xinit(Display *dpy, Colormap cmap, XftDraw *draw, Visual *vis)
+{
+	xdpy = dpy; xcmap = cmap; xd = draw, xvis = vis;
+}
+
+int
+isboxdraw(Rune u)
+{
+	Rune block = u & ~0xff;
+	return (boxdraw && block == 0x2500 && boxdata[(uint8_t)u]) ||
+	       (boxdraw_braille && block == 0x2800);
+}
+
+/* the "index" is actually the entire shape data encoded as ushort */
+ushort
+boxdrawindex(const Glyph *g)
+{
+	if (boxdraw_braille && (g->u & ~0xff) == 0x2800)
+		return BRL | (uint8_t)g->u;
+	if (boxdraw_bold && (g->mode & ATTR_BOLD))
+		return BDB | boxdata[(uint8_t)g->u];
+	return boxdata[(uint8_t)g->u];
+}
+
+void
+drawboxes(int x, int y, int cw, int ch, XftColor *fg, XftColor *bg,
+          const XftGlyphFontSpec *specs, int len)
+{
+	for ( ; len-- > 0; x += cw, specs++)
+		drawbox(x, y, cw, ch, fg, bg, (ushort)specs->glyph);
+}
+
+/* implementation */
+
+void
+drawbox(int x, int y, int w, int h, XftColor *fg, XftColor *bg, ushort bd)
+{
+	ushort cat = bd & ~(BDB | 0xff);  /* mask out bold and data */
+	if (bd & (BDL | BDA)) {
+		/* lines (light/double/heavy/arcs) */
+		drawboxlines(x, y, w, h, fg, bd);
+
+	} else if (cat == BBD) {
+		/* lower (8-X)/8 block */
+		int d = DIV((uint8_t)bd * h, 8);
+		XftDrawRect(xd, fg, x, y + d, w, h - d);
+
+	} else if (cat == BBU) {
+		/* upper X/8 block */
+		XftDrawRect(xd, fg, x, y, w, DIV((uint8_t)bd * h, 8));
+
+	} else if (cat == BBL) {
+		/* left X/8 block */
+		XftDrawRect(xd, fg, x, y, DIV((uint8_t)bd * w, 8), h);
+
+	} else if (cat == BBR) {
+		/* right (8-X)/8 block */
+		int d = DIV((uint8_t)bd * w, 8);
+		XftDrawRect(xd, fg, x + d, y, w - d, h);
+
+	} else if (cat == BBQ) {
+		/* Quadrants */
+		int w2 = DIV(w, 2), h2 = DIV(h, 2);
+		if (bd & TL)
+			XftDrawRect(xd, fg, x, y, w2, h2);
+		if (bd & TR)
+			XftDrawRect(xd, fg, x + w2, y, w - w2, h2);
+		if (bd & BL)
+			XftDrawRect(xd, fg, x, y + h2, w2, h - h2);
+		if (bd & BR)
+			XftDrawRect(xd, fg, x + w2, y + h2, w - w2, h - h2);
+
+	} else if (bd & BBS) {
+		/* Shades - data is 1/2/3 for 25%/50%/75% alpha, respectively */
+		int d = (uint8_t)bd;
+		XftColor xfc;
+		XRenderColor xrc = { .alpha = 0xffff };
+
+		xrc.red = DIV(fg->color.red * d + bg->color.red * (4 - d), 4);
+		xrc.green = DIV(fg->color.green * d + bg->color.green * (4 - d), 4);
+		xrc.blue = DIV(fg->color.blue * d + bg->color.blue * (4 - d), 4);
+
+		XftColorAllocValue(xdpy, xvis, xcmap, &xrc, &xfc);
+		XftDrawRect(xd, &xfc, x, y, w, h);
+		XftColorFree(xdpy, xvis, xcmap, &xfc);
+
+	} else if (cat == BRL) {
+		/* braille, each data bit corresponds to one dot at 2x4 grid */
+		int w1 = DIV(w, 2);
+		int h1 = DIV(h, 4), h2 = DIV(h, 2), h3 = DIV(3 * h, 4);
+
+		if (bd & 1)   XftDrawRect(xd, fg, x, y, w1, h1);
+		if (bd & 2)   XftDrawRect(xd, fg, x, y + h1, w1, h2 - h1);
+		if (bd & 4)   XftDrawRect(xd, fg, x, y + h2, w1, h3 - h2);
+		if (bd & 8)   XftDrawRect(xd, fg, x + w1, y, w - w1, h1);
+		if (bd & 16)  XftDrawRect(xd, fg, x + w1, y + h1, w - w1, h2 - h1);
+		if (bd & 32)  XftDrawRect(xd, fg, x + w1, y + h2, w - w1, h3 - h2);
+		if (bd & 64)  XftDrawRect(xd, fg, x, y + h3, w1, h - h3);
+		if (bd & 128) XftDrawRect(xd, fg, x + w1, y + h3, w - w1, h - h3);
+
+	}
+}
+
+void
+drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd)
+{
+	/* s: stem thickness. width/8 roughly matches underscore thickness. */
+	/* We draw bold as 1.5 * normal-stem and at least 1px thicker.      */
+	/* doubles draw at least 3px, even when w or h < 3. bold needs 6px. */
+	int mwh = MIN(w, h);
+	int base_s = MAX(1, DIV(mwh, 8));
+	int bold = (bd & BDB) && mwh >= 6;  /* possibly ignore boldness */
+	int s = bold ? MAX(base_s + 1, DIV(3 * base_s, 2)) : base_s;
+	int w2 = DIV(w - s, 2), h2 = DIV(h - s, 2);
+	/* the s-by-s square (x + w2, y + h2, s, s) is the center texel.    */
+	/* The base length (per direction till edge) includes this square.  */
+
+	int light = bd & (LL | LU | LR | LD);
+	int double_ = bd & (DL | DU | DR | DD);
+
+	if (light) {
+		/* d: additional (negative) length to not-draw the center   */
+		/* texel - at arcs and avoid drawing inside (some) doubles  */
+		int arc = bd & BDA;
+		int multi_light = light & (light - 1);
+		int multi_double = double_ & (double_ - 1);
+		/* light crosses double only at DH+LV, DV+LH (ref. shapes)  */
+		int d = arc || (multi_double && !multi_light) ? -s : 0;
+
+		if (bd & LL)
+			XftDrawRect(xd, fg, x, y + h2, w2 + s + d, s);
+		if (bd & LU)
+			XftDrawRect(xd, fg, x + w2, y, s, h2 + s + d);
+		if (bd & LR)
+			XftDrawRect(xd, fg, x + w2 - d, y + h2, w - w2 + d, s);
+		if (bd & LD)
+			XftDrawRect(xd, fg, x + w2, y + h2 - d, s, h - h2 + d);
+	}
+
+	/* double lines - also align with light to form heavy when combined */
+	if (double_) {
+		/*
+		* going clockwise, for each double-ray: p is additional length
+		* to the single-ray nearer to the previous direction, and n to
+		* the next. p and n adjust from the base length to lengths
+		* which consider other doubles - shorter to avoid intersections
+		* (p, n), or longer to draw the far-corner texel (n).
+		*/
+		int dl = bd & DL, du = bd & DU, dr = bd & DR, dd = bd & DD;
+		if (dl) {
+			int p = dd ? -s : 0, n = du ? -s : dd ? s : 0;
+			XftDrawRect(xd, fg, x, y + h2 + s, w2 + s + p, s);
+			XftDrawRect(xd, fg, x, y + h2 - s, w2 + s + n, s);
+		}
+		if (du) {
+			int p = dl ? -s : 0, n = dr ? -s : dl ? s : 0;
+			XftDrawRect(xd, fg, x + w2 - s, y, s, h2 + s + p);
+			XftDrawRect(xd, fg, x + w2 + s, y, s, h2 + s + n);
+		}
+		if (dr) {
+			int p = du ? -s : 0, n = dd ? -s : du ? s : 0;
+			XftDrawRect(xd, fg, x + w2 - p, y + h2 - s, w - w2 + p, s);
+			XftDrawRect(xd, fg, x + w2 - n, y + h2 + s, w - w2 + n, s);
+		}
+		if (dd) {
+			int p = dr ? -s : 0, n = dl ? -s : dr ? s : 0;
+			XftDrawRect(xd, fg, x + w2 + s, y + h2 - p, s, h - h2 + p);
+			XftDrawRect(xd, fg, x + w2 - s, y + h2 - n, s, h - h2 + n);
+		}
+	}
+}
diff --git a/boxdraw_data.h b/boxdraw_data.h
new file mode 100644
index 0000000..7890500
--- /dev/null
+++ b/boxdraw_data.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih
+ * MIT/X Consortium License
+ */
+
+/*
+ * U+25XX codepoints data
+ *
+ * References:
+ *   http://www.unicode.org/charts/PDF/U2500.pdf
+ *   http://www.unicode.org/charts/PDF/U2580.pdf
+ *
+ * Test page:
+ *   https://github.com/GNOME/vte/blob/master/doc/boxes.txt
+ */
+
+/* Each shape is encoded as 16-bits. Higher bits are category, lower are data */
+/* Categories (mutually exclusive except BDB): */
+/* For convenience, BDL/BDA/BBS/BDB are 1 bit each, the rest are enums */
+#define BDL (1<<8)   /* Box Draw Lines (light/double/heavy) */
+#define BDA (1<<9)   /* Box Draw Arc (light) */
+
+#define BBD (1<<10)  /* Box Block Down (lower) X/8 */
+#define BBL (2<<10)  /* Box Block Left X/8 */
+#define BBU (3<<10)  /* Box Block Upper X/8 */
+#define BBR (4<<10)  /* Box Block Right X/8 */
+#define BBQ (5<<10)  /* Box Block Quadrants */
+#define BRL (6<<10)  /* Box Braille (data is lower byte of U28XX) */
+
+#define BBS (1<<14)  /* Box Block Shades */
+#define BDB (1<<15)  /* Box Draw is Bold */
+
+/* (BDL/BDA) Light/Double/Heavy x Left/Up/Right/Down/Horizontal/Vertical      */
+/* Heavy is light+double (literally drawing light+double align to form heavy) */
+#define LL (1<<0)
+#define LU (1<<1)
+#define LR (1<<2)
+#define LD (1<<3)
+#define LH (LL+LR)
+#define LV (LU+LD)
+
+#define DL (1<<4)
+#define DU (1<<5)
+#define DR (1<<6)
+#define DD (1<<7)
+#define DH (DL+DR)
+#define DV (DU+DD)
+
+#define HL (LL+DL)
+#define HU (LU+DU)
+#define HR (LR+DR)
+#define HD (LD+DD)
+#define HH (HL+HR)
+#define HV (HU+HD)
+
+/* (BBQ) Quadrants Top/Bottom x Left/Right */
+#define TL (1<<0)
+#define TR (1<<1)
+#define BL (1<<2)
+#define BR (1<<3)
+
+/* Data for U+2500 - U+259F except dashes/diagonals */
+static const unsigned short boxdata[256] = {
+	/* light lines */
+	[0x00] = BDL + LH,       /* light horizontal */
+	[0x02] = BDL + LV,       /* light vertical */
+	[0x0c] = BDL + LD + LR,  /* light down and right */
+	[0x10] = BDL + LD + LL,  /* light down and left */
+	[0x14] = BDL + LU + LR,  /* light up and right */
+	[0x18] = BDL + LU + LL,  /* light up and left */
+	[0x1c] = BDL + LV + LR,  /* light vertical and right */
+	[0x24] = BDL + LV + LL,  /* light vertical and left */
+	[0x2c] = BDL + LH + LD,  /* light horizontal and down */
+	[0x34] = BDL + LH + LU,  /* light horizontal and up */
+	[0x3c] = BDL + LV + LH,  /* light vertical and horizontal */
+	[0x74] = BDL + LL,       /* light left */
+	[0x75] = BDL + LU,       /* light up */
+	[0x76] = BDL + LR,       /* light right */
+	[0x77] = BDL + LD,       /* light down */
+
+	/* heavy [+light] lines */
+	[0x01] = BDL + HH,
+	[0x03] = BDL + HV,
+	[0x0d] = BDL + HR + LD,
+	[0x0e] = BDL + HD + LR,
+	[0x0f] = BDL + HD + HR,
+	[0x11] = BDL + HL + LD,
+	[0x12] = BDL + HD + LL,
+	[0x13] = BDL + HD + HL,
+	[0x15] = BDL + HR + LU,
+	[0x16] = BDL + HU + LR,
+	[0x17] = BDL + HU + HR,
+	[0x19] = BDL + HL + LU,
+	[0x1a] = BDL + HU + LL,
+	[0x1b] = BDL + HU + HL,
+	[0x1d] = BDL + HR + LV,
+	[0x1e] = BDL + HU + LD + LR,
+	[0x1f] = BDL + HD + LR + LU,
+	[0x20] = BDL + HV + LR,
+	[0x21] = BDL + HU + HR + LD,
+	[0x22] = BDL + HD + HR + LU,
+	[0x23] = BDL + HV + HR,
+	[0x25] = BDL + HL + LV,
+	[0x26] = BDL + HU + LD + LL,
+	[0x27] = BDL + HD + LU + LL,
+	[0x28] = BDL + HV + LL,
+	[0x29] = BDL + HU + HL + LD,
+	[0x2a] = BDL + HD + HL + LU,
+	[0x2b] = BDL + HV + HL,
+	[0x2d] = BDL + HL + LD + LR,
+	[0x2e] = BDL + HR + LL + LD,
+	[0x2f] = BDL + HH + LD,
+	[0x30] = BDL + HD + LH,
+	[0x31] = BDL + HD + HL + LR,
+	[0x32] = BDL + HR + HD + LL,
+	[0x33] = BDL + HH + HD,
+	[0x35] = BDL + HL + LU + LR,
+	[0x36] = BDL + HR + LU + LL,
+	[0x37] = BDL + HH + LU,
+	[0x38] = BDL + HU + LH,
+	[0x39] = BDL + HU + HL + LR,
+	[0x3a] = BDL + HU + HR + LL,
+	[0x3b] = BDL + HH + HU,
+	[0x3d] = BDL + HL + LV + LR,
+	[0x3e] = BDL + HR + LV + LL,
+	[0x3f] = BDL + HH + LV,
+	[0x40] = BDL + HU + LH + LD,
+	[0x41] = BDL + HD + LH + LU,
+	[0x42] = BDL + HV + LH,
+	[0x43] = BDL + HU + HL + LD + LR,
+	[0x44] = BDL + HU + HR + LD + LL,
+	[0x45] = BDL + HD + HL + LU + LR,
+	[0x46] = BDL + HD + HR + LU + LL,
+	[0x47] = BDL + HH + HU + LD,
+	[0x48] = BDL + HH + HD + LU,
+	[0x49] = BDL + HV + HL + LR,
+	[0x4a] = BDL + HV + HR + LL,
+	[0x4b] = BDL + HV + HH,
+	[0x78] = BDL + HL,
+	[0x79] = BDL + HU,
+	[0x7a] = BDL + HR,
+	[0x7b] = BDL + HD,
+	[0x7c] = BDL + HR + LL,
+	[0x7d] = BDL + HD + LU,
+	[0x7e] = BDL + HL + LR,
+	[0x7f] = BDL + HU + LD,
+
+	/* double [+light] lines */
+	[0x50] = BDL + DH,
+	[0x51] = BDL + DV,
+	[0x52] = BDL + DR + LD,
+	[0x53] = BDL + DD + LR,
+	[0x54] = BDL + DR + DD,
+	[0x55] = BDL + DL + LD,
+	[0x56] = BDL + DD + LL,
+	[0x57] = BDL + DL + DD,
+	[0x58] = BDL + DR + LU,
+	[0x59] = BDL + DU + LR,
+	[0x5a] = BDL + DU + DR,
+	[0x5b] = BDL + DL + LU,
+	[0x5c] = BDL + DU + LL,
+	[0x5d] = BDL + DL + DU,
+	[0x5e] = BDL + DR + LV,
+	[0x5f] = BDL + DV + LR,
+	[0x60] = BDL + DV + DR,
+	[0x61] = BDL + DL + LV,
+	[0x62] = BDL + DV + LL,
+	[0x63] = BDL + DV + DL,
+	[0x64] = BDL + DH + LD,
+	[0x65] = BDL + DD + LH,
+	[0x66] = BDL + DD + DH,
+	[0x67] = BDL + DH + LU,
+	[0x68] = BDL + DU + LH,
+	[0x69] = BDL + DH + DU,
+	[0x6a] = BDL + DH + LV,
+	[0x6b] = BDL + DV + LH,
+	[0x6c] = BDL + DH + DV,
+
+	/* (light) arcs */
+	[0x6d] = BDA + LD + LR,
+	[0x6e] = BDA + LD + LL,
+	[0x6f] = BDA + LU + LL,
+	[0x70] = BDA + LU + LR,
+
+	/* Lower (Down) X/8 block (data is 8 - X) */
+	[0x81] = BBD + 7, [0x82] = BBD + 6, [0x83] = BBD + 5, [0x84] = BBD + 4,
+	[0x85] = BBD + 3, [0x86] = BBD + 2, [0x87] = BBD + 1, [0x88] = BBD + 0,
+
+	/* Left X/8 block (data is X) */
+	[0x89] = BBL + 7, [0x8a] = BBL + 6, [0x8b] = BBL + 5, [0x8c] = BBL + 4,
+	[0x8d] = BBL + 3, [0x8e] = BBL + 2, [0x8f] = BBL + 1,
+
+	/* upper 1/2 (4/8), 1/8 block (X), right 1/2, 1/8 block (8-X) */
+	[0x80] = BBU + 4, [0x94] = BBU + 1,
+	[0x90] = BBR + 4, [0x95] = BBR + 7,
+
+	/* Quadrants */
+	[0x96] = BBQ + BL,
+	[0x97] = BBQ + BR,
+	[0x98] = BBQ + TL,
+	[0x99] = BBQ + TL + BL + BR,
+	[0x9a] = BBQ + TL + BR,
+	[0x9b] = BBQ + TL + TR + BL,
+	[0x9c] = BBQ + TL + TR + BR,
+	[0x9d] = BBQ + TR,
+	[0x9e] = BBQ + BL + TR,
+	[0x9f] = BBQ + BL + TR + BR,
+
+	/* Shades, data is an alpha value in 25% units (1/4, 1/2, 3/4) */
+	[0x91] = BBS + 1, [0x92] = BBS + 2, [0x93] = BBS + 3,
+
+	/* U+2504 - U+250B, U+254C - U+254F: unsupported (dashes) */
+	/* U+2571 - U+2573: unsupported (diagonals) */
+};
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..ac97f09
--- /dev/null
+++ b/config.h
@@ -0,0 +1,489 @@
+/* See LICENSE file for copyright and license details. */
+
+/*
+ * appearance
+ *
+ * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
+ */
+//static char *font = "JetBrainsMono Nerd Font Mono:pixelsize=26:antialias=true:autohint=true";
+//static char *font = "SauceCodePro Nerd Font Mono:pixelsize=26:antialias=true:autohint=true";
+static char *font = "Source Code Pro:pixelsize=26:antialias=true:autohint=true";
+static int borderpx = 1;
+
+/*
+ * What program is execed by st depends of these precedence rules:
+ * 1: program passed with -e
+ * 2: scroll and/or utmp
+ * 3: SHELL environment variable
+ * 4: value of shell in /etc/passwd
+ * 5: value of shell in config.h
+ */
+static char *shell = "/bin/sh";
+char *utmp = NULL;
+/* scroll program: to enable use a string like "scroll" */
+char *scroll = NULL;
+char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400";
+
+/* identification sequence returned in DA and DECID */
+char *vtiden = "\033[?6c";
+
+/* Kerning / character bounding-box multipliers */
+static float cwscale = 1.0;
+static float chscale = 0.9;
+
+/*
+ * word delimiter string
+ *
+ * More advanced example: L" `'\"()[]{}"
+ */
+wchar_t *worddelimiters = L" ";
+
+/* selection timeouts (in milliseconds) */
+static unsigned int doubleclicktimeout = 300;
+static unsigned int tripleclicktimeout = 600;
+
+/* alt screens */
+int allowaltscreen = 1;
+
+/* allow certain non-interactive (insecure) window operations such as:
+   setting the clipboard text */
+int allowwindowops = 0;
+
+/*
+ * draw latency range in ms - from new content/keypress/etc until drawing.
+ * within this range, st draws when content stops arriving (idle). mostly it's
+ * near minlatency, but it waits longer for slow updates to avoid partial draw.
+ * low minlatency will tear/flicker more, as it can "detect" idle too early.
+ */
+static double minlatency = 8;
+static double maxlatency = 33;
+
+/*
+ * blinking timeout (set to 0 to disable blinking) for the terminal blinking
+ * attribute.
+ */
+static unsigned int blinktimeout = 800;
+
+/*
+ * thickness of underline and bar cursors
+ */
+static unsigned int cursorthickness = 2;
+
+/*
+ * 1: render most of the lines/blocks characters without using the font for
+ *    perfect alignment between cells (U2500 - U259F except dashes/diagonals).
+ *    Bold affects lines thickness if boxdraw_bold is not 0. Italic is ignored.
+ * 0: disable (render all U25XX glyphs normally from the font).
+ */
+const int boxdraw = 1;
+const int boxdraw_bold = 1;
+
+/* braille (U28XX):  1: render as adjacent "pixels",  0: use font */
+const int boxdraw_braille = 1;
+
+/* bg opacity */
+float alpha = 0.65;
+//float alpha = 0.5;                    //better visual if u r using a dark wallpaper
+
+/*
+ * bell volume. It must be a value between -100 and 100. Use 0 for disabling
+ * it
+ */
+static int bellvolume = 0;
+
+/* default TERM value */
+char *termname = "st-256color";
+
+/*
+ * spaces per tab
+ *
+ * When you are changing this value, don't forget to adapt the »it« value in
+ * the st.info and appropriately install the st.info in the environment where
+ * you use this st version.
+ *
+ *	it#$tabspaces,
+ *
+ * Secondly make sure your kernel is not expanding tabs. When running `stty
+ * -a` »tab0« should appear. You can tell the terminal to not expand tabs by
+ *  running following command:
+ *
+ *	stty tabs
+ */
+unsigned int tabspaces = 8;
+
+/* Terminal colors (16 first used in escape sequence) */
+static const char *colorname[] = {
+  /* 8 normal colors */
+  [0] = "#000000", /* black   */
+  [1] = "#ff5555", /* red     */
+  [2] = "#50fa7b", /* green   */
+  [3] = "#f1fa8c", /* yellow  */
+  [4] = "#bd93f9", /* blue    */
+  [5] = "#ff79c6", /* magenta */
+  [6] = "#8be9fd", /* cyan    */
+  [7] = "#bbbbbb", /* white   */
+
+  /* 8 bright colors */
+  [8]  = "#696969", /* black   */
+  [9]  = "#ff5555", /* red     */
+  [10] = "#50fa7b", /* green   */
+  [11] = "#f1fa8c", /* yellow  */
+  [12] = "#bd93f9", /* blue    */
+  [13] = "#ff79c6", /* magenta */
+  [14] = "#8be9fd", /* cyan    */
+  [15] = "#dddddd", /* white   */
+
+  /* special colors */
+//[256] = "#222D31", /* background */
+  [256] = "#282a36", /* background */
+  [257] = "#B5B5B5", /* foreground */
+};
+
+/*
+ * Default colors (colorname index)
+ * foreground, background, cursor, reverse cursor
+ */
+unsigned int defaultfg = 257;
+unsigned int defaultbg = 256;
+static unsigned int defaultcs = 257;
+static unsigned int defaultrcs = 257;
+
+/*
+ * Default shape of cursor
+ * 2: Block ("█")
+ * 4: Underline ("_")
+ * 6: Bar ("|")
+ * 7: Snowman ("☃")
+ */
+static unsigned int cursorshape = 2;
+
+/*
+ * Default columns and rows numbers
+ */
+
+static unsigned int cols = 80;
+static unsigned int rows = 24;
+
+/*
+ * Default colour and shape of the mouse cursor
+ */
+static unsigned int mouseshape = XC_xterm;
+static unsigned int mousefg = 7;
+static unsigned int mousebg = 0;
+
+/*
+ * Color used to display font attributes when fontconfig selected a font which
+ * doesn't match the ones requested.
+ */
+static unsigned int defaultattr = 11;
+
+/*
+ * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set).
+ * Note that if you want to use ShiftMask with selmasks, set this to an other
+ * modifier, set to 0 to not use it.
+ */
+static uint forcemousemod = ShiftMask;
+
+/* Internal keyboard shortcuts. */
+#define MODKEY Mod1Mask
+#define TERMMOD (ControlMask|ShiftMask)
+
+/*
+ * Internal mouse shortcuts.
+ * Beware that overloading Button1 will disable the selection.
+ */
+static MouseShortcut mshortcuts[] = {
+	/* mask                 button   function        argument       release */
+	{ XK_ANY_MOD,           Button2, selpaste,       {.i = 0},      1 },
+	{ XK_NO_MOD,            Button4, kscrollup,      {.i = 1}         },
+	{ XK_NO_MOD,            Button5, kscrolldown,    {.i = 1}         },
+	{ ControlMask,          Button4, kscrollup,      {.i = -1}        },
+	{ ControlMask,          Button5, kscrolldown,    {.i = -1}        },
+};
+
+static Shortcut shortcuts[] = {
+	/* mask                 keysym          function        argument */
+	{ XK_ANY_MOD,           XK_Break,       sendbreak,      {.i =  0} },
+	{ ControlMask,          XK_Print,       toggleprinter,  {.i =  0} },
+	{ ShiftMask,            XK_Print,       printscreen,    {.i =  0} },
+	{ XK_ANY_MOD,           XK_Print,       printsel,       {.i =  0} },
+	{ TERMMOD,              XK_Prior,       zoom,           {.f = +1} },
+	{ TERMMOD,              XK_Next,        zoom,           {.f = -1} },
+	{ TERMMOD,              XK_Home,        zoomreset,      {.f =  0} },
+	{ TERMMOD,              XK_C,           clipcopy,       {.i =  0} },
+	{ TERMMOD,              XK_V,           clippaste,      {.i =  0} },
+	{ TERMMOD,              XK_Y,           selpaste,       {.i =  0} },
+	{ ShiftMask,            XK_Insert,      selpaste,       {.i =  0} },
+	{ TERMMOD,              XK_Num_Lock,    numlock,        {.i =  0} },
+ 	{ TERMMOD,              XK_S,           keyboard_select,{.i =  0} },
+};
+
+/*
+ * Special keys (change & recompile st.info accordingly)
+ *
+ * Mask value:
+ * * Use XK_ANY_MOD to match the key no matter modifiers state
+ * * Use XK_NO_MOD to match the key alone (no modifiers)
+ * appkey value:
+ * * 0: no value
+ * * > 0: keypad application mode enabled
+ * *   = 2: term.numlock = 1
+ * * < 0: keypad application mode disabled
+ * appcursor value:
+ * * 0: no value
+ * * > 0: cursor application mode enabled
+ * * < 0: cursor application mode disabled
+ *
+ * Be careful with the order of the definitions because st searches in
+ * this table sequentially, so any XK_ANY_MOD must be in the last
+ * position for a key.
+ */
+
+/*
+ * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF)
+ * to be mapped below, add them to this array.
+ */
+static KeySym mappedkeys[] = { -1 };
+
+/*
+ * State bits to ignore when matching key or button events.  By default,
+ * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored.
+ */
+static uint ignoremod = Mod2Mask|XK_SWITCH_MOD;
+
+/*
+ * This is the huge key array which defines all compatibility to the Linux
+ * world. Please decide about changes wisely.
+ */
+static Key key[] = {
+	/* keysym           mask            string      appkey appcursor */
+	{ XK_KP_Home,       ShiftMask,      "\033[2J",       0,   -1},
+	{ XK_KP_Home,       ShiftMask,      "\033[1;2H",     0,   +1},
+	{ XK_KP_Home,       XK_ANY_MOD,     "\033[H",        0,   -1},
+	{ XK_KP_Home,       XK_ANY_MOD,     "\033[1~",       0,   +1},
+	{ XK_KP_Up,         XK_ANY_MOD,     "\033Ox",       +1,    0},
+	{ XK_KP_Up,         XK_ANY_MOD,     "\033[A",        0,   -1},
+	{ XK_KP_Up,         XK_ANY_MOD,     "\033OA",        0,   +1},
+	{ XK_KP_Down,       XK_ANY_MOD,     "\033Or",       +1,    0},
+	{ XK_KP_Down,       XK_ANY_MOD,     "\033[B",        0,   -1},
+	{ XK_KP_Down,       XK_ANY_MOD,     "\033OB",        0,   +1},
+	{ XK_KP_Left,       XK_ANY_MOD,     "\033Ot",       +1,    0},
+	{ XK_KP_Left,       XK_ANY_MOD,     "\033[D",        0,   -1},
+	{ XK_KP_Left,       XK_ANY_MOD,     "\033OD",        0,   +1},
+	{ XK_KP_Right,      XK_ANY_MOD,     "\033Ov",       +1,    0},
+	{ XK_KP_Right,      XK_ANY_MOD,     "\033[C",        0,   -1},
+	{ XK_KP_Right,      XK_ANY_MOD,     "\033OC",        0,   +1},
+	{ XK_KP_Prior,      ShiftMask,      "\033[5;2~",     0,    0},
+	{ XK_KP_Prior,      XK_ANY_MOD,     "\033[5~",       0,    0},
+	{ XK_KP_Begin,      XK_ANY_MOD,     "\033[E",        0,    0},
+	{ XK_KP_End,        ControlMask,    "\033[J",       -1,    0},
+	{ XK_KP_End,        ControlMask,    "\033[1;5F",    +1,    0},
+	{ XK_KP_End,        ShiftMask,      "\033[K",       -1,    0},
+	{ XK_KP_End,        ShiftMask,      "\033[1;2F",    +1,    0},
+	{ XK_KP_End,        XK_ANY_MOD,     "\033[4~",       0,    0},
+	{ XK_KP_Next,       ShiftMask,      "\033[6;2~",     0,    0},
+	{ XK_KP_Next,       XK_ANY_MOD,     "\033[6~",       0,    0},
+	{ XK_KP_Insert,     ShiftMask,      "\033[2;2~",    +1,    0},
+	{ XK_KP_Insert,     ShiftMask,      "\033[4l",      -1,    0},
+	{ XK_KP_Insert,     ControlMask,    "\033[L",       -1,    0},
+	{ XK_KP_Insert,     ControlMask,    "\033[2;5~",    +1,    0},
+	{ XK_KP_Insert,     XK_ANY_MOD,     "\033[4h",      -1,    0},
+	{ XK_KP_Insert,     XK_ANY_MOD,     "\033[2~",      +1,    0},
+	{ XK_KP_Delete,     ControlMask,    "\033[M",       -1,    0},
+	{ XK_KP_Delete,     ControlMask,    "\033[3;5~",    +1,    0},
+	{ XK_KP_Delete,     ShiftMask,      "\033[2K",      -1,    0},
+	{ XK_KP_Delete,     ShiftMask,      "\033[3;2~",    +1,    0},
+	{ XK_KP_Delete,     XK_ANY_MOD,     "\033[P",       -1,    0},
+	{ XK_KP_Delete,     XK_ANY_MOD,     "\033[3~",      +1,    0},
+	{ XK_KP_Multiply,   XK_ANY_MOD,     "\033Oj",       +2,    0},
+	{ XK_KP_Add,        XK_ANY_MOD,     "\033Ok",       +2,    0},
+	{ XK_KP_Enter,      XK_ANY_MOD,     "\033OM",       +2,    0},
+	{ XK_KP_Enter,      XK_ANY_MOD,     "\r",           -1,    0},
+	{ XK_KP_Subtract,   XK_ANY_MOD,     "\033Om",       +2,    0},
+	{ XK_KP_Decimal,    XK_ANY_MOD,     "\033On",       +2,    0},
+	{ XK_KP_Divide,     XK_ANY_MOD,     "\033Oo",       +2,    0},
+	{ XK_KP_0,          XK_ANY_MOD,     "\033Op",       +2,    0},
+	{ XK_KP_1,          XK_ANY_MOD,     "\033Oq",       +2,    0},
+	{ XK_KP_2,          XK_ANY_MOD,     "\033Or",       +2,    0},
+	{ XK_KP_3,          XK_ANY_MOD,     "\033Os",       +2,    0},
+	{ XK_KP_4,          XK_ANY_MOD,     "\033Ot",       +2,    0},
+	{ XK_KP_5,          XK_ANY_MOD,     "\033Ou",       +2,    0},
+	{ XK_KP_6,          XK_ANY_MOD,     "\033Ov",       +2,    0},
+	{ XK_KP_7,          XK_ANY_MOD,     "\033Ow",       +2,    0},
+	{ XK_KP_8,          XK_ANY_MOD,     "\033Ox",       +2,    0},
+	{ XK_KP_9,          XK_ANY_MOD,     "\033Oy",       +2,    0},
+	{ XK_Up,            ShiftMask,      "\033[1;2A",     0,    0},
+	{ XK_Up,            Mod1Mask,       "\033[1;3A",     0,    0},
+	{ XK_Up,         ShiftMask|Mod1Mask,"\033[1;4A",     0,    0},
+	{ XK_Up,            ControlMask,    "\033[1;5A",     0,    0},
+	{ XK_Up,      ShiftMask|ControlMask,"\033[1;6A",     0,    0},
+	{ XK_Up,       ControlMask|Mod1Mask,"\033[1;7A",     0,    0},
+	{ XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A",  0,    0},
+	{ XK_Up,            XK_ANY_MOD,     "\033[A",        0,   -1},
+	{ XK_Up,            XK_ANY_MOD,     "\033OA",        0,   +1},
+	{ XK_Down,          ShiftMask,      "\033[1;2B",     0,    0},
+	{ XK_Down,          Mod1Mask,       "\033[1;3B",     0,    0},
+	{ XK_Down,       ShiftMask|Mod1Mask,"\033[1;4B",     0,    0},
+	{ XK_Down,          ControlMask,    "\033[1;5B",     0,    0},
+	{ XK_Down,    ShiftMask|ControlMask,"\033[1;6B",     0,    0},
+	{ XK_Down,     ControlMask|Mod1Mask,"\033[1;7B",     0,    0},
+	{ XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0,    0},
+	{ XK_Down,          XK_ANY_MOD,     "\033[B",        0,   -1},
+	{ XK_Down,          XK_ANY_MOD,     "\033OB",        0,   +1},
+	{ XK_Left,          ShiftMask,      "\033[1;2D",     0,    0},
+	{ XK_Left,          Mod1Mask,       "\033[1;3D",     0,    0},
+	{ XK_Left,       ShiftMask|Mod1Mask,"\033[1;4D",     0,    0},
+	{ XK_Left,          ControlMask,    "\033[1;5D",     0,    0},
+	{ XK_Left,    ShiftMask|ControlMask,"\033[1;6D",     0,    0},
+	{ XK_Left,     ControlMask|Mod1Mask,"\033[1;7D",     0,    0},
+	{ XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0,    0},
+	{ XK_Left,          XK_ANY_MOD,     "\033[D",        0,   -1},
+	{ XK_Left,          XK_ANY_MOD,     "\033OD",        0,   +1},
+	{ XK_Right,         ShiftMask,      "\033[1;2C",     0,    0},
+	{ XK_Right,         Mod1Mask,       "\033[1;3C",     0,    0},
+	{ XK_Right,      ShiftMask|Mod1Mask,"\033[1;4C",     0,    0},
+	{ XK_Right,         ControlMask,    "\033[1;5C",     0,    0},
+	{ XK_Right,   ShiftMask|ControlMask,"\033[1;6C",     0,    0},
+	{ XK_Right,    ControlMask|Mod1Mask,"\033[1;7C",     0,    0},
+	{ XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0,   0},
+	{ XK_Right,         XK_ANY_MOD,     "\033[C",        0,   -1},
+	{ XK_Right,         XK_ANY_MOD,     "\033OC",        0,   +1},
+	{ XK_ISO_Left_Tab,  ShiftMask,      "\033[Z",        0,    0},
+	{ XK_Return,        Mod1Mask,       "\033\r",        0,    0},
+	{ XK_Return,        XK_ANY_MOD,     "\r",            0,    0},
+	{ XK_Insert,        ShiftMask,      "\033[4l",      -1,    0},
+	{ XK_Insert,        ShiftMask,      "\033[2;2~",    +1,    0},
+	{ XK_Insert,        ControlMask,    "\033[L",       -1,    0},
+	{ XK_Insert,        ControlMask,    "\033[2;5~",    +1,    0},
+	{ XK_Insert,        XK_ANY_MOD,     "\033[4h",      -1,    0},
+	{ XK_Insert,        XK_ANY_MOD,     "\033[2~",      +1,    0},
+	{ XK_Delete,        ControlMask,    "\033[M",       -1,    0},
+	{ XK_Delete,        ControlMask,    "\033[3;5~",    +1,    0},
+	{ XK_Delete,        ShiftMask,      "\033[2K",      -1,    0},
+	{ XK_Delete,        ShiftMask,      "\033[3;2~",    +1,    0},
+	{ XK_Delete,        XK_ANY_MOD,     "\033[P",       -1,    0},
+	{ XK_Delete,        XK_ANY_MOD,     "\033[3~",      +1,    0},
+	{ XK_BackSpace,     XK_NO_MOD,      "\177",          0,    0},
+	{ XK_BackSpace,     Mod1Mask,       "\033\177",      0,    0},
+	{ XK_Home,          ShiftMask,      "\033[2J",       0,   -1},
+	{ XK_Home,          ShiftMask,      "\033[1;2H",     0,   +1},
+	{ XK_Home,          XK_ANY_MOD,     "\033[H",        0,   -1},
+	{ XK_Home,          XK_ANY_MOD,     "\033[1~",       0,   +1},
+	{ XK_End,           ControlMask,    "\033[J",       -1,    0},
+	{ XK_End,           ControlMask,    "\033[1;5F",    +1,    0},
+	{ XK_End,           ShiftMask,      "\033[K",       -1,    0},
+	{ XK_End,           ShiftMask,      "\033[1;2F",    +1,    0},
+	{ XK_End,           XK_ANY_MOD,     "\033[4~",       0,    0},
+	{ XK_Prior,         ControlMask,    "\033[5;5~",     0,    0},
+	{ XK_Prior,         ShiftMask,      "\033[5;2~",     0,    0},
+	{ XK_Prior,         XK_ANY_MOD,     "\033[5~",       0,    0},
+	{ XK_Next,          ControlMask,    "\033[6;5~",     0,    0},
+	{ XK_Next,          ShiftMask,      "\033[6;2~",     0,    0},
+	{ XK_Next,          XK_ANY_MOD,     "\033[6~",       0,    0},
+	{ XK_F1,            XK_NO_MOD,      "\033OP" ,       0,    0},
+	{ XK_F1, /* F13 */  ShiftMask,      "\033[1;2P",     0,    0},
+	{ XK_F1, /* F25 */  ControlMask,    "\033[1;5P",     0,    0},
+	{ XK_F1, /* F37 */  Mod4Mask,       "\033[1;6P",     0,    0},
+	{ XK_F1, /* F49 */  Mod1Mask,       "\033[1;3P",     0,    0},
+	{ XK_F1, /* F61 */  Mod3Mask,       "\033[1;4P",     0,    0},
+	{ XK_F2,            XK_NO_MOD,      "\033OQ" ,       0,    0},
+	{ XK_F2, /* F14 */  ShiftMask,      "\033[1;2Q",     0,    0},
+	{ XK_F2, /* F26 */  ControlMask,    "\033[1;5Q",     0,    0},
+	{ XK_F2, /* F38 */  Mod4Mask,       "\033[1;6Q",     0,    0},
+	{ XK_F2, /* F50 */  Mod1Mask,       "\033[1;3Q",     0,    0},
+	{ XK_F2, /* F62 */  Mod3Mask,       "\033[1;4Q",     0,    0},
+	{ XK_F3,            XK_NO_MOD,      "\033OR" ,       0,    0},
+	{ XK_F3, /* F15 */  ShiftMask,      "\033[1;2R",     0,    0},
+	{ XK_F3, /* F27 */  ControlMask,    "\033[1;5R",     0,    0},
+	{ XK_F3, /* F39 */  Mod4Mask,       "\033[1;6R",     0,    0},
+	{ XK_F3, /* F51 */  Mod1Mask,       "\033[1;3R",     0,    0},
+	{ XK_F3, /* F63 */  Mod3Mask,       "\033[1;4R",     0,    0},
+	{ XK_F4,            XK_NO_MOD,      "\033OS" ,       0,    0},
+	{ XK_F4, /* F16 */  ShiftMask,      "\033[1;2S",     0,    0},
+	{ XK_F4, /* F28 */  ControlMask,    "\033[1;5S",     0,    0},
+	{ XK_F4, /* F40 */  Mod4Mask,       "\033[1;6S",     0,    0},
+	{ XK_F4, /* F52 */  Mod1Mask,       "\033[1;3S",     0,    0},
+	{ XK_F5,            XK_NO_MOD,      "\033[15~",      0,    0},
+	{ XK_F5, /* F17 */  ShiftMask,      "\033[15;2~",    0,    0},
+	{ XK_F5, /* F29 */  ControlMask,    "\033[15;5~",    0,    0},
+	{ XK_F5, /* F41 */  Mod4Mask,       "\033[15;6~",    0,    0},
+	{ XK_F5, /* F53 */  Mod1Mask,       "\033[15;3~",    0,    0},
+	{ XK_F6,            XK_NO_MOD,      "\033[17~",      0,    0},
+	{ XK_F6, /* F18 */  ShiftMask,      "\033[17;2~",    0,    0},
+	{ XK_F6, /* F30 */  ControlMask,    "\033[17;5~",    0,    0},
+	{ XK_F6, /* F42 */  Mod4Mask,       "\033[17;6~",    0,    0},
+	{ XK_F6, /* F54 */  Mod1Mask,       "\033[17;3~",    0,    0},
+	{ XK_F7,            XK_NO_MOD,      "\033[18~",      0,    0},
+	{ XK_F7, /* F19 */  ShiftMask,      "\033[18;2~",    0,    0},
+	{ XK_F7, /* F31 */  ControlMask,    "\033[18;5~",    0,    0},
+	{ XK_F7, /* F43 */  Mod4Mask,       "\033[18;6~",    0,    0},
+	{ XK_F7, /* F55 */  Mod1Mask,       "\033[18;3~",    0,    0},
+	{ XK_F8,            XK_NO_MOD,      "\033[19~",      0,    0},
+	{ XK_F8, /* F20 */  ShiftMask,      "\033[19;2~",    0,    0},
+	{ XK_F8, /* F32 */  ControlMask,    "\033[19;5~",    0,    0},
+	{ XK_F8, /* F44 */  Mod4Mask,       "\033[19;6~",    0,    0},
+	{ XK_F8, /* F56 */  Mod1Mask,       "\033[19;3~",    0,    0},
+	{ XK_F9,            XK_NO_MOD,      "\033[20~",      0,    0},
+	{ XK_F9, /* F21 */  ShiftMask,      "\033[20;2~",    0,    0},
+	{ XK_F9, /* F33 */  ControlMask,    "\033[20;5~",    0,    0},
+	{ XK_F9, /* F45 */  Mod4Mask,       "\033[20;6~",    0,    0},
+	{ XK_F9, /* F57 */  Mod1Mask,       "\033[20;3~",    0,    0},
+	{ XK_F10,           XK_NO_MOD,      "\033[21~",      0,    0},
+	{ XK_F10, /* F22 */ ShiftMask,      "\033[21;2~",    0,    0},
+	{ XK_F10, /* F34 */ ControlMask,    "\033[21;5~",    0,    0},
+	{ XK_F10, /* F46 */ Mod4Mask,       "\033[21;6~",    0,    0},
+	{ XK_F10, /* F58 */ Mod1Mask,       "\033[21;3~",    0,    0},
+	{ XK_F11,           XK_NO_MOD,      "\033[23~",      0,    0},
+	{ XK_F11, /* F23 */ ShiftMask,      "\033[23;2~",    0,    0},
+	{ XK_F11, /* F35 */ ControlMask,    "\033[23;5~",    0,    0},
+	{ XK_F11, /* F47 */ Mod4Mask,       "\033[23;6~",    0,    0},
+	{ XK_F11, /* F59 */ Mod1Mask,       "\033[23;3~",    0,    0},
+	{ XK_F12,           XK_NO_MOD,      "\033[24~",      0,    0},
+	{ XK_F12, /* F24 */ ShiftMask,      "\033[24;2~",    0,    0},
+	{ XK_F12, /* F36 */ ControlMask,    "\033[24;5~",    0,    0},
+	{ XK_F12, /* F48 */ Mod4Mask,       "\033[24;6~",    0,    0},
+	{ XK_F12, /* F60 */ Mod1Mask,       "\033[24;3~",    0,    0},
+	{ XK_F13,           XK_NO_MOD,      "\033[1;2P",     0,    0},
+	{ XK_F14,           XK_NO_MOD,      "\033[1;2Q",     0,    0},
+	{ XK_F15,           XK_NO_MOD,      "\033[1;2R",     0,    0},
+	{ XK_F16,           XK_NO_MOD,      "\033[1;2S",     0,    0},
+	{ XK_F17,           XK_NO_MOD,      "\033[15;2~",    0,    0},
+	{ XK_F18,           XK_NO_MOD,      "\033[17;2~",    0,    0},
+	{ XK_F19,           XK_NO_MOD,      "\033[18;2~",    0,    0},
+	{ XK_F20,           XK_NO_MOD,      "\033[19;2~",    0,    0},
+	{ XK_F21,           XK_NO_MOD,      "\033[20;2~",    0,    0},
+	{ XK_F22,           XK_NO_MOD,      "\033[21;2~",    0,    0},
+	{ XK_F23,           XK_NO_MOD,      "\033[23;2~",    0,    0},
+	{ XK_F24,           XK_NO_MOD,      "\033[24;2~",    0,    0},
+	{ XK_F25,           XK_NO_MOD,      "\033[1;5P",     0,    0},
+	{ XK_F26,           XK_NO_MOD,      "\033[1;5Q",     0,    0},
+	{ XK_F27,           XK_NO_MOD,      "\033[1;5R",     0,    0},
+	{ XK_F28,           XK_NO_MOD,      "\033[1;5S",     0,    0},
+	{ XK_F29,           XK_NO_MOD,      "\033[15;5~",    0,    0},
+	{ XK_F30,           XK_NO_MOD,      "\033[17;5~",    0,    0},
+	{ XK_F31,           XK_NO_MOD,      "\033[18;5~",    0,    0},
+	{ XK_F32,           XK_NO_MOD,      "\033[19;5~",    0,    0},
+	{ XK_F33,           XK_NO_MOD,      "\033[20;5~",    0,    0},
+	{ XK_F34,           XK_NO_MOD,      "\033[21;5~",    0,    0},
+	{ XK_F35,           XK_NO_MOD,      "\033[23;5~",    0,    0},
+};
+
+/*
+ * Selection types' masks.
+ * Use the same masks as usual.
+ * Button1Mask is always unset, to make masks match between ButtonPress.
+ * ButtonRelease and MotionNotify.
+ * If no match is found, regular selection is used.
+ */
+static uint selmasks[] = {
+	[SEL_RECTANGULAR] = Mod1Mask,
+};
+
+/*
+ * Printable characters in ASCII, used to estimate the advance width
+ * of single wide characters.
+ */
+static char ascii_printable[] =
+	" !\"#$%&'()*+,-./0123456789:;<=>?"
+	"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+	"`abcdefghijklmnopqrstuvwxyz{|}~";
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..1741840
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,37 @@
+# st version
+VERSION = 0.8.4
+
+# Customize below to fit your system
+
+# paths
+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` \
+       `$(PKG_CONFIG) --cflags harfbuzz`
+LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
+       `$(PKG_CONFIG) --libs fontconfig` \
+       `$(PKG_CONFIG) --libs freetype2` \
+       `$(PKG_CONFIG) --libs harfbuzz`
+
+# flags
+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/hb.c b/hb.c
new file mode 100644
index 0000000..467bcac
--- /dev/null
+++ b/hb.c
@@ -0,0 +1,140 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <X11/Xft/Xft.h>
+#include <hb.h>
+#include <hb-ft.h>
+
+#include "st.h"
+
+void hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length);
+hb_font_t *hbfindfont(XftFont *match);
+
+typedef struct {
+	XftFont *match;
+	hb_font_t *font;
+} HbFontMatch;
+
+static int hbfontslen = 0;
+static HbFontMatch *hbfontcache = NULL;
+
+void
+hbunloadfonts()
+{
+	for (int i = 0; i < hbfontslen; i++) {
+		hb_font_destroy(hbfontcache[i].font);
+		XftUnlockFace(hbfontcache[i].match);
+	}
+
+	if (hbfontcache != NULL) {
+		free(hbfontcache);
+		hbfontcache = NULL;
+	}
+	hbfontslen = 0;
+}
+
+hb_font_t *
+hbfindfont(XftFont *match)
+{
+	for (int i = 0; i < hbfontslen; i++) {
+		if (hbfontcache[i].match == match)
+			return hbfontcache[i].font;
+	}
+
+	/* Font not found in cache, caching it now. */
+	hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 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[hbfontslen].match = match;
+	hbfontcache[hbfontslen].font = font;
+	hbfontslen += 1;
+
+	return font;
+}
+
+void
+hbtransform(XftGlyphFontSpec *specs, const Glyph *glyphs, size_t len, int x, int y)
+{
+	int start = 0, length = 1, gstart = 0;
+	hb_codepoint_t *codepoints = calloc(len, sizeof(hb_codepoint_t));
+
+	for (int idx = 1, specidx = 1; idx < len; idx++) {
+		if (glyphs[idx].mode & ATTR_WDUMMY) {
+			length += 1;
+			continue;
+		}
+
+		if (specs[specidx].font != specs[start].font || ATTRCMP(glyphs[gstart], glyphs[idx]) || selected(x + idx, y) != selected(x + gstart, y)) {
+			hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length);
+
+			/* Reset the sequence. */
+			length = 1;
+			start = specidx;
+			gstart = idx;
+		} else {
+			length += 1;
+		}
+
+		specidx++;
+	}
+
+	/* EOL. */
+	hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length);
+
+	/* Apply the transformation to glyph specs. */
+	for (int i = 0, specidx = 0; i < len; i++) {
+		if (glyphs[i].mode & ATTR_WDUMMY)
+			continue;
+		if (glyphs[i].mode & ATTR_BOXDRAW) {
+			specidx++;
+			continue;
+		}
+
+		if (codepoints[i] != specs[specidx].glyph)
+			((Glyph *)glyphs)[i].mode |= ATTR_LIGA;
+
+		specs[specidx++].glyph = codepoints[i];
+	}
+
+	free(codepoints);
+}
+
+void
+hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length)
+{
+	hb_font_t *font = hbfindfont(xfont);
+	if (font == NULL)
+		return;
+
+	Rune rune;
+	ushort mode = USHRT_MAX;
+	hb_buffer_t *buffer = hb_buffer_create();
+	hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
+
+	/* Fill buffer with codepoints. */
+	for (int i = start; i < (start+length); i++) {
+		rune = string[i].u;
+		mode = string[i].mode;
+		if (mode & ATTR_WDUMMY)
+			rune = 0x0020;
+		hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1);
+	}
+
+	/* Shape the segment. */
+	hb_shape(font, buffer, NULL, 0);
+
+	/* Get new glyph info. */
+	hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, NULL);
+
+	/* Write new codepoints. */
+	for (int i = 0; i < length; i++) {
+		hb_codepoint_t gid = info[i].codepoint;
+		codepoints[start+i] = gid;
+	}
+
+	/* Cleanup. */
+	hb_buffer_destroy(buffer);
+}
diff --git a/hb.h b/hb.h
new file mode 100644
index 0000000..b3e02d0
--- /dev/null
+++ b/hb.h
@@ -0,0 +1,7 @@
+#include <X11/Xft/Xft.h>
+#include <hb.h>
+#include <hb-ft.h>
+
+void hbunloadfonts();
+void hbtransform(XftGlyphFontSpec *, const Glyph *, size_t, int, int);
+
diff --git a/patches/st-alpha-0.8.2.diff b/patches/st-alpha-0.8.2.diff
new file mode 100644
index 0000000..dad6615
--- /dev/null
+++ b/patches/st-alpha-0.8.2.diff
@@ -0,0 +1,163 @@
+diff --git a/config.def.h b/config.def.h
+index 0e01717..e116631 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -82,6 +82,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 */
+@@ -109,6 +112,7 @@ static const char *colorname[] = {
+ 	/* more colors can be added after 255 to use with DefaultXX */
+ 	"#cccccc",
+ 	"#555555",
++	"black",
+ };
+ 
+ 
+@@ -117,7 +121,7 @@ static const char *colorname[] = {
+  * foreground, background, cursor, reverse cursor
+  */
+ unsigned int defaultfg = 7;
+-unsigned int defaultbg = 0;
++unsigned int defaultbg = 258;
+ static unsigned int defaultcs = 256;
+ static unsigned int defaultrcs = 257;
+ 
+diff --git a/config.mk b/config.mk
+index 0cbb002..1d2f0e2 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 38c61c4..b7634ab 100644
+--- a/st.h
++++ b/st.h
+@@ -120,3 +120,4 @@ extern char *termname;
+ extern unsigned int tabspaces;
+ extern unsigned int defaultfg;
+ extern unsigned int defaultbg;
++extern float alpha;
+diff --git a/x.c b/x.c
+index 0422421..588dec3 100644
+--- a/x.c
++++ b/x.c
+@@ -98,6 +98,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;
+@@ -229,6 +230,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;
+@@ -688,7 +690,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);
+ 
+@@ -748,6 +750,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;
+ }
+ 
+@@ -1004,11 +1013,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())
+@@ -1018,7 +1039,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 */
+@@ -1038,19 +1059,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);
+ 
+@@ -1894,6 +1911,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;
diff --git a/patches/st-anysize-0.8.1.diff b/patches/st-anysize-0.8.1.diff
new file mode 100644
index 0000000..1eaf2ba
--- /dev/null
+++ b/patches/st-anysize-0.8.1.diff
@@ -0,0 +1,152 @@
+diff --git a/x.c b/x.c
+index 00cb6b1..8f87c0f 100644
+--- a/x.c
++++ b/x.c
+@@ -78,6 +78,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 */
+@@ -311,7 +312,7 @@ zoomreset(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;
+ }
+@@ -319,7 +320,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;
+ }
+@@ -675,6 +676,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);
+@@ -793,8 +797,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;
+@@ -1022,8 +1026,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 + cols * win.cw;
++	win.h = 2 * win.vborderpx + rows * win.ch;
+ 	if (xw.gm & XNegative)
+ 		xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2;
+ 	if (xw.gm & YNegative)
+@@ -1123,7 +1127,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;
+@@ -1259,7 +1263,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;
+@@ -1349,17 +1353,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.vborderpx,
+ 			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.hborderpx);
++	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. */
+@@ -1452,35 +1456,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);
+ 	}
+ }
diff --git a/patches/st-boxdraw_v2-0.8.3.diff b/patches/st-boxdraw_v2-0.8.3.diff
new file mode 100644
index 0000000..04a868c
--- /dev/null
+++ b/patches/st-boxdraw_v2-0.8.3.diff
@@ -0,0 +1,600 @@
+From 3f3b80b9966c60086f4ed80ce4de0cbf03468d36 Mon Sep 17 00:00:00 2001
+From: "Avi Halachmi (:avih)" <avihpit@yahoo.com>
+Date: Wed, 26 Dec 2018 14:51:45 +0200
+Subject: [PATCH] boxdraw_v2: custom render lines/blocks/braille for perfect
+ alignment
+
+It seems impossible to ensure that blocks and line drawing glyphs
+align without visible gaps for all combinations of arbitrary font,
+size and width/height scale factor.
+
+This commit adds options to render most of the lines/blocks and
+braille codepoints without using the font such that they align
+perfectly regardless of font, size or other configuration values.
+
+Supported codepoints are U+2500 - U+259F except dashes/diagonals,
+and U28XX.
+
+The lines/blocks data is stored as 16-bit values at boxdraw_data.h
+
+boxdraw/braille are independent, disabled by default at config[.def].h
+---
+ Makefile       |   3 +-
+ boxdraw.c      | 194 ++++++++++++++++++++++++++++++++++++++++++++
+ boxdraw_data.h | 214 +++++++++++++++++++++++++++++++++++++++++++++++++
+ config.def.h   |  12 +++
+ st.c           |   3 +
+ st.h           |  10 +++
+ x.c            |  21 +++--
+ 7 files changed, 451 insertions(+), 6 deletions(-)
+ create mode 100644 boxdraw.c
+ create mode 100644 boxdraw_data.h
+
+diff --git a/Makefile b/Makefile
+index 470ac86..6dfa212 100644
+--- a/Makefile
++++ b/Makefile
+@@ -4,7 +4,7 @@
+ 
+ include config.mk
+ 
+-SRC = st.c x.c
++SRC = st.c x.c boxdraw.c
+ OBJ = $(SRC:.c=.o)
+ 
+ all: options st
+@@ -23,6 +23,7 @@ config.h:
+ 
+ st.o: config.h st.h win.h
+ x.o: arg.h config.h st.h win.h
++boxdraw.o: config.h st.h boxdraw_data.h
+ 
+ $(OBJ): config.h config.mk
+ 
+diff --git a/boxdraw.c b/boxdraw.c
+new file mode 100644
+index 0000000..28a92d0
+--- /dev/null
++++ b/boxdraw.c
+@@ -0,0 +1,194 @@
++/*
++ * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih
++ * MIT/X Consortium License
++ */
++
++#include <X11/Xft/Xft.h>
++#include "st.h"
++#include "boxdraw_data.h"
++
++/* Rounded non-negative integers division of n / d  */
++#define DIV(n, d) (((n) + (d) / 2) / (d))
++
++static Display *xdpy;
++static Colormap xcmap;
++static XftDraw *xd;
++static Visual *xvis;
++
++static void drawbox(int, int, int, int, XftColor *, XftColor *, ushort);
++static void drawboxlines(int, int, int, int, XftColor *, ushort);
++
++/* public API */
++
++void
++boxdraw_xinit(Display *dpy, Colormap cmap, XftDraw *draw, Visual *vis)
++{
++	xdpy = dpy; xcmap = cmap; xd = draw, xvis = vis;
++}
++
++int
++isboxdraw(Rune u)
++{
++	Rune block = u & ~0xff;
++	return (boxdraw && block == 0x2500 && boxdata[(uint8_t)u]) ||
++	       (boxdraw_braille && block == 0x2800);
++}
++
++/* the "index" is actually the entire shape data encoded as ushort */
++ushort
++boxdrawindex(const Glyph *g)
++{
++	if (boxdraw_braille && (g->u & ~0xff) == 0x2800)
++		return BRL | (uint8_t)g->u;
++	if (boxdraw_bold && (g->mode & ATTR_BOLD))
++		return BDB | boxdata[(uint8_t)g->u];
++	return boxdata[(uint8_t)g->u];
++}
++
++void
++drawboxes(int x, int y, int cw, int ch, XftColor *fg, XftColor *bg,
++          const XftGlyphFontSpec *specs, int len)
++{
++	for ( ; len-- > 0; x += cw, specs++)
++		drawbox(x, y, cw, ch, fg, bg, (ushort)specs->glyph);
++}
++
++/* implementation */
++
++void
++drawbox(int x, int y, int w, int h, XftColor *fg, XftColor *bg, ushort bd)
++{
++	ushort cat = bd & ~(BDB | 0xff);  /* mask out bold and data */
++	if (bd & (BDL | BDA)) {
++		/* lines (light/double/heavy/arcs) */
++		drawboxlines(x, y, w, h, fg, bd);
++
++	} else if (cat == BBD) {
++		/* lower (8-X)/8 block */
++		int d = DIV((uint8_t)bd * h, 8);
++		XftDrawRect(xd, fg, x, y + d, w, h - d);
++
++	} else if (cat == BBU) {
++		/* upper X/8 block */
++		XftDrawRect(xd, fg, x, y, w, DIV((uint8_t)bd * h, 8));
++
++	} else if (cat == BBL) {
++		/* left X/8 block */
++		XftDrawRect(xd, fg, x, y, DIV((uint8_t)bd * w, 8), h);
++
++	} else if (cat == BBR) {
++		/* right (8-X)/8 block */
++		int d = DIV((uint8_t)bd * w, 8);
++		XftDrawRect(xd, fg, x + d, y, w - d, h);
++
++	} else if (cat == BBQ) {
++		/* Quadrants */
++		int w2 = DIV(w, 2), h2 = DIV(h, 2);
++		if (bd & TL)
++			XftDrawRect(xd, fg, x, y, w2, h2);
++		if (bd & TR)
++			XftDrawRect(xd, fg, x + w2, y, w - w2, h2);
++		if (bd & BL)
++			XftDrawRect(xd, fg, x, y + h2, w2, h - h2);
++		if (bd & BR)
++			XftDrawRect(xd, fg, x + w2, y + h2, w - w2, h - h2);
++
++	} else if (bd & BBS) {
++		/* Shades - data is 1/2/3 for 25%/50%/75% alpha, respectively */
++		int d = (uint8_t)bd;
++		XftColor xfc;
++		XRenderColor xrc = { .alpha = 0xffff };
++
++		xrc.red = DIV(fg->color.red * d + bg->color.red * (4 - d), 4);
++		xrc.green = DIV(fg->color.green * d + bg->color.green * (4 - d), 4);
++		xrc.blue = DIV(fg->color.blue * d + bg->color.blue * (4 - d), 4);
++
++		XftColorAllocValue(xdpy, xvis, xcmap, &xrc, &xfc);
++		XftDrawRect(xd, &xfc, x, y, w, h);
++		XftColorFree(xdpy, xvis, xcmap, &xfc);
++
++	} else if (cat == BRL) {
++		/* braille, each data bit corresponds to one dot at 2x4 grid */
++		int w1 = DIV(w, 2);
++		int h1 = DIV(h, 4), h2 = DIV(h, 2), h3 = DIV(3 * h, 4);
++
++		if (bd & 1)   XftDrawRect(xd, fg, x, y, w1, h1);
++		if (bd & 2)   XftDrawRect(xd, fg, x, y + h1, w1, h2 - h1);
++		if (bd & 4)   XftDrawRect(xd, fg, x, y + h2, w1, h3 - h2);
++		if (bd & 8)   XftDrawRect(xd, fg, x + w1, y, w - w1, h1);
++		if (bd & 16)  XftDrawRect(xd, fg, x + w1, y + h1, w - w1, h2 - h1);
++		if (bd & 32)  XftDrawRect(xd, fg, x + w1, y + h2, w - w1, h3 - h2);
++		if (bd & 64)  XftDrawRect(xd, fg, x, y + h3, w1, h - h3);
++		if (bd & 128) XftDrawRect(xd, fg, x + w1, y + h3, w - w1, h - h3);
++
++	}
++}
++
++void
++drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd)
++{
++	/* s: stem thickness. width/8 roughly matches unders

…… diff 过长已截断 ……