X-Git-Url: https://git.xinqibao.xyz/st.git/blobdiff_plain/3e44ee5569a81ba6f06e1ecd19bf0ceb1e97f18d..33201ac65f74e45b4fa60822ba9a538c3cfa9b25:/x.c?ds=inline

diff --git a/x.c b/x.c
index df2a88c..76fb910 100644
--- a/x.c
+++ b/x.c
@@ -15,13 +15,49 @@
 #include <X11/Xft/Xft.h>
 #include <X11/XKBlib.h>
 
+static char *argv0;
 #include "arg.h"
+#include "st.h"
+#include "win.h"
 
-#define Glyph Glyph_
-#define Font Font_
+/* types used in config.h */
+typedef struct {
+	uint mod;
+	KeySym keysym;
+	void (*func)(const Arg *);
+	const Arg arg;
+} Shortcut;
 
-#include "win.h"
-#include "st.h"
+typedef struct {
+	uint b;
+	uint mask;
+	char *s;
+} MouseShortcut;
+
+typedef struct {
+	KeySym k;
+	uint mask;
+	char *s;
+	/* three-valued logic variables: 0 indifferent, 1 on, -1 off */
+	signed char appkey;    /* application keypad */
+	signed char appcursor; /* application cursor */
+} Key;
+
+/* X modifiers */
+#define XK_ANY_MOD    UINT_MAX
+#define XK_NO_MOD     0
+#define XK_SWITCH_MOD (1<<13)
+
+/* function definitions used in config.h */
+static void clipcopy(const Arg *);
+static void clippaste(const Arg *);
+static void selpaste(const Arg *);
+static void zoom(const Arg *);
+static void zoomabs(const Arg *);
+static void zoomreset(const Arg *);
+
+/* config.h for applying patches and the configuration. */
+#include "config.h"
 
 /* XEMBED messages */
 #define XEMBED_FOCUS_IN  4
@@ -34,6 +70,7 @@
 
 typedef XftDraw *Draw;
 typedef XftColor Color;
+typedef XftGlyphFontSpec GlyphFontSpec;
 
 /* Purely graphic info */
 typedef struct {
@@ -41,6 +78,7 @@ typedef struct {
 	Colormap cmap;
 	Window win;
 	Drawable buf;
+	GlyphFontSpec *specbuf; /* font spec buffer used for rendering */
 	Atom xembed, wmdeletewin, netwmname, netwmpid;
 	XIM xim;
 	XIC xic;
@@ -55,9 +93,13 @@ typedef struct {
 
 typedef struct {
 	Atom xtarget;
+	char *primary, *clipboard;
+	struct timespec tclick1;
+	struct timespec tclick2;
 } XSelection;
 
 /* Font structure */
+#define Font Font_
 typedef struct {
 	int height;
 	int width;
@@ -87,9 +129,17 @@ static void xdrawglyph(Glyph, int, int);
 static void xclear(int, int, int, int);
 static void xdrawcursor(void);
 static int xgeommasktogravity(int);
+static void xinit(void);
+static void cresize(int, int);
+static void xresize(int, int);
 static int xloadfont(Font *, FcPattern *);
+static void xloadfonts(char *, double);
 static void xunloadfont(Font *);
+static void xunloadfonts(void);
 static void xsetenv(void);
+static void xseturgency(int);
+static int x2col(int);
+static int y2row(int);
 
 static void expose(XEvent *);
 static void visibility(XEvent *);
@@ -105,10 +155,14 @@ static void propnotify(XEvent *);
 static void selnotify(XEvent *);
 static void selclear_(XEvent *);
 static void selrequest(XEvent *);
-
-static void selcopy(Time);
-static void getbuttoninfo(XEvent *);
+static void setsel(char *, Time);
+static void mousesel(XEvent *, int);
 static void mousereport(XEvent *);
+static char *kmap(KeySym, uint);
+static int match(uint, uint);
+
+static void run(void);
+static void usage(void);
 
 static void (*handler[LASTEvent])(XEvent *) = {
 	[KeyPress] = kpress,
@@ -140,6 +194,12 @@ static void (*handler[LASTEvent])(XEvent *) = {
 static DC dc;
 static XWindow xw;
 static XSelection xsel;
+static TermWindow win;
+
+enum window_state {
+	WIN_VISIBLE = 1,
+	WIN_FOCUSED = 2
+};
 
 /* Font Ring Cache */
 enum {
@@ -158,26 +218,114 @@ typedef struct {
 /* Fontcache is an array now. A new font will be appended to the array. */
 static Fontcache frc[16];
 static int frclen = 0;
+static char *usedfont = NULL;
+static double usedfontsize = 0;
+static double defaultfontsize = 0;
+
+static char *opt_class = NULL;
+static char **opt_cmd  = NULL;
+static char *opt_embed = NULL;
+static char *opt_font  = NULL;
+static char *opt_io    = NULL;
+static char *opt_line  = NULL;
+static char *opt_name  = NULL;
+static char *opt_title = NULL;
 
 void
-getbuttoninfo(XEvent *e)
+clipcopy(const Arg *dummy)
 {
-	int type;
-	uint state = e->xbutton.state & ~(Button1Mask | forceselmod);
+	Atom clipboard;
 
-	sel.alt = IS_SET(MODE_ALTSCREEN);
+	if (xsel.clipboard != NULL)
+		free(xsel.clipboard);
 
-	sel.oe.x = x2col(e->xbutton.x);
-	sel.oe.y = y2row(e->xbutton.y);
-	selnormalize();
+	if (xsel.primary != NULL) {
+		xsel.clipboard = xstrdup(xsel.primary);
+		clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
+		XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime);
+	}
+}
+
+void
+clippaste(const Arg *dummy)
+{
+	Atom clipboard;
+
+	clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
+	XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard,
+			xw.win, CurrentTime);
+}
 
-	sel.type = SEL_REGULAR;
-	for (type = 1; type < selmaskslen; ++type) {
+void
+selpaste(const Arg *dummy)
+{
+	XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY,
+			xw.win, CurrentTime);
+}
+
+void
+zoom(const Arg *arg)
+{
+	Arg larg;
+
+	larg.f = usedfontsize + arg->f;
+	zoomabs(&larg);
+}
+
+void
+zoomabs(const Arg *arg)
+{
+	xunloadfonts();
+	xloadfonts(usedfont, arg->f);
+	cresize(0, 0);
+	redraw();
+	xhints();
+}
+
+void
+zoomreset(const Arg *arg)
+{
+	Arg larg;
+
+	if (defaultfontsize > 0) {
+		larg.f = defaultfontsize;
+		zoomabs(&larg);
+	}
+}
+
+int
+x2col(int x)
+{
+	x -= borderpx;
+	x /= win.cw;
+
+	return LIMIT(x, 0, term.col-1);
+}
+
+int
+y2row(int y)
+{
+	y -= borderpx;
+	y /= win.ch;
+
+	return LIMIT(y, 0, term.row-1);
+}
+
+void
+mousesel(XEvent *e, int done)
+{
+	int type, seltype = SEL_REGULAR;
+	uint state = e->xbutton.state & ~(Button1Mask | forceselmod);
+
+	for (type = 1; type < LEN(selmasks); ++type) {
 		if (match(selmasks[type], state)) {
-			sel.type = type;
+			seltype = type;
 			break;
 		}
 	}
+	selextend(x2col(e->xbutton.x), y2row(e->xbutton.y), seltype, done);
+	if (done)
+		setsel(getsel(), e->xbutton.time);
 }
 
 void
@@ -241,7 +389,7 @@ mousereport(XEvent *e)
 		return;
 	}
 
-	ttywrite(buf, len);
+	ttywrite(buf, len, 0);
 }
 
 void
@@ -249,57 +397,41 @@ bpress(XEvent *e)
 {
 	struct timespec now;
 	MouseShortcut *ms;
+	int snap;
 
 	if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
 		mousereport(e);
 		return;
 	}
 
-	for (ms = mshortcuts; ms < mshortcuts + mshortcutslen; ms++) {
+	for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
 		if (e->xbutton.button == ms->b
 				&& match(ms->mask, e->xbutton.state)) {
-			ttysend(ms->s, strlen(ms->s));
+			ttywrite(ms->s, strlen(ms->s), 1);
 			return;
 		}
 	}
 
 	if (e->xbutton.button == Button1) {
-		clock_gettime(CLOCK_MONOTONIC, &now);
-
-		/* Clear previous selection, logically and visually. */
-		selclear_(NULL);
-		sel.mode = SEL_EMPTY;
-		sel.type = SEL_REGULAR;
-		sel.oe.x = sel.ob.x = x2col(e->xbutton.x);
-		sel.oe.y = sel.ob.y = y2row(e->xbutton.y);
-
 		/*
 		 * If the user clicks below predefined timeouts specific
 		 * snapping behaviour is exposed.
 		 */
-		if (TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) {
-			sel.snap = SNAP_LINE;
-		} else if (TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) {
-			sel.snap = SNAP_WORD;
+		clock_gettime(CLOCK_MONOTONIC, &now);
+		if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) {
+			snap = SNAP_LINE;
+		} else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) {
+			snap = SNAP_WORD;
 		} else {
-			sel.snap = 0;
+			snap = 0;
 		}
-		selnormalize();
+		xsel.tclick2 = xsel.tclick1;
+		xsel.tclick1 = now;
 
-		if (sel.snap != 0)
-			sel.mode = SEL_READY;
-		tsetdirt(sel.nb.y, sel.ne.y);
-		sel.tclick2 = sel.tclick1;
-		sel.tclick1 = now;
+		selstart(x2col(e->xbutton.x), y2row(e->xbutton.y), snap);
 	}
 }
 
-void
-selcopy(Time t)
-{
-	xsetsel(getsel(), t);
-}
-
 void
 propnotify(XEvent *e)
 {
@@ -387,10 +519,10 @@ selnotify(XEvent *e)
 		}
 
 		if (IS_SET(MODE_BRCKTPASTE) && ofs == 0)
-			ttywrite("\033[200~", 6);
-		ttysend((char *)data, nitems * format / 8);
+			ttywrite("\033[200~", 6, 0);
+		ttywrite((char *)data, nitems * format / 8, 1);
 		if (IS_SET(MODE_BRCKTPASTE) && rem == 0)
-			ttywrite("\033[201~", 6);
+			ttywrite("\033[201~", 6, 0);
 		XFree(data);
 		/* number of 32-bit chunks returned */
 		ofs += nitems * format / 32;
@@ -403,36 +535,10 @@ selnotify(XEvent *e)
 	XDeleteProperty(xw.dpy, xw.win, (int)property);
 }
 
-void
-xselpaste(void)
-{
-	XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY,
-			xw.win, CurrentTime);
-}
-
 void
 xclipcopy(void)
 {
-	Atom clipboard;
-
-	if (sel.clipboard != NULL)
-		free(sel.clipboard);
-
-	if (sel.primary != NULL) {
-		sel.clipboard = xstrdup(sel.primary);
-		clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
-		XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime);
-	}
-}
-
-void
-xclippaste(void)
-{
-	Atom clipboard;
-
-	clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
-	XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard,
-			xw.win, CurrentTime);
+	clipcopy(NULL);
 }
 
 void
@@ -476,9 +582,9 @@ selrequest(XEvent *e)
 		 */
 		clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
 		if (xsre->selection == XA_PRIMARY) {
-			seltext = sel.primary;
+			seltext = xsel.primary;
 		} else if (xsre->selection == clipboard) {
-			seltext = sel.clipboard;
+			seltext = xsel.clipboard;
 		} else {
 			fprintf(stderr,
 				"Unhandled clipboard selection 0x%lx\n",
@@ -500,16 +606,22 @@ selrequest(XEvent *e)
 }
 
 void
-xsetsel(char *str, Time t)
+setsel(char *str, Time t)
 {
-	free(sel.primary);
-	sel.primary = str;
+	free(xsel.primary);
+	xsel.primary = str;
 
 	XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t);
 	if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win)
 		selclear_(NULL);
 }
 
+void
+xsetsel(char *str)
+{
+	setsel(str, CurrentTime);
+}
+
 void
 brelease(XEvent *e)
 {
@@ -518,41 +630,39 @@ brelease(XEvent *e)
 		return;
 	}
 
-	if (e->xbutton.button == Button2) {
-		xselpaste();
-	} else if (e->xbutton.button == Button1) {
-		if (sel.mode == SEL_READY) {
-			getbuttoninfo(e);
-			selcopy(e->xbutton.time);
-		} else
-			selclear_(NULL);
-		sel.mode = SEL_IDLE;
-		tsetdirt(sel.nb.y, sel.ne.y);
-	}
+	if (e->xbutton.button == Button2)
+		selpaste(NULL);
+	else if (e->xbutton.button == Button1)
+		mousesel(e, 1);
 }
 
 void
 bmotion(XEvent *e)
 {
-	int oldey, oldex, oldsby, oldsey;
-
 	if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
 		mousereport(e);
 		return;
 	}
 
-	if (!sel.mode)
-		return;
+	mousesel(e, 0);
+}
+
+void
+cresize(int width, int height)
+{
+	int col, row;
+
+	if (width != 0)
+		win.w = width;
+	if (height != 0)
+		win.h = height;
 
-	sel.mode = SEL_READY;
-	oldey = sel.oe.y;
-	oldex = sel.oe.x;
-	oldsby = sel.nb.y;
-	oldsey = sel.ne.y;
-	getbuttoninfo(e);
+	col = (win.w - 2 * borderpx) / win.cw;
+	row = (win.h - 2 * borderpx) / win.ch;
 
-	if (oldey != sel.oe.y || oldex != sel.oe.x)
-		tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
+	tresize(col, row);
+	xresize(col, row);
+	ttyresize(win.tw, win.th);
 }
 
 void
@@ -566,6 +676,9 @@ xresize(int col, int row)
 			DefaultDepth(xw.dpy, xw.scr));
 	XftDrawChange(xw.draw, xw.buf);
 	xclear(0, 0, win.w, win.h);
+
+	/* resize to new width */
+	xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
 }
 
 ushort
@@ -605,7 +718,7 @@ xloadcols(void)
 	static int loaded;
 	Color *cp;
 
-	dc.collen = MAX(colornamelen, 256);
+	dc.collen = MAX(LEN(colorname), 256);
 	dc.col = xmalloc(dc.collen * sizeof(Color));
 
 	if (loaded) {
@@ -926,6 +1039,9 @@ xinit(void)
 	XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
 	XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
 
+	/* font spec buffer */
+	xw.specbuf = xmalloc(term.col * sizeof(GlyphFontSpec));
+
 	/* Xft rendering context */
 	xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
 
@@ -979,6 +1095,10 @@ xinit(void)
 	xhints();
 	XSync(xw.dpy, False);
 
+	clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1);
+	clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2);
+	xsel.primary = NULL;
+	xsel.clipboard = NULL;
 	xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0);
 	if (xsel.xtarget == None)
 		xsel.xtarget = XA_STRING;
@@ -1270,7 +1390,6 @@ xdrawcursor(void)
 	static int oldx = 0, oldy = 0;
 	int curx;
 	Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og;
-	int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
 	Color drawcol;
 
 	LIMIT(oldx, 0, term.col-1);
@@ -1286,7 +1405,7 @@ xdrawcursor(void)
 
 	/* remove the old cursor */
 	og = term.line[oldy][oldx];
-	if (ena_sel && selected(oldx, oldy))
+	if (selected(oldx, oldy))
 		og.mode ^= ATTR_REVERSE;
 	xdrawglyph(og, oldx, oldy);
 
@@ -1300,7 +1419,7 @@ xdrawcursor(void)
 	if (IS_SET(MODE_REVERSE)) {
 		g.mode |= ATTR_REVERSE;
 		g.bg = defaultfg;
-		if (ena_sel && selected(term.c.x, term.c.y)) {
+		if (selected(term.c.x, term.c.y)) {
 			drawcol = dc.col[defaultcs];
 			g.fg = defaultrcs;
 		} else {
@@ -1308,7 +1427,7 @@ xdrawcursor(void)
 			g.fg = defaultcs;
 		}
 	} else {
-		if (ena_sel && selected(term.c.x, term.c.y)) {
+		if (selected(term.c.x, term.c.y)) {
 			drawcol = dc.col[defaultrcs];
 			g.fg = defaultfg;
 			g.bg = defaultrcs;
@@ -1381,6 +1500,7 @@ void
 xsettitle(char *p)
 {
 	XTextProperty prop;
+	DEFAULT(p, "st");
 
 	Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
 			&prop);
@@ -1406,7 +1526,6 @@ drawregion(int x1, int y1, int x2, int y2)
 	int i, x, y, ox, numspecs;
 	Glyph base, new;
 	XftGlyphFontSpec *specs;
-	int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
 
 	if (!(win.state & WIN_VISIBLE))
 		return;
@@ -1417,7 +1536,7 @@ drawregion(int x1, int y1, int x2, int y2)
 
 		term.dirty[y] = 0;
 
-		specs = term.specbuf;
+		specs = xw.specbuf;
 		numspecs = xmakeglyphfontspecs(specs, &term.line[y][x1], x2 - x1, x1, y);
 
 		i = ox = 0;
@@ -1425,7 +1544,7 @@ drawregion(int x1, int y1, int x2, int y2)
 			new = term.line[y][x];
 			if (new.mode == ATTR_WDUMMY)
 				continue;
-			if (ena_sel && selected(x, y))
+			if (selected(x, y))
 				new.mode ^= ATTR_REVERSE;
 			if (i > 0 && ATTRCMP(base, new)) {
 				xdrawglyphfontspecs(specs, base, i, ox, y);
@@ -1472,6 +1591,16 @@ xsetpointermotion(int set)
 	XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs);
 }
 
+int
+xsetcursor(int cursor)
+{
+	DEFAULT(cursor, 1);
+	if (!BETWEEN(cursor, 0, 6))
+		return 1;
+	win.cursor = cursor;
+	return 0;
+}
+
 void
 xseturgency(int add)
 {
@@ -1483,9 +1612,12 @@ xseturgency(int add)
 }
 
 void
-xbell(int vol)
+xbell(void)
 {
-	XkbBell(xw.dpy, xw.win, vol, (Atom)NULL);
+	if (!(win.state & WIN_FOCUSED))
+		xseturgency(1);
+	if (bellvolume)
+		XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL);
 }
 
 void
@@ -1501,13 +1633,56 @@ focus(XEvent *ev)
 		win.state |= WIN_FOCUSED;
 		xseturgency(0);
 		if (IS_SET(MODE_FOCUS))
-			ttywrite("\033[I", 3);
+			ttywrite("\033[I", 3, 0);
 	} else {
 		XUnsetICFocus(xw.xic);
 		win.state &= ~WIN_FOCUSED;
 		if (IS_SET(MODE_FOCUS))
-			ttywrite("\033[O", 3);
+			ttywrite("\033[O", 3, 0);
+	}
+}
+
+int
+match(uint mask, uint state)
+{
+	return mask == XK_ANY_MOD || mask == (state & ~ignoremod);
+}
+
+char*
+kmap(KeySym k, uint state)
+{
+	Key *kp;
+	int i;
+
+	/* Check for mapped keys out of X11 function keys. */
+	for (i = 0; i < LEN(mappedkeys); i++) {
+		if (mappedkeys[i] == k)
+			break;
+	}
+	if (i == LEN(mappedkeys)) {
+		if ((k & 0xFFFF) < 0xFD00)
+			return NULL;
+	}
+
+	for (kp = key; kp < key + LEN(key); kp++) {
+		if (kp->k != k)
+			continue;
+
+		if (!match(kp->mask, state))
+			continue;
+
+		if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0)
+			continue;
+		if (term.numlock && kp->appkey == 2)
+			continue;
+
+		if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0)
+			continue;
+
+		return kp->s;
 	}
+
+	return NULL;
 }
 
 void
@@ -1526,7 +1701,7 @@ kpress(XEvent *ev)
 
 	len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status);
 	/* 1. shortcuts */
-	for (bp = shortcuts; bp < shortcuts + shortcutslen; bp++) {
+	for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
 		if (ksym == bp->keysym && match(bp->mod, e->state)) {
 			bp->func(&(bp->arg));
 			return;
@@ -1535,7 +1710,7 @@ kpress(XEvent *ev)
 
 	/* 2. custom keys from config.h */
 	if ((customkey = kmap(ksym, e->state))) {
-		ttysend(customkey, strlen(customkey));
+		ttywrite(customkey, strlen(customkey), 1);
 		return;
 	}
 
@@ -1554,7 +1729,7 @@ kpress(XEvent *ev)
 			len = 2;
 		}
 	}
-	ttysend(buf, len);
+	ttywrite(buf, len, 1);
 }
 
 
@@ -1586,7 +1761,6 @@ resize(XEvent *e)
 		return;
 
 	cresize(e->xconfigure.width, e->xconfigure.height);
-	ttyresize();
 }
 
 void
@@ -1615,9 +1789,8 @@ run(void)
 		}
 	} while (ev.type != MapNotify);
 
+	ttynew(opt_line, opt_io, opt_cmd);
 	cresize(w, h);
-	ttynew();
-	ttyresize();
 
 	clock_gettime(CLOCK_MONOTONIC, &last);
 	lastblink = last;
@@ -1698,6 +1871,19 @@ run(void)
 	}
 }
 
+void
+usage(void)
+{
+	die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]"
+	    " [-n name] [-o file]\n"
+	    "          [-T title] [-t title] [-w windowid]"
+	    " [[-e] command [args ...]]\n"
+	    "       %s [-aiv] [-c class] [-f font] [-g geometry]"
+	    " [-n name] [-o file]\n"
+	    "          [-T title] [-t title] [-w windowid] -l line"
+	    " [stty_args ...]\n", argv0, argv0);
+}
+
 int
 main(int argc, char *argv[])
 {