Compare commits
	
		
			10 Commits
		
	
	
		
			6576e2afad
			...
			130d81b14c
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 130d81b14c | |||
| cc2d3a597a | |||
| 0749fc4857 | |||
| 539dfd0bac | |||
| af62c87aa8 | |||
| aa9d16a41b | |||
| 71d1f725b3 | |||
| fb7383d52f | |||
| 3a32e68b82 | |||
| a8d62fe07b | 
							
								
								
									
										79
									
								
								config.def.h
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								config.def.h
									
									
									
									
									
								
							| @@ -56,6 +56,12 @@ int allowwindowops = 0; | ||||
| static double minlatency = 8; | ||||
| static double maxlatency = 33; | ||||
|  | ||||
| /* | ||||
|  * Synchronized-Update timeout in ms | ||||
|  * https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec | ||||
|  */ | ||||
| static uint su_timeout = 200; | ||||
|  | ||||
| /* | ||||
|  * blinking timeout (set to 0 to disable blinking) for the terminal blinking | ||||
|  * attribute. | ||||
| @@ -93,35 +99,30 @@ 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 */ | ||||
| 	"black", | ||||
| 	"red3", | ||||
| 	"green3", | ||||
| 	"yellow3", | ||||
| 	"blue2", | ||||
| 	"magenta3", | ||||
| 	"cyan3", | ||||
| 	"gray90", | ||||
| 	[0] = "#282828", /* hard contrast: #1d2021 / soft contrast: #32302f */ | ||||
| 	[1] = "#cc241d", /* red     */ | ||||
| 	[2] = "#98971a", /* green   */ | ||||
| 	[3] = "#d79921", /* yellow  */ | ||||
| 	[4] = "#458588", /* blue    */ | ||||
| 	[5] = "#b16286", /* magenta */ | ||||
| 	[6] = "#689d6a", /* cyan    */ | ||||
| 	[7] = "#a89984", /* white   */ | ||||
|  | ||||
| 	/* 8 bright colors */ | ||||
| 	"gray50", | ||||
| 	"red", | ||||
| 	"green", | ||||
| 	"yellow", | ||||
| 	"#5c5cff", | ||||
| 	"magenta", | ||||
| 	"cyan", | ||||
| 	"white", | ||||
|  | ||||
| 	[255] = 0, | ||||
|  | ||||
| 	/* more colors can be added after 255 to use with DefaultXX */ | ||||
| 	"#cccccc", | ||||
| 	"#555555", | ||||
| 	"gray90", /* default foreground colour */ | ||||
| 	"black", /* default background colour */ | ||||
| 	[8]  = "#928374", /* black   */ | ||||
| 	[9]  = "#fb4934", /* red     */ | ||||
| 	[10] = "#b8bb26", /* green   */ | ||||
| 	[11] = "#fabd2f", /* yellow  */ | ||||
| 	[12] = "#83a598", /* blue    */ | ||||
| 	[13] = "#d3869b", /* magenta */ | ||||
| 	[14] = "#8ec07c", /* cyan    */ | ||||
| 	[15] = "#ebdbb2", /* white   */ | ||||
| }; | ||||
|  | ||||
|  | ||||
| @@ -129,9 +130,9 @@ static const char *colorname[] = { | ||||
|  * Default colors (colorname index) | ||||
|  * foreground, background, cursor, reverse cursor | ||||
|  */ | ||||
| unsigned int defaultfg = 258; | ||||
| unsigned int defaultbg = 259; | ||||
| unsigned int defaultcs = 256; | ||||
| unsigned int defaultfg = 15; | ||||
| unsigned int defaultbg = 0; | ||||
| unsigned int defaultcs = 15; | ||||
| static unsigned int defaultrcs = 257; | ||||
|  | ||||
| /* | ||||
| @@ -472,3 +473,27 @@ static char ascii_printable[] = | ||||
| 	" !\"#$%&'()*+,-./0123456789:;<=>?" | ||||
| 	"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" | ||||
| 	"`abcdefghijklmnopqrstuvwxyz{|}~"; | ||||
|  | ||||
| /** | ||||
|  * Undercurl style. Set UNDERCURL_STYLE to one of the available styles. | ||||
|  * | ||||
|  * Curly: Dunno how to draw it *shrug* | ||||
|  *  _   _   _   _ | ||||
|  * ( ) ( ) ( ) ( ) | ||||
|  *	 (_) (_) (_) (_) | ||||
|  * | ||||
|  * Spiky: | ||||
|  * /\  /\   /\	/\ | ||||
|  *   \/  \/	  \/ | ||||
|  * | ||||
|  * Capped: | ||||
|  *	_     _     _ | ||||
|  * / \   / \   / \ | ||||
|  *    \_/   \_/ | ||||
|  */ | ||||
| // Available styles | ||||
| #define UNDERCURL_CURLY 0 | ||||
| #define UNDERCURL_SPIKY 1 | ||||
| #define UNDERCURL_CAPPED 2 | ||||
| // Active style | ||||
| #define UNDERCURL_STYLE UNDERCURL_SPIKY | ||||
|   | ||||
| @@ -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` | ||||
|  | ||||
|   | ||||
							
								
								
									
										133
									
								
								st.c
									
									
									
									
									
								
							
							
						
						
									
										133
									
								
								st.c
									
									
									
									
									
								
							| @@ -33,6 +33,7 @@ | ||||
| #define UTF_SIZ       4 | ||||
| #define ESC_BUF_SIZ   (128*UTF_SIZ) | ||||
| #define ESC_ARG_SIZ   16 | ||||
| #define CAR_PER_ARG   4 | ||||
| #define STR_BUF_SIZ   ESC_BUF_SIZ | ||||
| #define STR_ARG_SIZ   ESC_ARG_SIZ | ||||
|  | ||||
| @@ -42,6 +43,8 @@ | ||||
| #define ISCONTROLC1(c)		(BETWEEN(c, 0x80, 0x9f)) | ||||
| #define ISCONTROL(c)		(ISCONTROLC0(c) || ISCONTROLC1(c)) | ||||
| #define ISDELIM(u)		(u && wcschr(worddelimiters, u)) | ||||
| #define STRESCARGREST(n)	((n) == 0 ? strescseq.buf : strescseq.argp[(n)-1] + 1) | ||||
| #define STRESCARGJUST(n)	(*(strescseq.argp[n]) = '\0', STRESCARGREST(n)) | ||||
|  | ||||
| enum term_mode { | ||||
| 	MODE_WRAP        = 1 << 0, | ||||
| @@ -139,6 +142,7 @@ typedef struct { | ||||
| 	int arg[ESC_ARG_SIZ]; | ||||
| 	int narg;              /* nb of args */ | ||||
| 	char mode[2]; | ||||
| 	int carg[ESC_ARG_SIZ][CAR_PER_ARG]; /* colon args */ | ||||
| } CSIEscape; | ||||
|  | ||||
| /* STR Escape sequence structs */ | ||||
| @@ -148,7 +152,7 @@ typedef struct { | ||||
| 	char *buf;             /* allocated raw string */ | ||||
| 	size_t siz;            /* allocation size */ | ||||
| 	size_t len;            /* raw string length */ | ||||
| 	char *args[STR_ARG_SIZ]; | ||||
| 	char *argp[STR_ARG_SIZ]; /* pointers to the end of nth argument */ | ||||
| 	int narg;              /* nb of args */ | ||||
| } STREscape; | ||||
|  | ||||
| @@ -159,6 +163,7 @@ static void ttywriteraw(const char *, size_t); | ||||
|  | ||||
| static void csidump(void); | ||||
| static void csihandle(void); | ||||
| static void readcolonargs(char **, int, int[][CAR_PER_ARG]); | ||||
| static void csiparse(void); | ||||
| static void csireset(void); | ||||
| static void osc_color_response(int, int, int); | ||||
| @@ -232,6 +237,33 @@ static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; | ||||
| static const Rune utfmin[UTF_SIZ + 1] = {       0,    0,  0x80,  0x800,  0x10000}; | ||||
| static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; | ||||
|  | ||||
| #include <time.h> | ||||
| static int su = 0; | ||||
| struct timespec sutv; | ||||
|  | ||||
| static void | ||||
| tsync_begin() | ||||
| { | ||||
| 	clock_gettime(CLOCK_MONOTONIC, &sutv); | ||||
| 	su = 1; | ||||
| } | ||||
|  | ||||
| static void | ||||
| tsync_end() | ||||
| { | ||||
| 	su = 0; | ||||
| } | ||||
|  | ||||
| int | ||||
| tinsync(uint timeout) | ||||
| { | ||||
| 	struct timespec now; | ||||
| 	if (su && !clock_gettime(CLOCK_MONOTONIC, &now) | ||||
| 	       && TIMEDIFF(now, sutv) >= timeout) | ||||
| 		su = 0; | ||||
| 	return su; | ||||
| } | ||||
|  | ||||
| ssize_t | ||||
| xwrite(int fd, const char *s, size_t len) | ||||
| { | ||||
| @@ -814,6 +846,9 @@ ttynew(const char *line, char *cmd, const char *out, char **args) | ||||
| 	return cmdfd; | ||||
| } | ||||
|  | ||||
| static int twrite_aborted = 0; | ||||
| int ttyread_pending() { return twrite_aborted; } | ||||
|  | ||||
| size_t | ||||
| ttyread(void) | ||||
| { | ||||
| @@ -822,7 +857,7 @@ ttyread(void) | ||||
| 	int ret, written; | ||||
|  | ||||
| 	/* append read bytes to unprocessed bytes */ | ||||
| 	ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); | ||||
| 	ret = twrite_aborted ? 1 : read(cmdfd, buf+buflen, LEN(buf)-buflen); | ||||
|  | ||||
| 	switch (ret) { | ||||
| 	case 0: | ||||
| @@ -830,7 +865,7 @@ ttyread(void) | ||||
| 	case -1: | ||||
| 		die("couldn't read from shell: %s\n", strerror(errno)); | ||||
| 	default: | ||||
| 		buflen += ret; | ||||
| 		buflen += twrite_aborted ? 0 : ret; | ||||
| 		written = twrite(buf, buflen, 0); | ||||
| 		buflen -= written; | ||||
| 		/* keep any incomplete UTF-8 byte sequence for the next call */ | ||||
| @@ -990,6 +1025,7 @@ tsetdirtattr(int attr) | ||||
| void | ||||
| tfulldirt(void) | ||||
| { | ||||
| 	tsync_end(); | ||||
| 	tsetdirt(0, term.row-1); | ||||
| } | ||||
|  | ||||
| @@ -1127,6 +1163,28 @@ tnewline(int first_col) | ||||
| 	tmoveto(first_col ? 0 : term.c.x, y); | ||||
| } | ||||
|  | ||||
| void | ||||
| readcolonargs(char **p, int cursor, int params[][CAR_PER_ARG]) | ||||
| { | ||||
| 	int i = 0; | ||||
| 	for (; i < CAR_PER_ARG; i++) | ||||
| 		params[cursor][i] = -1; | ||||
|  | ||||
| 	if (**p != ':') | ||||
| 		return; | ||||
|  | ||||
| 	char *np = NULL; | ||||
| 	i = 0; | ||||
|  | ||||
| 	while (**p == ':' && i < CAR_PER_ARG) { | ||||
| 		while (**p == ':') | ||||
| 			(*p)++; | ||||
| 		params[cursor][i] = strtol(*p, &np, 10); | ||||
| 		*p = np; | ||||
| 		i++; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void | ||||
| csiparse(void) | ||||
| { | ||||
| @@ -1149,6 +1207,7 @@ csiparse(void) | ||||
| 			v = -1; | ||||
| 		csiescseq.arg[csiescseq.narg++] = v; | ||||
| 		p = np; | ||||
| 		readcolonargs(&p, csiescseq.narg-1, csiescseq.carg); | ||||
| 		if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) | ||||
| 			break; | ||||
| 		p++; | ||||
| @@ -1365,6 +1424,10 @@ tsetattr(const int *attr, int l) | ||||
| 				ATTR_STRUCK     ); | ||||
| 			term.c.attr.fg = defaultfg; | ||||
| 			term.c.attr.bg = defaultbg; | ||||
| 			term.c.attr.ustyle = -1; | ||||
| 			term.c.attr.ucolor[0] = -1; | ||||
| 			term.c.attr.ucolor[1] = -1; | ||||
| 			term.c.attr.ucolor[2] = -1; | ||||
| 			break; | ||||
| 		case 1: | ||||
| 			term.c.attr.mode |= ATTR_BOLD; | ||||
| @@ -1376,7 +1439,14 @@ tsetattr(const int *attr, int l) | ||||
| 			term.c.attr.mode |= ATTR_ITALIC; | ||||
| 			break; | ||||
| 		case 4: | ||||
| 			term.c.attr.mode |= ATTR_UNDERLINE; | ||||
| 			term.c.attr.ustyle = csiescseq.carg[i][0]; | ||||
|  | ||||
| 			if (term.c.attr.ustyle != 0) | ||||
| 				term.c.attr.mode |= ATTR_UNDERLINE; | ||||
| 			else | ||||
| 				term.c.attr.mode &= ~ATTR_UNDERLINE; | ||||
|  | ||||
| 			term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; | ||||
| 			break; | ||||
| 		case 5: /* slow blink */ | ||||
| 			/* FALLTHROUGH */ | ||||
| @@ -1427,6 +1497,18 @@ tsetattr(const int *attr, int l) | ||||
| 		case 49: | ||||
| 			term.c.attr.bg = defaultbg; | ||||
| 			break; | ||||
| 		case 58: | ||||
| 			term.c.attr.ucolor[0] = csiescseq.carg[i][1]; | ||||
| 			term.c.attr.ucolor[1] = csiescseq.carg[i][2]; | ||||
| 			term.c.attr.ucolor[2] = csiescseq.carg[i][3]; | ||||
| 			term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; | ||||
| 			break; | ||||
| 		case 59: | ||||
| 			term.c.attr.ucolor[0] = -1; | ||||
| 			term.c.attr.ucolor[1] = -1; | ||||
| 			term.c.attr.ucolor[2] = -1; | ||||
| 			term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; | ||||
| 			break; | ||||
| 		default: | ||||
| 			if (BETWEEN(attr[i], 30, 37)) { | ||||
| 				term.c.attr.fg = attr[i] - 30; | ||||
| @@ -1879,29 +1961,30 @@ strhandle(void) | ||||
| 	}; | ||||
|  | ||||
| 	term.esc &= ~(ESC_STR_END|ESC_STR); | ||||
| 	strparse(); | ||||
| 	par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0; | ||||
| 	strescseq.buf[strescseq.len] = '\0'; | ||||
|  | ||||
| 	switch (strescseq.type) { | ||||
| 	case ']': /* OSC -- Operating System Command */ | ||||
| 		strparse(); | ||||
| 		par = (narg = strescseq.narg) ? atoi(STRESCARGJUST(0)) : 0; | ||||
| 		switch (par) { | ||||
| 		case 0: | ||||
| 			if (narg > 1) { | ||||
| 				xsettitle(strescseq.args[1]); | ||||
| 				xseticontitle(strescseq.args[1]); | ||||
| 				xsettitle(STRESCARGREST(1)); | ||||
| 				xseticontitle(STRESCARGREST(1)); | ||||
| 			} | ||||
| 			return; | ||||
| 		case 1: | ||||
| 			if (narg > 1) | ||||
| 				xseticontitle(strescseq.args[1]); | ||||
| 				xseticontitle(STRESCARGREST(1)); | ||||
| 			return; | ||||
| 		case 2: | ||||
| 			if (narg > 1) | ||||
| 				xsettitle(strescseq.args[1]); | ||||
| 				xsettitle(STRESCARGREST(1)); | ||||
| 			return; | ||||
| 		case 52: | ||||
| 			if (narg > 2 && allowwindowops) { | ||||
| 				dec = base64dec(strescseq.args[2]); | ||||
| 				dec = base64dec(STRESCARGJUST(2)); | ||||
| 				if (dec) { | ||||
| 					xsetsel(dec); | ||||
| 					xclipcopy(); | ||||
| @@ -1915,7 +1998,7 @@ strhandle(void) | ||||
| 		case 12: | ||||
| 			if (narg < 2) | ||||
| 				break; | ||||
| 			p = strescseq.args[1]; | ||||
| 			p = STRESCARGREST(1); | ||||
| 			if ((j = par - 10) < 0 || j >= LEN(osc_table)) | ||||
| 				break; /* shouldn't be possible */ | ||||
|  | ||||
| @@ -1931,10 +2014,10 @@ strhandle(void) | ||||
| 		case 4: /* color set */ | ||||
| 			if (narg < 3) | ||||
| 				break; | ||||
| 			p = strescseq.args[2]; | ||||
| 			p = STRESCARGJUST(2); | ||||
| 			/* FALLTHROUGH */ | ||||
| 		case 104: /* color reset */ | ||||
| 			j = (narg > 1) ? atoi(strescseq.args[1]) : -1; | ||||
| 			j = (narg > 1) ? atoi(STRESCARGJUST(1)) : -1; | ||||
|  | ||||
| 			if (p && !strcmp(p, "?")) { | ||||
| 				osc_color_response(j, 0, 1); | ||||
| @@ -1956,9 +2039,15 @@ strhandle(void) | ||||
| 		} | ||||
| 		break; | ||||
| 	case 'k': /* old title set compatibility */ | ||||
| 		xsettitle(strescseq.args[0]); | ||||
| 		xsettitle(strescseq.buf); | ||||
| 		return; | ||||
| 	case 'P': /* DCS -- Device Control String */ | ||||
| 		/* https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec */ | ||||
| 		if (strstr(strescseq.buf, "=1s") == strescseq.buf) | ||||
| 			tsync_begin();  /* BSU */ | ||||
| 		else if (strstr(strescseq.buf, "=2s") == strescseq.buf) | ||||
| 			tsync_end();  /* ESU */ | ||||
| 		return; | ||||
| 	case '_': /* APC -- Application Program Command */ | ||||
| 	case '^': /* PM -- Privacy Message */ | ||||
| 		return; | ||||
| @@ -1975,18 +2064,17 @@ strparse(void) | ||||
| 	char *p = strescseq.buf; | ||||
|  | ||||
| 	strescseq.narg = 0; | ||||
| 	strescseq.buf[strescseq.len] = '\0'; | ||||
|  | ||||
| 	if (*p == '\0') | ||||
| 		return; | ||||
|  | ||||
| 	while (strescseq.narg < STR_ARG_SIZ) { | ||||
| 		strescseq.args[strescseq.narg++] = p; | ||||
| 		while ((c = *p) != ';' && c != '\0') | ||||
| 			++p; | ||||
| 			p++; | ||||
| 		strescseq.argp[strescseq.narg++] = p; | ||||
| 		if (c == '\0') | ||||
| 			return; | ||||
| 		*p++ = '\0'; | ||||
| 		p++; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -2507,6 +2595,9 @@ twrite(const char *buf, int buflen, int show_ctrl) | ||||
| 	Rune u; | ||||
| 	int n; | ||||
|  | ||||
| 	int su0 = su; | ||||
| 	twrite_aborted = 0; | ||||
|  | ||||
| 	for (n = 0; n < buflen; n += charsize) { | ||||
| 		if (IS_SET(MODE_UTF8)) { | ||||
| 			/* process a complete utf8 char */ | ||||
| @@ -2517,6 +2608,10 @@ twrite(const char *buf, int buflen, int show_ctrl) | ||||
| 			u = buf[n] & 0xFF; | ||||
| 			charsize = 1; | ||||
| 		} | ||||
| 		if (su0 && !su) { | ||||
| 			twrite_aborted = 1; | ||||
| 			break;  // ESU - allow rendering before a new BSU | ||||
| 		} | ||||
| 		if (show_ctrl && ISCONTROL(u)) { | ||||
| 			if (u & 0x80) { | ||||
| 				u &= 0x7f; | ||||
|   | ||||
							
								
								
									
										4
									
								
								st.h
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								st.h
									
									
									
									
									
								
							| @@ -34,6 +34,7 @@ enum glyph_attribute { | ||||
| 	ATTR_WIDE       = 1 << 9, | ||||
| 	ATTR_WDUMMY     = 1 << 10, | ||||
| 	ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, | ||||
| 	ATTR_DIRTYUNDERLINE = 1 << 15, | ||||
| }; | ||||
|  | ||||
| enum selection_mode { | ||||
| @@ -65,6 +66,8 @@ typedef struct { | ||||
| 	ushort mode;      /* attribute flags */ | ||||
| 	uint32_t fg;      /* foreground  */ | ||||
| 	uint32_t bg;      /* background  */ | ||||
| 	int ustyle;	  /* underline style */ | ||||
| 	int ucolor[3];    /* underline color */ | ||||
| } Glyph; | ||||
|  | ||||
| typedef Glyph *Line; | ||||
| @@ -124,3 +127,4 @@ extern unsigned int tabspaces; | ||||
| extern unsigned int defaultfg; | ||||
| extern unsigned int defaultbg; | ||||
| extern unsigned int defaultcs; | ||||
| extern float alpha; | ||||
|   | ||||
							
								
								
									
										2
									
								
								st.info
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								st.info
									
									
									
									
									
								
							| @@ -1,4 +1,5 @@ | ||||
| st-mono| simpleterm monocolor, | ||||
| 	Su, | ||||
| 	acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, | ||||
| 	am, | ||||
| 	bce, | ||||
| @@ -191,6 +192,7 @@ st-mono| simpleterm monocolor, | ||||
| 	Ms=\E]52;%p1%s;%p2%s\007, | ||||
| 	Se=\E[2 q, | ||||
| 	Ss=\E[%p1%d q, | ||||
| 	Sync=\EP=%p1%ds\E\\, | ||||
|  | ||||
| st| simpleterm, | ||||
| 	use=st-mono, | ||||
|   | ||||
							
								
								
									
										555
									
								
								x.c
									
									
									
									
									
								
							
							
						
						
									
										555
									
								
								x.c
									
									
									
									
									
								
							| @@ -45,6 +45,14 @@ typedef struct { | ||||
| 	signed char appcursor; /* application cursor */ | ||||
| } Key; | ||||
|  | ||||
| /* Undercurl slope types */ | ||||
| enum undercurl_slope_type { | ||||
| 	UNDERCURL_SLOPE_ASCENDING = 0, | ||||
| 	UNDERCURL_SLOPE_TOP_CAP = 1, | ||||
| 	UNDERCURL_SLOPE_DESCENDING = 2, | ||||
| 	UNDERCURL_SLOPE_BOTTOM_CAP = 3 | ||||
| }; | ||||
|  | ||||
| /* X modifiers */ | ||||
| #define XK_ANY_MOD    UINT_MAX | ||||
| #define XK_NO_MOD     0 | ||||
| @@ -81,6 +89,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 */ | ||||
| @@ -105,6 +114,7 @@ typedef struct { | ||||
| 	XSetWindowAttributes attrs; | ||||
| 	int scr; | ||||
| 	int isfixed; /* is fixed geometry? */ | ||||
| 	int depth; /* bit depth */ | ||||
| 	int l, t; /* left and top offset */ | ||||
| 	int gm; /* geometry mask */ | ||||
| } XWindow; | ||||
| @@ -243,6 +253,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; | ||||
| @@ -331,7 +342,7 @@ ttysend(const Arg *arg) | ||||
| int | ||||
| evcol(XEvent *e) | ||||
| { | ||||
| 	int x = e->xbutton.x - borderpx; | ||||
| 	int x = e->xbutton.x - win.hborderpx; | ||||
| 	LIMIT(x, 0, win.tw - 1); | ||||
| 	return x / win.cw; | ||||
| } | ||||
| @@ -339,7 +350,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; | ||||
| } | ||||
| @@ -686,6 +697,8 @@ setsel(char *str, Time t) | ||||
| 	XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); | ||||
| 	if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) | ||||
| 		selclear(); | ||||
|  | ||||
| 	xclipcopy(); | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -709,7 +722,9 @@ brelease(XEvent *e) | ||||
|  | ||||
| 	if (mouseaction(e, 1)) | ||||
| 		return; | ||||
| 	if (btn == Button1) | ||||
| 	if (btn == Button3) | ||||
| 		selpaste(NULL); | ||||
| 	else if (btn == Button1) | ||||
| 		mousesel(e, 1); | ||||
| } | ||||
|  | ||||
| @@ -739,6 +754,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); | ||||
| @@ -752,7 +770,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); | ||||
|  | ||||
| @@ -812,6 +830,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; | ||||
| } | ||||
|  | ||||
| @@ -869,8 +894,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; | ||||
| @@ -1134,11 +1159,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()) | ||||
| @@ -1148,12 +1185,12 @@ 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 */ | ||||
| 	win.w = 2 * borderpx + cols * win.cw; | ||||
| 	win.h = 2 * borderpx + rows * win.ch; | ||||
| 	win.w = 2 * win.hborderpx + 2 * borderpx + cols * win.cw; | ||||
| 	win.h = 2 * win.vborderpx + 2 * borderpx + rows * win.ch; | ||||
| 	if (xw.gm & XNegative) | ||||
| 		xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; | ||||
| 	if (xw.gm & YNegative) | ||||
| @@ -1168,19 +1205,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); | ||||
|  | ||||
| @@ -1242,7 +1275,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; | ||||
| @@ -1371,11 +1404,56 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x | ||||
| 	return numspecs; | ||||
| } | ||||
|  | ||||
| static int isSlopeRising (int x, int iPoint, int waveWidth) | ||||
| { | ||||
| 	//    .     .     .     . | ||||
| 	//   / \   / \   / \   / \ | ||||
| 	//  /   \ /   \ /   \ /   \ | ||||
| 	// .     .     .     .     . | ||||
|  | ||||
| 	// Find absolute `x` of point | ||||
| 	x += iPoint * (waveWidth/2); | ||||
|  | ||||
| 	// Find index of absolute wave | ||||
| 	int absSlope = x / ((float)waveWidth/2); | ||||
|  | ||||
| 	return (absSlope % 2); | ||||
| } | ||||
|  | ||||
| static int getSlope (int x, int iPoint, int waveWidth) | ||||
| { | ||||
| 	// Sizes: Caps are half width of slopes | ||||
| 	//    1_2       1_2       1_2      1_2 | ||||
| 	//   /   \     /   \     /   \    /   \ | ||||
| 	//  /     \   /     \   /     \  /     \ | ||||
| 	// 0       3_0       3_0      3_0       3_ | ||||
| 	// <2->    <1>         <---6----> | ||||
|  | ||||
| 	// Find type of first point | ||||
| 	int firstType; | ||||
| 	x -= (x / waveWidth) * waveWidth; | ||||
| 	if (x < (waveWidth * (2.f/6.f))) | ||||
| 		firstType = UNDERCURL_SLOPE_ASCENDING; | ||||
| 	else if (x < (waveWidth * (3.f/6.f))) | ||||
| 		firstType = UNDERCURL_SLOPE_TOP_CAP; | ||||
| 	else if (x < (waveWidth * (5.f/6.f))) | ||||
| 		firstType = UNDERCURL_SLOPE_DESCENDING; | ||||
| 	else | ||||
| 		firstType = UNDERCURL_SLOPE_BOTTOM_CAP; | ||||
|  | ||||
| 	// Find type of given point | ||||
| 	int pointType = (iPoint % 4); | ||||
| 	pointType += firstType; | ||||
| 	pointType %= 4; | ||||
|  | ||||
| 	return pointType; | ||||
| } | ||||
|  | ||||
| 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; | ||||
| @@ -1412,10 +1490,6 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i | ||||
| 		bg = &dc.col[base.bg]; | ||||
| 	} | ||||
|  | ||||
| 	/* Change basic system colors [0-7] to bright system colors [8-15] */ | ||||
| 	if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7)) | ||||
| 		fg = &dc.col[base.fg + 8]; | ||||
|  | ||||
| 	if (IS_SET(MODE_REVERSE)) { | ||||
| 		if (fg == &dc.col[defaultfg]) { | ||||
| 			fg = &dc.col[defaultbg]; | ||||
| @@ -1465,17 +1539,17 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i | ||||
|  | ||||
| 	/* Intelligent cleaning up of the borders. */ | ||||
| 	if (x == 0) { | ||||
| 		xclear(0, (y == 0)? 0 : winy, borderpx, | ||||
| 		xclear(0, (y == 0)? 0 : winy, win.hborderpx, | ||||
| 			winy + win.ch + | ||||
| 			((winy + win.ch >= borderpx + win.th)? win.h : 0)); | ||||
| 			((winy + win.ch >= win.vborderpx + win.th)? win.h : 0)); | ||||
| 	} | ||||
| 	if (winx + width >= borderpx + win.tw) { | ||||
| 	if (winx + width >= win.hborderpx + win.tw) { | ||||
| 		xclear(winx + width, (y == 0)? 0 : winy, win.w, | ||||
| 			((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch))); | ||||
| 			((winy + win.ch >= win.vborderpx + win.th)? win.h : (winy + win.ch))); | ||||
| 	} | ||||
| 	if (y == 0) | ||||
| 		xclear(winx, 0, winx + width, borderpx); | ||||
| 	if (winy + win.ch >= borderpx + win.th) | ||||
| 		xclear(winx, 0, winx + width, win.vborderpx); | ||||
| 	if (winy + win.ch >= win.vborderpx + win.th) | ||||
| 		xclear(winx, winy + win.ch, winx + width, win.h); | ||||
|  | ||||
| 	/* Clean up the region we want to draw to. */ | ||||
| @@ -1493,8 +1567,357 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i | ||||
|  | ||||
| 	/* Render underline and strikethrough. */ | ||||
| 	if (base.mode & ATTR_UNDERLINE) { | ||||
| 		XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1, | ||||
| 				width, 1); | ||||
| 		// Underline Color | ||||
| 		const int widthThreshold  = 28; // +1 width every widthThreshold px of font | ||||
| 		int wlw = (win.ch / widthThreshold) + 1; // Wave Line Width | ||||
| 		int linecolor; | ||||
| 		if ((base.ucolor[0] >= 0) && | ||||
| 			!(base.mode & ATTR_BLINK && win.mode & MODE_BLINK) && | ||||
| 			!(base.mode & ATTR_INVISIBLE) | ||||
| 		) { | ||||
| 			// Special color for underline | ||||
| 			// Index | ||||
| 			if (base.ucolor[1] < 0) { | ||||
| 				linecolor = dc.col[base.ucolor[0]].pixel; | ||||
| 			} | ||||
| 			// RGB | ||||
| 			else { | ||||
| 				XColor lcolor; | ||||
| 				lcolor.red = base.ucolor[0] * 257; | ||||
| 				lcolor.green = base.ucolor[1] * 257; | ||||
| 				lcolor.blue = base.ucolor[2] * 257; | ||||
| 				lcolor.flags = DoRed | DoGreen | DoBlue; | ||||
| 				XAllocColor(xw.dpy, xw.cmap, &lcolor); | ||||
| 				linecolor = lcolor.pixel; | ||||
| 			} | ||||
| 		} else { | ||||
| 			// Foreground color for underline | ||||
| 			linecolor = fg->pixel; | ||||
| 		} | ||||
|  | ||||
| 		XGCValues ugcv = { | ||||
| 			.foreground = linecolor, | ||||
| 			.line_width = wlw, | ||||
| 			.line_style = LineSolid, | ||||
| 			.cap_style = CapNotLast | ||||
| 		}; | ||||
|  | ||||
| 		GC ugc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw), | ||||
| 			GCForeground | GCLineWidth | GCLineStyle | GCCapStyle, | ||||
| 			&ugcv); | ||||
|  | ||||
| 		// Underline Style | ||||
| 		if (base.ustyle != 3) { | ||||
| 			//XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, width, 1); | ||||
| 			XFillRectangle(xw.dpy, XftDrawDrawable(xw.draw), ugc, winx, | ||||
| 				winy + dc.font.ascent + 1, width, wlw); | ||||
| 		} else if (base.ustyle == 3) { | ||||
| 			int ww = win.cw;//width; | ||||
| 			int wh = dc.font.descent - wlw/2 - 1;//r.height/7; | ||||
| 			int wx = winx; | ||||
| 			int wy = winy + win.ch - dc.font.descent; | ||||
|  | ||||
| #if UNDERCURL_STYLE == UNDERCURL_CURLY | ||||
| 			// Draw waves | ||||
| 			int narcs = charlen * 2 + 1; | ||||
| 			XArc *arcs = xmalloc(sizeof(XArc) * narcs); | ||||
|  | ||||
| 			int i = 0; | ||||
| 			for (i = 0; i < charlen-1; i++) { | ||||
| 				arcs[i*2] = (XArc) { | ||||
| 					.x = wx + win.cw * i + ww / 4, | ||||
| 					.y = wy, | ||||
| 					.width = win.cw / 2, | ||||
| 					.height = wh, | ||||
| 					.angle1 = 0, | ||||
| 					.angle2 = 180 * 64 | ||||
| 				}; | ||||
| 				arcs[i*2+1] = (XArc) { | ||||
| 					.x = wx + win.cw * i + ww * 0.75, | ||||
| 					.y = wy, | ||||
| 					.width = win.cw/2, | ||||
| 					.height = wh, | ||||
| 					.angle1 = 180 * 64, | ||||
| 					.angle2 = 180 * 64 | ||||
| 				}; | ||||
| 			} | ||||
| 			// Last wave | ||||
| 			arcs[i*2] = (XArc) {wx + ww * i + ww / 4, wy, ww / 2, wh, | ||||
| 			0, 180 * 64 }; | ||||
| 			// Last wave tail | ||||
| 			arcs[i*2+1] = (XArc) {wx + ww * i + ww * 0.75, wy, ceil(ww / 2.), | ||||
| 			wh, 180 * 64, 90 * 64}; | ||||
| 			// First wave tail | ||||
| 			i++; | ||||
| 			arcs[i*2] = (XArc) {wx - ww/4 - 1, wy, ceil(ww / 2.), wh, 270 * 64, | ||||
| 			90 * 64 }; | ||||
|  | ||||
| 			XDrawArcs(xw.dpy, XftDrawDrawable(xw.draw), ugc, arcs, narcs); | ||||
|  | ||||
| 			free(arcs); | ||||
| #elif UNDERCURL_STYLE == UNDERCURL_SPIKY | ||||
| 			// Make the underline corridor larger | ||||
| 			/* | ||||
| 			wy -= wh; | ||||
| 			*/ | ||||
| 			wh *= 2; | ||||
|  | ||||
| 			// Set the angle of the slope to 45° | ||||
| 			ww = wh; | ||||
|  | ||||
| 			// Position of wave is independent of word, it's absolute | ||||
| 			wx = (wx / (ww/2)) * (ww/2); | ||||
|  | ||||
| 			int marginStart = winx - wx; | ||||
|  | ||||
| 			// Calculate number of points with floating precision | ||||
| 			float n = width;					// Width of word in pixels | ||||
| 			n = (n / ww) * 2;					// Number of slopes (/ or \) | ||||
| 			n += 2;								// Add two last points | ||||
| 			int npoints = n;					// Convert to int | ||||
|  | ||||
| 			// Total length of underline | ||||
| 			float waveLength = 0; | ||||
|  | ||||
| 			if (npoints >= 3) { | ||||
| 				// We add an aditional slot in case we use a bonus point | ||||
| 				XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1)); | ||||
|  | ||||
| 				// First point (Starts with the word bounds) | ||||
| 				points[0] = (XPoint) { | ||||
| 					.x = wx + marginStart, | ||||
| 					.y = (isSlopeRising(wx, 0, ww)) | ||||
| 						? (wy - marginStart + ww/2.f) | ||||
| 						: (wy + marginStart) | ||||
| 				}; | ||||
|  | ||||
| 				// Second point (Goes back to the absolute point coordinates) | ||||
| 				points[1] = (XPoint) { | ||||
| 					.x = (ww/2.f) - marginStart, | ||||
| 					.y = (isSlopeRising(wx, 1, ww)) | ||||
| 						? (ww/2.f - marginStart) | ||||
| 						: (-ww/2.f + marginStart) | ||||
| 				}; | ||||
| 				waveLength += (ww/2.f) - marginStart; | ||||
|  | ||||
| 				// The rest of the points | ||||
| 				for (int i = 2; i < npoints-1; i++) { | ||||
| 					points[i] = (XPoint) { | ||||
| 						.x = ww/2, | ||||
| 						.y = (isSlopeRising(wx, i, ww)) | ||||
| 							? wh/2 | ||||
| 							: -wh/2 | ||||
| 					}; | ||||
| 					waveLength += ww/2; | ||||
| 				} | ||||
|  | ||||
| 				// Last point | ||||
| 				points[npoints-1] = (XPoint) { | ||||
| 					.x = ww/2, | ||||
| 					.y = (isSlopeRising(wx, npoints-1, ww)) | ||||
| 						? wh/2 | ||||
| 						: -wh/2 | ||||
| 				}; | ||||
| 				waveLength += ww/2; | ||||
|  | ||||
| 				// End | ||||
| 				if (waveLength < width) { // Add a bonus point? | ||||
| 					int marginEnd = width - waveLength; | ||||
| 					points[npoints] = (XPoint) { | ||||
| 						.x = marginEnd, | ||||
| 						.y = (isSlopeRising(wx, npoints, ww)) | ||||
| 							? (marginEnd) | ||||
| 							: (-marginEnd) | ||||
| 					}; | ||||
|  | ||||
| 					npoints++; | ||||
| 				} else if (waveLength > width) { // Is last point too far? | ||||
| 					int marginEnd = waveLength - width; | ||||
| 					points[npoints-1].x -= marginEnd; | ||||
| 					if (isSlopeRising(wx, npoints-1, ww)) | ||||
| 						points[npoints-1].y -= (marginEnd); | ||||
| 					else | ||||
| 						points[npoints-1].y += (marginEnd); | ||||
| 				} | ||||
|  | ||||
| 				// Draw the lines | ||||
| 				XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints, | ||||
| 						CoordModePrevious); | ||||
|  | ||||
| 				// Draw a second underline with an offset of 1 pixel | ||||
| 				if ( ((win.ch / (widthThreshold/2)) % 2)) { | ||||
| 					points[0].x++; | ||||
|  | ||||
| 					XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, | ||||
| 							npoints, CoordModePrevious); | ||||
| 				} | ||||
|  | ||||
| 				// Free resources | ||||
| 				free(points); | ||||
| 			} | ||||
| #else // UNDERCURL_CAPPED | ||||
| 			// Cap is half of wave width | ||||
| 			float capRatio = 0.5f; | ||||
|  | ||||
| 			// Make the underline corridor larger | ||||
| 			wh *= 2; | ||||
|  | ||||
| 			// Set the angle of the slope to 45° | ||||
| 			ww = wh; | ||||
| 			ww *= 1 + capRatio; // Add a bit of width for the cap | ||||
|  | ||||
| 			// Position of wave is independent of word, it's absolute | ||||
| 			wx = (wx / ww) * ww; | ||||
|  | ||||
| 			float marginStart; | ||||
| 			switch(getSlope(winx, 0, ww)) { | ||||
| 				case UNDERCURL_SLOPE_ASCENDING: | ||||
| 					marginStart = winx - wx; | ||||
| 					break; | ||||
| 				case UNDERCURL_SLOPE_TOP_CAP: | ||||
| 					marginStart = winx - (wx + (ww * (2.f/6.f))); | ||||
| 					break; | ||||
| 				case UNDERCURL_SLOPE_DESCENDING: | ||||
| 					marginStart = winx - (wx + (ww * (3.f/6.f))); | ||||
| 					break; | ||||
| 				case UNDERCURL_SLOPE_BOTTOM_CAP: | ||||
| 					marginStart = winx - (wx + (ww * (5.f/6.f))); | ||||
| 					break; | ||||
| 			} | ||||
|  | ||||
| 			// Calculate number of points with floating precision | ||||
| 			float n = width;					// Width of word in pixels | ||||
| 												//					   ._. | ||||
| 			n = (n / ww) * 4;					// Number of points (./   \.) | ||||
| 			n += 2;								// Add two last points | ||||
| 			int npoints = n;					// Convert to int | ||||
|  | ||||
| 			// Position of the pen to draw the lines | ||||
| 			float penX = 0; | ||||
| 			float penY = 0; | ||||
|  | ||||
| 			if (npoints >= 3) { | ||||
| 				XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1)); | ||||
|  | ||||
| 				// First point (Starts with the word bounds) | ||||
| 				penX = winx; | ||||
| 				switch (getSlope(winx, 0, ww)) { | ||||
| 					case UNDERCURL_SLOPE_ASCENDING: | ||||
| 						penY = wy + wh/2.f - marginStart; | ||||
| 						break; | ||||
| 					case UNDERCURL_SLOPE_TOP_CAP: | ||||
| 						penY = wy; | ||||
| 						break; | ||||
| 					case UNDERCURL_SLOPE_DESCENDING: | ||||
| 						penY = wy + marginStart; | ||||
| 						break; | ||||
| 					case UNDERCURL_SLOPE_BOTTOM_CAP: | ||||
| 						penY = wy + wh/2.f; | ||||
| 						break; | ||||
| 				} | ||||
| 				points[0].x = penX; | ||||
| 				points[0].y = penY; | ||||
|  | ||||
| 				// Second point (Goes back to the absolute point coordinates) | ||||
| 				switch (getSlope(winx, 1, ww)) { | ||||
| 					case UNDERCURL_SLOPE_ASCENDING: | ||||
| 						penX += ww * (1.f/6.f) - marginStart; | ||||
| 						penY += 0; | ||||
| 						break; | ||||
| 					case UNDERCURL_SLOPE_TOP_CAP: | ||||
| 						penX += ww * (2.f/6.f) - marginStart; | ||||
| 						penY += -wh/2.f + marginStart; | ||||
| 						break; | ||||
| 					case UNDERCURL_SLOPE_DESCENDING: | ||||
| 						penX += ww * (1.f/6.f) - marginStart; | ||||
| 						penY += 0; | ||||
| 						break; | ||||
| 					case UNDERCURL_SLOPE_BOTTOM_CAP: | ||||
| 						penX += ww * (2.f/6.f) - marginStart; | ||||
| 						penY += -marginStart + wh/2.f; | ||||
| 						break; | ||||
| 				} | ||||
| 				points[1].x = penX; | ||||
| 				points[1].y = penY; | ||||
|  | ||||
| 				// The rest of the points | ||||
| 				for (int i = 2; i < npoints; i++) { | ||||
| 					switch (getSlope(winx, i, ww)) { | ||||
| 						case UNDERCURL_SLOPE_ASCENDING: | ||||
| 						case UNDERCURL_SLOPE_DESCENDING: | ||||
| 							penX += ww * (1.f/6.f); | ||||
| 							penY += 0; | ||||
| 							break; | ||||
| 						case UNDERCURL_SLOPE_TOP_CAP: | ||||
| 							penX += ww * (2.f/6.f); | ||||
| 							penY += -wh / 2.f; | ||||
| 							break; | ||||
| 						case UNDERCURL_SLOPE_BOTTOM_CAP: | ||||
| 							penX += ww * (2.f/6.f); | ||||
| 							penY += wh / 2.f; | ||||
| 							break; | ||||
| 					} | ||||
| 					points[i].x = penX; | ||||
| 					points[i].y = penY; | ||||
| 				} | ||||
|  | ||||
| 				// End | ||||
| 				float waveLength = penX - winx; | ||||
| 				if (waveLength < width) { // Add a bonus point? | ||||
| 					int marginEnd = width - waveLength; | ||||
| 					penX += marginEnd; | ||||
| 					switch(getSlope(winx, npoints, ww)) { | ||||
| 						case UNDERCURL_SLOPE_ASCENDING: | ||||
| 						case UNDERCURL_SLOPE_DESCENDING: | ||||
| 							//penY += 0; | ||||
| 							break; | ||||
| 						case UNDERCURL_SLOPE_TOP_CAP: | ||||
| 							penY += -marginEnd; | ||||
| 							break; | ||||
| 						case UNDERCURL_SLOPE_BOTTOM_CAP: | ||||
| 							penY += marginEnd; | ||||
| 							break; | ||||
| 					} | ||||
|  | ||||
| 					points[npoints].x = penX; | ||||
| 					points[npoints].y = penY; | ||||
|  | ||||
| 					npoints++; | ||||
| 				} else if (waveLength > width) { // Is last point too far? | ||||
| 					int marginEnd = waveLength - width; | ||||
| 					points[npoints-1].x -= marginEnd; | ||||
| 					switch(getSlope(winx, npoints-1, ww)) { | ||||
| 						case UNDERCURL_SLOPE_TOP_CAP: | ||||
| 							points[npoints-1].y += marginEnd; | ||||
| 							break; | ||||
| 						case UNDERCURL_SLOPE_BOTTOM_CAP: | ||||
| 							points[npoints-1].y -= marginEnd; | ||||
| 							break; | ||||
| 						default: | ||||
| 							break; | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				// Draw the lines | ||||
| 				XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints, | ||||
| 						CoordModeOrigin); | ||||
|  | ||||
| 				// Draw a second underline with an offset of 1 pixel | ||||
| 				if ( ((win.ch / (widthThreshold/2)) % 2)) { | ||||
| 					for (int i = 0; i < npoints; i++) | ||||
| 						points[i].x++; | ||||
|  | ||||
| 					XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, | ||||
| 							npoints, CoordModeOrigin); | ||||
| 				} | ||||
|  | ||||
| 				// Free resources | ||||
| 				free(points); | ||||
| 			} | ||||
| #endif | ||||
| 		} | ||||
|  | ||||
| 		XFreeGC(xw.dpy, ugc); | ||||
| 	} | ||||
|  | ||||
| 	if (base.mode & ATTR_STRUCK) { | ||||
| @@ -1520,6 +1943,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 +1972,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 { | ||||
| 			g.fg = defaultbg; | ||||
| 			g.bg = defaultcs; | ||||
| 		} 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 { | ||||
| 			drawcol = dc.col[g.bg]; | ||||
| 		} | ||||
| 		drawcol = dc.col[g.bg]; | ||||
| 	} | ||||
|  | ||||
| 	/* draw the new one */ | ||||
| @@ -1569,35 +2003,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); | ||||
| 	} | ||||
| } | ||||
| @@ -1642,6 +2076,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); | ||||
| } | ||||
|  | ||||
| @@ -1911,6 +2347,9 @@ resize(XEvent *e) | ||||
| 	cresize(e->xconfigure.width, e->xconfigure.height); | ||||
| } | ||||
|  | ||||
| int tinsync(uint); | ||||
| int ttyread_pending(); | ||||
|  | ||||
| void | ||||
| run(void) | ||||
| { | ||||
| @@ -1945,7 +2384,7 @@ run(void) | ||||
| 		FD_SET(ttyfd, &rfd); | ||||
| 		FD_SET(xfd, &rfd); | ||||
|  | ||||
| 		if (XPending(xw.dpy)) | ||||
| 		if (XPending(xw.dpy) || ttyread_pending()) | ||||
| 			timeout = 0;  /* existing events might not set xfd */ | ||||
|  | ||||
| 		seltv.tv_sec = timeout / 1E3; | ||||
| @@ -1959,7 +2398,8 @@ run(void) | ||||
| 		} | ||||
| 		clock_gettime(CLOCK_MONOTONIC, &now); | ||||
|  | ||||
| 		if (FD_ISSET(ttyfd, &rfd)) | ||||
| 		int ttyin = FD_ISSET(ttyfd, &rfd) || ttyread_pending(); | ||||
| 		if (ttyin) | ||||
| 			ttyread(); | ||||
|  | ||||
| 		xev = 0; | ||||
| @@ -1983,7 +2423,7 @@ run(void) | ||||
| 		 * maximum latency intervals during `cat huge.txt`, and perfect | ||||
| 		 * sync with periodic updates from animations/key-repeats/etc. | ||||
| 		 */ | ||||
| 		if (FD_ISSET(ttyfd, &rfd) || xev) { | ||||
| 		if (ttyin || xev) { | ||||
| 			if (!drawing) { | ||||
| 				trigger = now; | ||||
| 				drawing = 1; | ||||
| @@ -1994,6 +2434,18 @@ run(void) | ||||
| 				continue;  /* we have time, try to find idle */ | ||||
| 		} | ||||
|  | ||||
| 		if (tinsync(su_timeout)) { | ||||
| 			/* | ||||
| 			 * on synchronized-update draw-suspension: don't reset | ||||
| 			 * drawing so that we draw ASAP once we can (just after | ||||
| 			 * ESU). it won't be too soon because we already can | ||||
| 			 * draw now but we skip. we set timeout > 0 to draw on | ||||
| 			 * SU-timeout even without new content. | ||||
| 			 */ | ||||
| 			timeout = minlatency; | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		/* idle detected or maxlatency exhausted -> draw */ | ||||
| 		timeout = -1; | ||||
| 		if (blinktimeout && tattrset(ATTR_BLINK)) { | ||||
| @@ -2038,6 +2490,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; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user