Compare commits

..

19 Commits

Author SHA1 Message Date
0fb63d30a3
Merge branch 'feat/scrollback' into custom 2023-06-27 23:58:06 +02:00
b9f99cf7f1
Merge branch 'feat/boxdraw' into custom 2023-06-27 23:30:37 +02:00
e7ca32ef87
Merge branch 'feat/xresources' into custom 2023-06-27 22:55:24 +02:00
0770075e1a
Merged feat/xrandrfontsize branch into custom 2023-06-27 22:51:30 +02:00
49dc8002cd Added w3m v0.8.3 patch (#12)
Reviewed-on: #12
2023-06-27 22:36:44 +02:00
cc2d3a597a Added undercurl v0.8.4 patch (#11)
Reviewed-on: #11
2023-06-27 22:36:16 +02:00
0749fc4857 Added title-parsing-fix v0.8.5 patch (#10)
Reviewed-on: #10
2023-06-27 22:35:52 +02:00
539dfd0bac Added appsync part2 v2020-06-18 patch (#9)
Reviewed-on: #9
2023-06-27 22:35:14 +02:00
af62c87aa8 Added gruvbox-dark v0.8.5 patch (#8)
Reviewed-on: #8
2023-06-27 22:31:59 +02:00
aa9d16a41b Added dynamic-cursor-color v0.9 patch (#7)
Reviewed-on: #7
2023-06-27 22:31:10 +02:00
71d1f725b3 feat/clipboard (#6)
Reviewed-on: #6
2023-06-27 22:30:31 +02:00
fb7383d52f Added bold-is-not-bright v2019-01-27 patch (#3)
Reviewed-on: #3
2023-06-27 21:59:07 +02:00
3a32e68b82 Added anysize v.2022-07-18 patch (#2)
Reviewed-on: #2
2023-06-27 21:57:51 +02:00
a8d62fe07b Added alpha patch v0.8.5 (#1)
Reviewed-on: #1
2023-06-27 21:56:48 +02:00
4bb9184a41 Added xresources v2020-06-04 patch 2023-06-27 21:31:03 +02:00
6e60af80d8 Added xrandrfontsize v0.8.4 patch 2023-06-27 21:28:57 +02:00
be27b47a83 Added ligature-boxdraw v0.9 patch 2023-06-20 23:47:37 +02:00
a91b4c0c80 Added glyph-wide-support-boxdraw v2022-04-11 patch 2023-06-20 23:17:58 +02:00
0252e4a965 Added boxdraw v0.8.5 patch 2023-06-20 23:10:41 +02:00
9 changed files with 1395 additions and 113 deletions

View File

@ -4,7 +4,7 @@
include config.mk include config.mk
SRC = st.c x.c hb.c SRC = st.c x.c boxdraw.c hb.c
OBJ = $(SRC:.c=.o) OBJ = $(SRC:.c=.o)
all: options st all: options st
@ -23,6 +23,7 @@ config.h:
st.o: config.h st.h win.h st.o: config.h st.h win.h
x.o: arg.h config.h st.h win.h hb.h x.o: arg.h config.h st.h win.h hb.h
boxdraw.o: config.h st.h boxdraw_data.h
hb.o: st.h hb.o: st.h
$(OBJ): config.h config.mk $(OBJ): config.h config.mk

194
boxdraw.c Normal file
View File

@ -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);
}
}
}

214
boxdraw_data.h Normal file
View File

@ -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) */
};

View File

@ -8,6 +8,20 @@
static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true";
static int borderpx = 2; static int borderpx = 2;
/*
* Override/adjust fontsize of choosen monitors:
*/
MonitorConfig monitors_config[] = {
// skip = fixed relative points size (monitor dpi)
// =0 : fixed absolute pixel size (default screen dpi)
// >0 : auto absolute pixel size (monitor dpi)
// <0 : auto relative points size (monitor dpi)
// {"DP-1", 0}, // BUG:(size=0): not restored to default after back'n'forth
{"HDMI-0~1", -20}, // BUG:(ignored DPI=220): = 20 is eqv to 10pt (DPI=110)
{"HDMI-0~2", -14},
};
float winmovethreshold = 0.6;
/* /*
* What program is execed by st depends of these precedence rules: * What program is execed by st depends of these precedence rules:
* 1: program passed with -e * 1: program passed with -e
@ -56,6 +70,12 @@ int allowwindowops = 0;
static double minlatency = 8; static double minlatency = 8;
static double maxlatency = 33; 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 * blinking timeout (set to 0 to disable blinking) for the terminal blinking
* attribute. * attribute.
@ -67,6 +87,18 @@ static unsigned int blinktimeout = 800;
*/ */
static unsigned int cursorthickness = 2; 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 = 0;
const int boxdraw_bold = 0;
/* braille (U28XX): 1: render as adjacent "pixels", 0: use font */
const int boxdraw_braille = 0;
/* /*
* bell volume. It must be a value between -100 and 100. Use 0 for disabling * bell volume. It must be a value between -100 and 100. Use 0 for disabling
* it * it
@ -93,35 +125,30 @@ char *termname = "st-256color";
*/ */
unsigned int tabspaces = 8; unsigned int tabspaces = 8;
/* bg opacity */
float alpha = 0.8;
/* Terminal colors (16 first used in escape sequence) */ /* Terminal colors (16 first used in escape sequence) */
static const char *colorname[] = { static const char *colorname[] = {
/* 8 normal colors */ /* 8 normal colors */
"black", [0] = "#282828", /* hard contrast: #1d2021 / soft contrast: #32302f */
"red3", [1] = "#cc241d", /* red */
"green3", [2] = "#98971a", /* green */
"yellow3", [3] = "#d79921", /* yellow */
"blue2", [4] = "#458588", /* blue */
"magenta3", [5] = "#b16286", /* magenta */
"cyan3", [6] = "#689d6a", /* cyan */
"gray90", [7] = "#a89984", /* white */
/* 8 bright colors */ /* 8 bright colors */
"gray50", [8] = "#928374", /* black */
"red", [9] = "#fb4934", /* red */
"green", [10] = "#b8bb26", /* green */
"yellow", [11] = "#fabd2f", /* yellow */
"#5c5cff", [12] = "#83a598", /* blue */
"magenta", [13] = "#d3869b", /* magenta */
"cyan", [14] = "#8ec07c", /* cyan */
"white", [15] = "#ebdbb2", /* white */
[255] = 0,
/* more colors can be added after 255 to use with DefaultXX */
"#cccccc",
"#555555",
"gray90", /* default foreground colour */
"black", /* default background colour */
}; };
@ -129,9 +156,9 @@ static const char *colorname[] = {
* Default colors (colorname index) * Default colors (colorname index)
* foreground, background, cursor, reverse cursor * foreground, background, cursor, reverse cursor
*/ */
unsigned int defaultfg = 258; unsigned int defaultfg = 15;
unsigned int defaultbg = 259; unsigned int defaultbg = 0;
unsigned int defaultcs = 256; unsigned int defaultcs = 15;
static unsigned int defaultrcs = 257; static unsigned int defaultrcs = 257;
/* /*
@ -170,6 +197,42 @@ static unsigned int defaultattr = 11;
*/ */
static uint forcemousemod = ShiftMask; static uint forcemousemod = ShiftMask;
/*
* Xresources preferences to load at startup
*/
ResourcePref resources[] = {
{ "font", STRING, &font },
{ "color0", STRING, &colorname[0] },
{ "color1", STRING, &colorname[1] },
{ "color2", STRING, &colorname[2] },
{ "color3", STRING, &colorname[3] },
{ "color4", STRING, &colorname[4] },
{ "color5", STRING, &colorname[5] },
{ "color6", STRING, &colorname[6] },
{ "color7", STRING, &colorname[7] },
{ "color8", STRING, &colorname[8] },
{ "color9", STRING, &colorname[9] },
{ "color10", STRING, &colorname[10] },
{ "color11", STRING, &colorname[11] },
{ "color12", STRING, &colorname[12] },
{ "color13", STRING, &colorname[13] },
{ "color14", STRING, &colorname[14] },
{ "color15", STRING, &colorname[15] },
{ "background", STRING, &colorname[256] },
{ "foreground", STRING, &colorname[257] },
{ "cursorColor", STRING, &colorname[258] },
{ "termname", STRING, &termname },
{ "shell", STRING, &shell },
{ "minlatency", INTEGER, &minlatency },
{ "maxlatency", INTEGER, &maxlatency },
{ "blinktimeout", INTEGER, &blinktimeout },
{ "bellvolume", INTEGER, &bellvolume },
{ "tabspaces", INTEGER, &tabspaces },
{ "borderpx", INTEGER, &borderpx },
{ "cwscale", FLOAT, &cwscale },
{ "chscale", FLOAT, &chscale },
};
/* /*
* Internal mouse shortcuts. * Internal mouse shortcuts.
* Beware that overloading Button1 will disable the selection. * Beware that overloading Button1 will disable the selection.
@ -196,6 +259,7 @@ static Shortcut shortcuts[] = {
{ TERMMOD, XK_Prior, zoom, {.f = +1} }, { TERMMOD, XK_Prior, zoom, {.f = +1} },
{ TERMMOD, XK_Next, zoom, {.f = -1} }, { TERMMOD, XK_Next, zoom, {.f = -1} },
{ TERMMOD, XK_Home, zoomreset, {.f = 0} }, { TERMMOD, XK_Home, zoomreset, {.f = 0} },
{ TERMMOD, XK_End, refreshxrandr, {.i = 0} },
{ TERMMOD, XK_C, clipcopy, {.i = 0} }, { TERMMOD, XK_C, clipcopy, {.i = 0} },
{ TERMMOD, XK_V, clippaste, {.i = 0} }, { TERMMOD, XK_V, clippaste, {.i = 0} },
{ TERMMOD, XK_Y, selpaste, {.i = 0} }, { TERMMOD, XK_Y, selpaste, {.i = 0} },
@ -474,3 +538,27 @@ static char ascii_printable[] =
" !\"#$%&'()*+,-./0123456789:;<=>?" " !\"#$%&'()*+,-./0123456789:;<=>?"
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
"`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

View File

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

151
st.c
View File

@ -33,6 +33,7 @@
#define UTF_SIZ 4 #define UTF_SIZ 4
#define ESC_BUF_SIZ (128*UTF_SIZ) #define ESC_BUF_SIZ (128*UTF_SIZ)
#define ESC_ARG_SIZ 16 #define ESC_ARG_SIZ 16
#define CAR_PER_ARG 4
#define STR_BUF_SIZ ESC_BUF_SIZ #define STR_BUF_SIZ ESC_BUF_SIZ
#define STR_ARG_SIZ ESC_ARG_SIZ #define STR_ARG_SIZ ESC_ARG_SIZ
#define HISTSIZE 2000 #define HISTSIZE 2000
@ -44,6 +45,24 @@
#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
#define ISDELIM(u) (u && wcschr(worddelimiters, u)) #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))
#define TLINE(y) ( \
(y) < term.scr ? term.hist[(term.histi + (y) - term.scr + 1 + HISTSIZE) % HISTSIZE] \
: term.line[(y) - term.scr] \
)
#define TLINEABS(y) ( \
(y) < 0 ? term.hist[(term.histi + (y) + 1 + HISTSIZE) % HISTSIZE] : term.line[(y)] \
)
#define UPDATEWRAPNEXT(alt, col) do { \
if ((term.c.state & CURSOR_WRAPNEXT) && term.c.x + term.wrapcwidth[alt] < col) { \
term.c.x += term.wrapcwidth[alt]; \
term.c.state &= ~CURSOR_WRAPNEXT; \
} \
} while (0);
#define TLINE(y) ( \ #define TLINE(y) ( \
(y) < term.scr ? term.hist[(term.histi + (y) - term.scr + 1 + HISTSIZE) % HISTSIZE] \ (y) < term.scr ? term.hist[(term.histi + (y) - term.scr + 1 + HISTSIZE) % HISTSIZE] \
@ -167,6 +186,7 @@ typedef struct {
int arg[ESC_ARG_SIZ]; int arg[ESC_ARG_SIZ];
int narg; /* nb of args */ int narg; /* nb of args */
char mode[2]; char mode[2];
int carg[ESC_ARG_SIZ][CAR_PER_ARG]; /* colon args */
} CSIEscape; } CSIEscape;
/* STR Escape sequence structs */ /* STR Escape sequence structs */
@ -176,7 +196,7 @@ typedef struct {
char *buf; /* allocated raw string */ char *buf; /* allocated raw string */
size_t siz; /* allocation size */ size_t siz; /* allocation size */
size_t len; /* raw string length */ 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 */ int narg; /* nb of args */
} STREscape; } STREscape;
@ -187,6 +207,7 @@ static void ttywriteraw(const char *, size_t);
static void csidump(void); static void csidump(void);
static void csihandle(void); static void csihandle(void);
static void readcolonargs(char **, int, int[][CAR_PER_ARG]);
static void csiparse(void); static void csiparse(void);
static void csireset(void); static void csireset(void);
static void osc_color_response(int, int, int); static void osc_color_response(int, int, int);
@ -274,6 +295,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 utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; 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 ssize_t
xwrite(int fd, const char *s, size_t len) xwrite(int fd, const char *s, size_t len)
{ {
@ -889,6 +937,9 @@ ttynew(const char *line, char *cmd, const char *out, char **args)
return cmdfd; return cmdfd;
} }
static int twrite_aborted = 0;
int ttyread_pending() { return twrite_aborted; }
size_t size_t
ttyread(void) ttyread(void)
{ {
@ -897,7 +948,7 @@ ttyread(void)
int ret, written; int ret, written;
/* append read bytes to unprocessed bytes */ /* 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) { switch (ret) {
case 0: case 0:
@ -905,7 +956,7 @@ ttyread(void)
case -1: case -1:
die("couldn't read from shell: %s\n", strerror(errno)); die("couldn't read from shell: %s\n", strerror(errno));
default: default:
buflen += ret; buflen += twrite_aborted ? 0 : ret;
written = twrite(buf, buflen, 0); written = twrite(buf, buflen, 0);
buflen -= written; buflen -= written;
/* keep any incomplete UTF-8 byte sequence for the next call */ /* keep any incomplete UTF-8 byte sequence for the next call */
@ -1066,6 +1117,7 @@ tsetdirtattr(int attr)
void void
tfulldirt(void) tfulldirt(void)
{ {
tsync_end();
for (int i = 0; i < term.row; i++) for (int i = 0; i < term.row; i++)
term.dirty[i] = 1; term.dirty[i] = 1;
} }
@ -1352,6 +1404,28 @@ tnewline(int first_col)
tmoveto(first_col ? 0 : term.c.x, y); 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 void
csiparse(void) csiparse(void)
{ {
@ -1374,6 +1448,7 @@ csiparse(void)
v = -1; v = -1;
csiescseq.arg[csiescseq.narg++] = v; csiescseq.arg[csiescseq.narg++] = v;
p = np; p = np;
readcolonargs(&p, csiescseq.narg-1, csiescseq.carg);
if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ)
break; break;
p++; p++;
@ -1441,6 +1516,9 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
term.line[y][x] = *attr; term.line[y][x] = *attr;
term.line[y][x].u = u; term.line[y][x].u = u;
term.line[y][x].mode |= ATTR_SET; term.line[y][x].mode |= ATTR_SET;
if (isboxdraw(u))
term.line[y][x].mode |= ATTR_BOXDRAW;
} }
void void
@ -1594,6 +1672,10 @@ tsetattr(const int *attr, int l)
ATTR_STRUCK ); ATTR_STRUCK );
term.c.attr.fg = defaultfg; term.c.attr.fg = defaultfg;
term.c.attr.bg = defaultbg; 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; break;
case 1: case 1:
term.c.attr.mode |= ATTR_BOLD; term.c.attr.mode |= ATTR_BOLD;
@ -1605,7 +1687,14 @@ tsetattr(const int *attr, int l)
term.c.attr.mode |= ATTR_ITALIC; term.c.attr.mode |= ATTR_ITALIC;
break; break;
case 4: case 4:
term.c.attr.ustyle = csiescseq.carg[i][0];
if (term.c.attr.ustyle != 0)
term.c.attr.mode |= ATTR_UNDERLINE; term.c.attr.mode |= ATTR_UNDERLINE;
else
term.c.attr.mode &= ~ATTR_UNDERLINE;
term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
break; break;
case 5: /* slow blink */ case 5: /* slow blink */
/* FALLTHROUGH */ /* FALLTHROUGH */
@ -1656,6 +1745,18 @@ tsetattr(const int *attr, int l)
case 49: case 49:
term.c.attr.bg = defaultbg; term.c.attr.bg = defaultbg;
break; 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: default:
if (BETWEEN(attr[i], 30, 37)) { if (BETWEEN(attr[i], 30, 37)) {
term.c.attr.fg = attr[i] - 30; term.c.attr.fg = attr[i] - 30;
@ -2113,29 +2214,30 @@ strhandle(void)
}; };
term.esc &= ~(ESC_STR_END|ESC_STR); term.esc &= ~(ESC_STR_END|ESC_STR);
strparse(); strescseq.buf[strescseq.len] = '\0';
par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0;
switch (strescseq.type) { switch (strescseq.type) {
case ']': /* OSC -- Operating System Command */ case ']': /* OSC -- Operating System Command */
strparse();
par = (narg = strescseq.narg) ? atoi(STRESCARGJUST(0)) : 0;
switch (par) { switch (par) {
case 0: case 0:
if (narg > 1) { if (narg > 1) {
xsettitle(strescseq.args[1]); xsettitle(STRESCARGREST(1));
xseticontitle(strescseq.args[1]); xseticontitle(STRESCARGREST(1));
} }
return; return;
case 1: case 1:
if (narg > 1) if (narg > 1)
xseticontitle(strescseq.args[1]); xseticontitle(STRESCARGREST(1));
return; return;
case 2: case 2:
if (narg > 1) if (narg > 1)
xsettitle(strescseq.args[1]); xsettitle(STRESCARGREST(1));
return; return;
case 52: case 52:
if (narg > 2 && allowwindowops) { if (narg > 2 && allowwindowops) {
dec = base64dec(strescseq.args[2]); dec = base64dec(STRESCARGJUST(2));
if (dec) { if (dec) {
xsetsel(dec); xsetsel(dec);
xclipcopy(); xclipcopy();
@ -2149,7 +2251,7 @@ strhandle(void)
case 12: case 12:
if (narg < 2) if (narg < 2)
break; break;
p = strescseq.args[1]; p = STRESCARGREST(1);
if ((j = par - 10) < 0 || j >= LEN(osc_table)) if ((j = par - 10) < 0 || j >= LEN(osc_table))
break; /* shouldn't be possible */ break; /* shouldn't be possible */
@ -2165,10 +2267,10 @@ strhandle(void)
case 4: /* color set */ case 4: /* color set */
if (narg < 3) if (narg < 3)
break; break;
p = strescseq.args[2]; p = STRESCARGJUST(2);
/* FALLTHROUGH */ /* FALLTHROUGH */
case 104: /* color reset */ case 104: /* color reset */
j = (narg > 1) ? atoi(strescseq.args[1]) : -1; j = (narg > 1) ? atoi(STRESCARGJUST(1)) : -1;
if (p && !strcmp(p, "?")) { if (p && !strcmp(p, "?")) {
osc_color_response(j, 0, 1); osc_color_response(j, 0, 1);
@ -2190,9 +2292,15 @@ strhandle(void)
} }
break; break;
case 'k': /* old title set compatibility */ case 'k': /* old title set compatibility */
xsettitle(strescseq.args[0]); xsettitle(strescseq.buf);
return; return;
case 'P': /* DCS -- Device Control String */ 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 '_': /* APC -- Application Program Command */
case '^': /* PM -- Privacy Message */ case '^': /* PM -- Privacy Message */
return; return;
@ -2209,18 +2317,17 @@ strparse(void)
char *p = strescseq.buf; char *p = strescseq.buf;
strescseq.narg = 0; strescseq.narg = 0;
strescseq.buf[strescseq.len] = '\0';
if (*p == '\0') if (*p == '\0')
return; return;
while (strescseq.narg < STR_ARG_SIZ) { while (strescseq.narg < STR_ARG_SIZ) {
strescseq.args[strescseq.narg++] = p;
while ((c = *p) != ';' && c != '\0') while ((c = *p) != ';' && c != '\0')
++p; p++;
strescseq.argp[strescseq.narg++] = p;
if (c == '\0') if (c == '\0')
return; return;
*p++ = '\0'; p++;
} }
} }
@ -2735,6 +2842,9 @@ twrite(const char *buf, int buflen, int show_ctrl)
Rune u; Rune u;
int n; int n;
int su0 = su;
twrite_aborted = 0;
for (n = 0; n < buflen; n += charsize) { for (n = 0; n < buflen; n += charsize) {
if (IS_SET(MODE_UTF8)) { if (IS_SET(MODE_UTF8)) {
/* process a complete utf8 char */ /* process a complete utf8 char */
@ -2745,6 +2855,10 @@ twrite(const char *buf, int buflen, int show_ctrl)
u = buf[n] & 0xFF; u = buf[n] & 0xFF;
charsize = 1; charsize = 1;
} }
if (su0 && !su) {
twrite_aborted = 1;
break; // ESU - allow rendering before a new BSU
}
if (show_ctrl && ISCONTROL(u)) { if (show_ctrl && ISCONTROL(u)) {
if (u & 0x80) { if (u & 0x80) {
u &= 0x7f; u &= 0x7f;
@ -3072,7 +3186,6 @@ draw(void)
xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
term.ocx, term.ocy, term.line[term.ocy][term.ocx], term.ocx, term.ocy, term.line[term.ocy][term.ocx],
term.line[term.ocy], term.col); term.line[term.ocy], term.col);
term.ocx = cx; term.ocx = cx;
term.ocy = term.c.y; term.ocy = term.c.y;
xfinishdraw(); xfinishdraw();

20
st.h
View File

@ -35,8 +35,16 @@ enum glyph_attribute {
ATTR_WRAP = 1 << 9, ATTR_WRAP = 1 << 9,
ATTR_WIDE = 1 << 10, ATTR_WIDE = 1 << 10,
ATTR_WDUMMY = 1 << 11, ATTR_WDUMMY = 1 << 11,
ATTR_BOXDRAW = 1 << 11,
ATTR_SELECTED = 1 << 12, ATTR_SELECTED = 1 << 12,
ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
ATTR_DIRTYUNDERLINE = 1 << 15,
};
enum drawing_mode {
DRAW_NONE = 0,
DRAW_BG = 1 << 0,
DRAW_FG = 1 << 1,
}; };
enum selection_mode { enum selection_mode {
@ -68,6 +76,8 @@ typedef struct {
ushort mode; /* attribute flags */ ushort mode; /* attribute flags */
uint32_t fg; /* foreground */ uint32_t fg; /* foreground */
uint32_t bg; /* background */ uint32_t bg; /* background */
int ustyle; /* underline style */
int ucolor[3]; /* underline color */
} Glyph; } Glyph;
typedef Glyph *Line; typedef Glyph *Line;
@ -117,6 +127,14 @@ void *xmalloc(size_t);
void *xrealloc(void *, size_t); void *xrealloc(void *, size_t);
char *xstrdup(const char *); char *xstrdup(const char *);
int isboxdraw(Rune);
ushort boxdrawindex(const Glyph *);
#ifdef XFT_VERSION
/* only exposed to x.c, otherwise we'll need Xft.h for the types */
void boxdraw_xinit(Display *, Colormap, XftDraw *, Visual *);
void drawboxes(int, int, int, int, XftColor *, XftColor *, const XftGlyphFontSpec *, int);
#endif
/* config.h globals */ /* config.h globals */
extern char *utmp; extern char *utmp;
extern char *scroll; extern char *scroll;
@ -130,3 +148,5 @@ extern unsigned int tabspaces;
extern unsigned int defaultfg; extern unsigned int defaultfg;
extern unsigned int defaultbg; extern unsigned int defaultbg;
extern unsigned int defaultcs; extern unsigned int defaultcs;
extern const int boxdraw, boxdraw_bold, boxdraw_braille;
extern float alpha;

View File

@ -1,4 +1,5 @@
st-mono| simpleterm monocolor, st-mono| simpleterm monocolor,
Su,
acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
am, am,
bce, bce,
@ -191,6 +192,7 @@ st-mono| simpleterm monocolor,
Ms=\E]52;%p1%s;%p2%s\007, Ms=\E]52;%p1%s;%p2%s\007,
Se=\E[2 q, Se=\E[2 q,
Ss=\E[%p1%d q, Ss=\E[%p1%d q,
Sync=\EP=%p1%ds\E\\,
st| simpleterm, st| simpleterm,
use=st-mono, use=st-mono,

739
x.c
View File

@ -14,6 +14,7 @@
#include <X11/keysym.h> #include <X11/keysym.h>
#include <X11/Xft/Xft.h> #include <X11/Xft/Xft.h>
#include <X11/XKBlib.h> #include <X11/XKBlib.h>
#include <X11/extensions/Xrandr.h>
char *argv0; char *argv0;
#include "arg.h" #include "arg.h"
@ -46,6 +47,29 @@ typedef struct {
signed char appcursor; /* application cursor */ signed char appcursor; /* application cursor */
} Key; } Key;
<<<<<<< HEAD
/* 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
};
=======
typedef struct {
const char *name;
float defaultfontsize;
} MonitorConfig;
typedef struct {
Atom name;
int x, y, w, h;
float defaultfontsize, usedfontsize;
} MonitorInfo;
static void refreshxrandr(const Arg *dummy);
>>>>>>> feat/xrandrfontsize
/* X modifiers */ /* X modifiers */
#define XK_ANY_MOD UINT_MAX #define XK_ANY_MOD UINT_MAX
#define XK_NO_MOD 0 #define XK_NO_MOD 0
@ -82,6 +106,7 @@ typedef XftGlyphFontSpec GlyphFontSpec;
typedef struct { typedef struct {
int tw, th; /* tty width and height */ int tw, th; /* tty width and height */
int w, h; /* window width and height */ int w, h; /* window width and height */
int hborderpx, vborderpx;
int ch; /* char height */ int ch; /* char height */
int cw; /* char width */ int cw; /* char width */
int mode; /* window state/mode flags */ int mode; /* window state/mode flags */
@ -106,6 +131,7 @@ typedef struct {
XSetWindowAttributes attrs; XSetWindowAttributes attrs;
int scr; int scr;
int isfixed; /* is fixed geometry? */ int isfixed; /* is fixed geometry? */
int depth; /* bit depth */
int l, t; /* left and top offset */ int l, t; /* left and top offset */
int gm; /* geometry mask */ int gm; /* geometry mask */
} XWindow; } XWindow;
@ -217,6 +243,11 @@ static void (*handler[LASTEvent])(XEvent *) = {
[SelectionRequest] = selrequest, [SelectionRequest] = selrequest,
}; };
static double defaultrelfontsize = 0;
static MonitorInfo *monitors_info = NULL;
static int monitors_num = 0;
static int prev_mindex = -1;
/* Globals */ /* Globals */
static DC dc; static DC dc;
static XWindow xw; static XWindow xw;
@ -245,6 +276,7 @@ static char *usedfont = NULL;
static double usedfontsize = 0; static double usedfontsize = 0;
static double defaultfontsize = 0; static double defaultfontsize = 0;
static char *opt_alpha = NULL;
static char *opt_class = NULL; static char *opt_class = NULL;
static char **opt_cmd = NULL; static char **opt_cmd = NULL;
static char *opt_embed = NULL; static char *opt_embed = NULL;
@ -333,7 +365,7 @@ ttysend(const Arg *arg)
int int
evcol(XEvent *e) evcol(XEvent *e)
{ {
int x = e->xbutton.x - borderpx; int x = e->xbutton.x - win.hborderpx;
LIMIT(x, 0, win.tw - 1); LIMIT(x, 0, win.tw - 1);
return x / win.cw; return x / win.cw;
} }
@ -341,7 +373,7 @@ evcol(XEvent *e)
int int
evrow(XEvent *e) evrow(XEvent *e)
{ {
int y = e->xbutton.y - borderpx; int y = e->xbutton.y - win.vborderpx;
LIMIT(y, 0, win.th - 1); LIMIT(y, 0, win.th - 1);
return y / win.ch; return y / win.ch;
} }
@ -688,6 +720,8 @@ setsel(char *str, Time t)
XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t);
if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win)
selclear(); selclear();
xclipcopy();
} }
void void
@ -711,7 +745,9 @@ brelease(XEvent *e)
if (mouseaction(e, 1)) if (mouseaction(e, 1))
return; return;
if (btn == Button1) if (btn == Button3)
selpaste(NULL);
else if (btn == Button1)
mousesel(e, 1); mousesel(e, 1);
} }
@ -741,6 +777,9 @@ cresize(int width, int height)
col = MAX(1, col); col = MAX(1, col);
row = MAX(1, row); row = MAX(1, row);
win.hborderpx = (win.w - col * win.cw) / 2;
win.vborderpx = (win.h - row * win.ch) / 2;
tresize(col, row); tresize(col, row);
xresize(col, row); xresize(col, row);
ttyresize(win.tw, win.th); ttyresize(win.tw, win.th);
@ -754,7 +793,7 @@ xresize(int col, int row)
XFreePixmap(xw.dpy, xw.buf); XFreePixmap(xw.dpy, xw.buf);
xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
DefaultDepth(xw.dpy, xw.scr)); xw.depth);
XftDrawChange(xw.draw, xw.buf); XftDrawChange(xw.draw, xw.buf);
xclear(0, 0, win.w, win.h); xclear(0, 0, win.w, win.h);
@ -814,6 +853,13 @@ xloadcols(void)
else else
die("could not allocate color %d\n", i); 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; loaded = 1;
} }
@ -871,8 +917,8 @@ xhints(void)
sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize;
sizeh->height = win.h; sizeh->height = win.h;
sizeh->width = win.w; sizeh->width = win.w;
sizeh->height_inc = win.ch; sizeh->height_inc = 1;
sizeh->width_inc = win.cw; sizeh->width_inc = 1;
sizeh->base_height = 2 * borderpx; sizeh->base_height = 2 * borderpx;
sizeh->base_width = 2 * borderpx; sizeh->base_width = 2 * borderpx;
sizeh->min_height = win.ch + 2 * borderpx; sizeh->min_height = win.ch + 2 * borderpx;
@ -1139,11 +1185,23 @@ xinit(int cols, int rows)
Window parent; Window parent;
pid_t thispid = getpid(); pid_t thispid = getpid();
XColor xmousefg, xmousebg; XColor xmousefg, xmousebg;
XWindowAttributes attr;
XVisualInfo vis;
if (!(xw.dpy = XOpenDisplay(NULL))) if (!(xw.dpy = XOpenDisplay(NULL)))
die("can't open display\n"); die("can't open display\n");
xw.scr = XDefaultScreen(xw.dpy); 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 */ /* font */
if (!FcInit()) if (!FcInit())
@ -1153,12 +1211,12 @@ xinit(int cols, int rows)
xloadfonts(usedfont, 0); xloadfonts(usedfont, 0);
/* colors */ /* colors */
xw.cmap = XDefaultColormap(xw.dpy, xw.scr); xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None);
xloadcols(); xloadcols();
/* adjust fixed window geometry */ /* adjust fixed window geometry */
win.w = 2 * borderpx + cols * win.cw; win.w = 2 * win.hborderpx + 2 * borderpx + cols * win.cw;
win.h = 2 * borderpx + rows * win.ch; win.h = 2 * win.vborderpx + 2 * borderpx + rows * win.ch;
if (xw.gm & XNegative) if (xw.gm & XNegative)
xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2;
if (xw.gm & YNegative) if (xw.gm & YNegative)
@ -1173,19 +1231,15 @@ xinit(int cols, int rows)
| ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
xw.attrs.colormap = xw.cmap; 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, 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 xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
| CWEventMask | CWColormap, &xw.attrs); | CWEventMask | CWColormap, &xw.attrs);
memset(&gcvalues, 0, sizeof(gcvalues)); memset(&gcvalues, 0, sizeof(gcvalues));
gcvalues.graphics_exposures = False; gcvalues.graphics_exposures = False;
dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth);
&gcvalues); dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues);
xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
DefaultDepth(xw.dpy, xw.scr));
XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
@ -1263,7 +1317,7 @@ xresetfontsettings(ushort mode, Font **font, int *frcflags)
int int
xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) 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; ushort mode, prevmode = USHRT_MAX;
Font *font = &dc.font; Font *font = &dc.font;
int frcflags = FRC_NORMAL; int frcflags = FRC_NORMAL;
@ -1421,11 +1475,56 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
return numspecs; 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 void
xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
{ {
int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); 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; width = charlen * win.cw;
Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
XRenderColor colfg, colbg; XRenderColor colfg, colbg;
@ -1462,10 +1561,6 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
bg = &dc.col[base.bg]; 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 (IS_SET(MODE_REVERSE)) {
if (fg == &dc.col[defaultfg]) { if (fg == &dc.col[defaultfg]) {
fg = &dc.col[defaultbg]; fg = &dc.col[defaultbg];
@ -1513,6 +1608,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
if (base.mode & ATTR_INVISIBLE) if (base.mode & ATTR_INVISIBLE)
fg = bg; fg = bg;
if (dmode & DRAW_BG) {
/* Intelligent cleaning up of the borders. */ /* Intelligent cleaning up of the borders. */
if (x == 0) { if (x == 0) {
xclear(0, (y == 0)? 0 : winy, borderpx, xclear(0, (y == 0)? 0 : winy, borderpx,
@ -1527,9 +1623,9 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
xclear(winx, 0, winx + width, borderpx); xclear(winx, 0, winx + width, borderpx);
if (winy + win.ch >= borderpx + win.th) if (winy + win.ch >= borderpx + win.th)
xclear(winx, winy + win.ch, winx + width, win.h); xclear(winx, winy + win.ch, winx + width, win.h);
/* Fill the background */
/* Clean up the region we want to draw to. */
XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
}
/* Set the clip region because Xft is sometimes dirty. */ /* Set the clip region because Xft is sometimes dirty. */
r.x = 0; r.x = 0;
@ -1543,8 +1639,357 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
/* Render underline and strikethrough. */ /* Render underline and strikethrough. */
if (base.mode & ATTR_UNDERLINE) { if (base.mode & ATTR_UNDERLINE) {
XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1, // Underline Color
width, 1); 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) { if (base.mode & ATTR_STRUCK) {
@ -1570,6 +2015,7 @@ void
xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len) xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len)
{ {
Color drawcol; Color drawcol;
XRenderColor colbg;
/* remove the old cursor */ /* remove the old cursor */
if (selected(ox, oy)) if (selected(ox, oy))
@ -1601,12 +2047,22 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int le
if (selected(cx, cy)) { if (selected(cx, cy)) {
g.fg = defaultfg; g.fg = defaultfg;
g.bg = defaultrcs; g.bg = defaultrcs;
} else { } else if (!(og.mode & ATTR_REVERSE)) {
g.fg = defaultbg; unsigned long col = g.bg;
g.bg = defaultcs; 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 */ /* draw the new one */
if (IS_SET(MODE_FOCUSED)) { if (IS_SET(MODE_FOCUSED)) {
@ -1622,35 +2078,35 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int le
case 3: /* Blinking Underline */ case 3: /* Blinking Underline */
case 4: /* Steady Underline */ case 4: /* Steady Underline */
XftDrawRect(xw.draw, &drawcol, XftDrawRect(xw.draw, &drawcol,
borderpx + cx * win.cw, win.hborderpx + cx * win.cw,
borderpx + (cy + 1) * win.ch - \ win.vborderpx + (cy + 1) * win.ch - \
cursorthickness, cursorthickness,
win.cw, cursorthickness); win.cw, cursorthickness);
break; break;
case 5: /* Blinking bar */ case 5: /* Blinking bar */
case 6: /* Steady bar */ case 6: /* Steady bar */
XftDrawRect(xw.draw, &drawcol, XftDrawRect(xw.draw, &drawcol,
borderpx + cx * win.cw, win.hborderpx + cx * win.cw,
borderpx + cy * win.ch, win.vborderpx + cy * win.ch,
cursorthickness, win.ch); cursorthickness, win.ch);
break; break;
} }
} else { } else {
XftDrawRect(xw.draw, &drawcol, XftDrawRect(xw.draw, &drawcol,
borderpx + cx * win.cw, win.hborderpx + cx * win.cw,
borderpx + cy * win.ch, win.vborderpx + cy * win.ch,
win.cw - 1, 1); win.cw - 1, 1);
XftDrawRect(xw.draw, &drawcol, XftDrawRect(xw.draw, &drawcol,
borderpx + cx * win.cw, win.hborderpx + cx * win.cw,
borderpx + cy * win.ch, win.vborderpx + cy * win.ch,
1, win.ch - 1); 1, win.ch - 1);
XftDrawRect(xw.draw, &drawcol, XftDrawRect(xw.draw, &drawcol,
borderpx + (cx + 1) * win.cw - 1, win.hborderpx + (cx + 1) * win.cw - 1,
borderpx + cy * win.ch, win.vborderpx + cy * win.ch,
1, win.ch - 1); 1, win.ch - 1);
XftDrawRect(xw.draw, &drawcol, XftDrawRect(xw.draw, &drawcol,
borderpx + cx * win.cw, win.hborderpx + cx * win.cw,
borderpx + (cy + 1) * win.ch - 1, win.vborderpx + (cy + 1) * win.ch - 1,
win.cw, 1); win.cw, 1);
} }
} }
@ -1695,6 +2151,8 @@ xsettitle(char *p)
int int
xstartdraw(void) 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); return IS_SET(MODE_VISIBLE);
} }
@ -1806,6 +2264,144 @@ xseturgency(int add)
XFree(h); XFree(h);
} }
static void
cachemonitorinfo()
{
int prev_num = monitors_num;
MonitorInfo *prev_info = monitors_info;
XRRMonitorInfo *xmonitors = XRRGetMonitors(xw.dpy, XRootWindow(xw.dpy, xw.scr), 1, &monitors_num);
if (!monitors_num)
die("xrandr found no monitors");
monitors_info = xmalloc(monitors_num * sizeof(MonitorInfo));
for (int i = 0; i < monitors_num; ++i) {
XRRMonitorInfo *xm = &xmonitors[i];
MonitorInfo *m = &monitors_info[i];
m->name = xm->name;
m->x = xm->x;
m->y = xm->y;
m->w = xm->width;
m->h = xm->height;
float px_mm = ((float)m->w / xm->mwidth + (float)m->h / xm->mheight) / 2;
float px_pt = 25.4 * px_mm / 72;
m->defaultfontsize = defaultrelfontsize * px_pt;
// Override defaultfontsize (dpi) by user config
char *name = XGetAtomName(xw.dpy, xm->name);
for (int j = 0; j < LEN(monitors_config); ++j)
if (!strcmp(name, monitors_config[j].name)) {
m->defaultfontsize = monitors_config[j].defaultfontsize;
if (m->defaultfontsize < 0)
m->defaultfontsize *= -px_pt;
break;
}
// fprintf(stderr, "%s: %fpx, %f\n", name, m->defaultfontsize, m->usedfontsize);
XFree(name);
// Restore usedfontsize (zoom) after re-cache for monitors with the same name
m->usedfontsize = m->defaultfontsize;
for (int j = 0; j < prev_num; ++j)
if (prev_info[j].name == m->name) {
m->usedfontsize = prev_info[j].usedfontsize;
break;
}
}
XRRFreeMonitors(xmonitors);
free(prev_info);
}
static int
getmonitorindex_threshold(int w, int h, int x, int y)
{
int mindex = -1;
float fontsize = 0;
int thresholdarea = winmovethreshold * w * h;
for (int i = 0; i < monitors_num; ++i) {
MonitorInfo *m = &monitors_info[i];
int overlap_w = MAX(0, MIN(x + w, m->x + m->w) - MAX(x, m->x));
int overlap_h = MAX(0, MIN(y + h, m->y + m->h) - MAX(y, m->y));
int area = overlap_w * overlap_h;
// Choose monitor with largest dpi (defaultfontsize)
// from all "mirrored"/overlapped (e.g. projector)
if (area >= thresholdarea && fontsize < m->defaultfontsize) {
fontsize = m->defaultfontsize;
mindex = i;
}
}
return mindex;
}
static int
getmonitorindex_nearest(int w, int h, int x, int y)
{
int mindex = -1;
float fontsize = 0;
int overlaparea = 0;
for (int i = 0; i < monitors_num; ++i) {
MonitorInfo *m = &monitors_info[i];
int overlap_w = MAX(0, MIN(x + w, m->x + m->w) - MAX(x, m->x));
int overlap_h = MAX(0, MIN(y + h, m->y + m->h) - MAX(y, m->y));
int area = overlap_w * overlap_h;
// Choose monitor with largest overlapping area
// e.g. when "st" is initially spawned in-between monitors
if (area > overlaparea) {
overlaparea = area;
mindex = i;
}
}
return mindex;
}
static void
adjustmonitorfontsize(int mindex)
{
if (mindex < 0 || prev_mindex == mindex)
return;
// Save zoom of current monitor before switching
if (prev_mindex >= 0)
monitors_info[prev_mindex].usedfontsize = usedfontsize;
defaultfontsize = monitors_info[mindex].defaultfontsize;
// fprintf(stderr, "Crossing: %fpx\n", defaultfontsize);
// NOTE: do nothing if font size differs by less than 1%
double fontsize = monitors_info[mindex].usedfontsize;
double delta = 0.01 * usedfontsize;
if (!BETWEEN(fontsize - usedfontsize, -delta, delta)) {
// fprintf(stderr, "Adjusted: %fpx\n", fontsize);
xunloadfonts();
xloadfonts(usedfont, fontsize);
}
prev_mindex = mindex;
}
void
refreshxrandr(const Arg *dummy)
{
// Reset index to detect change of window association on "xrandr ... --primary"
// otherwise: zoom won't be saved on switching and new font size won't be loaded
// CRIT!!! event from xrandr may place another monitor into same index
if (prev_mindex >= 0)
monitors_info[prev_mindex].usedfontsize = usedfontsize;
prev_mindex = -1;
XWindowAttributes xattr = {0};
cachemonitorinfo();
XGetWindowAttributes(xw.dpy, xw.win, &xattr);
int mindex = getmonitorindex_threshold(xattr.width, xattr.height, xattr.x, xattr.y);
if (mindex < 0)
mindex = getmonitorindex_nearest(xattr.width, xattr.height, xattr.x, xattr.y);
adjustmonitorfontsize(mindex);
}
void void
xbell(void) xbell(void)
{ {
@ -1958,12 +2554,23 @@ cmessage(XEvent *e)
void void
resize(XEvent *e) resize(XEvent *e)
{ {
// BAD: no resize on monitor plug/unplug/reconfigure -- until window itself is kept in the same place
// NOTE: no resize event on zoomabs()
// fprintf(stderr, "Resize: %dx%d+%d+%d\n",
// e->xconfigure.width, e->xconfigure.height, e->xconfigure.x, e->xconfigure.y);
adjustmonitorfontsize(getmonitorindex_threshold(
e->xconfigure.width, e->xconfigure.height, e->xconfigure.x, e->xconfigure.y));
if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) if (e->xconfigure.width == win.w && e->xconfigure.height == win.h)
return; return;
cresize(e->xconfigure.width, e->xconfigure.height); cresize(e->xconfigure.width, e->xconfigure.height);
} }
int tinsync(uint);
int ttyread_pending();
void void
run(void) run(void)
{ {
@ -1990,6 +2597,22 @@ run(void)
} }
} while (ev.type != MapNotify); } while (ev.type != MapNotify);
int rr_event_base, rr_error_base, rr_major, rr_minor;
if (!XRRQueryExtension (xw.dpy, &rr_event_base, &rr_error_base) ||
!XRRQueryVersion (xw.dpy, &rr_major, &rr_minor) ||
rr_major < 1 || (rr_major == 1 && rr_minor < 5))
{
die("RandR 1.5 extension isn't available\n");
}
XRRSelectInput(xw.dpy, xw.win, RRCrtcChangeNotifyMask);
// WARN: can query actual window size/pos only after window is mapped and its width/height are adjusted by WM
// * x/y are WM-dependent and can't be determined beforehand anyway
// * defaultfontsize isn't available until font is loaded and actual Fc*() size queried
// BAD: fonts on startup are always reloaded -- how to specify their size beforehand ?
FcPatternGetDouble(dc.font.match->pattern, FC_SIZE, 0, &defaultrelfontsize);
refreshxrandr(0);
ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd);
cresize(w, h); cresize(w, h);
@ -1998,7 +2621,7 @@ run(void)
FD_SET(ttyfd, &rfd); FD_SET(ttyfd, &rfd);
FD_SET(xfd, &rfd); FD_SET(xfd, &rfd);
if (XPending(xw.dpy)) if (XPending(xw.dpy) || ttyread_pending())
timeout = 0; /* existing events might not set xfd */ timeout = 0; /* existing events might not set xfd */
seltv.tv_sec = timeout / 1E3; seltv.tv_sec = timeout / 1E3;
@ -2012,7 +2635,8 @@ run(void)
} }
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
if (FD_ISSET(ttyfd, &rfd)) int ttyin = FD_ISSET(ttyfd, &rfd) || ttyread_pending();
if (ttyin)
ttyread(); ttyread();
xev = 0; xev = 0;
@ -2021,6 +2645,16 @@ run(void)
XNextEvent(xw.dpy, &ev); XNextEvent(xw.dpy, &ev);
if (XFilterEvent(&ev, None)) if (XFilterEvent(&ev, None))
continue; continue;
if (LASTEvent <= ev.type) {
if (rr_event_base + RRNotify == ev.type &&
RRNotify_CrtcChange == ((XRRNotifyEvent *)&ev)->subtype)
{
XRRUpdateConfiguration(&ev);
// fprintf(stderr, "Monitor change: %d > %d\n", rr_event_base, LASTEvent);
refreshxrandr(0);
}
continue;
}
if (handler[ev.type]) if (handler[ev.type])
(handler[ev.type])(&ev); (handler[ev.type])(&ev);
} }
@ -2036,7 +2670,7 @@ run(void)
* maximum latency intervals during `cat huge.txt`, and perfect * maximum latency intervals during `cat huge.txt`, and perfect
* sync with periodic updates from animations/key-repeats/etc. * sync with periodic updates from animations/key-repeats/etc.
*/ */
if (FD_ISSET(ttyfd, &rfd) || xev) { if (ttyin || xev) {
if (!drawing) { if (!drawing) {
trigger = now; trigger = now;
drawing = 1; drawing = 1;
@ -2047,6 +2681,18 @@ run(void)
continue; /* we have time, try to find idle */ 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 */ /* idle detected or maxlatency exhausted -> draw */
timeout = -1; timeout = -1;
if (blinktimeout && tattrset(ATTR_BLINK)) { if (blinktimeout && tattrset(ATTR_BLINK)) {
@ -2091,6 +2737,9 @@ main(int argc, char *argv[])
case 'a': case 'a':
allowaltscreen = 0; allowaltscreen = 0;
break; break;
case 'A':
opt_alpha = EARGF(usage());
break;
case 'c': case 'c':
opt_class = EARGF(usage()); opt_class = EARGF(usage());
break; break;