sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

commit 194d3409bb68d0dc19b2089b27d1d88b309e6d15
parent 04ea68c362646efcbe743c614664729bd295be5a
Author: Dmytro Kolomoiets <amerlyq+code@gmail.com>
Date:   Fri, 24 Dec 2021 15:08:50 +0200

st - add patch xrandrfontsize

Diffstat:
Ast.suckless.org/patches/xrandrfontsize/index.md | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ast.suckless.org/patches/xrandrfontsize/xrandrfontsize-0.8.4-20211224-2f6e597.diff | 288+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 342 insertions(+), 0 deletions(-)

diff --git a/st.suckless.org/patches/xrandrfontsize/index.md b/st.suckless.org/patches/xrandrfontsize/index.md @@ -0,0 +1,54 @@ +xrandrfontsize +============== + +Description +----------- +Solves the frustrating necessity of adjusting the font size when constantly +moving terminal window between monitors (i.e. by tiling WM). + +Primary usecases: + +* monitors have very different DPI (e.g. HiDPI laptop + FullHD monitor); +* one monitor is near you (laptop) and another is far (UHD TV on the wall); +* one monitor is for coding (large font) and another for logs (small font); +* single ultrawide monitor is split into two (for main working area to be always in the center -- instead of bottom left corner). + +Example for monitor splitting (first associated area is treated as primary): +```bash +xrandr --listmonitors | grep -wo "HDMI-0~[0-9]" | sed 's/^/--delmonitor /' | xargs -r xrandr +xrandr --setmonitor HDMI-0~1 2845/648x1600/366+995+0 HDMI-0 --setmonitor HDMI-0~2 995/226x1600/366+0+0 none +``` + +Configure +--------- + +Associative map value meaning: + +* `commented-out` : fixed relative points size (monitor dpi) +* `=0` : use fixed absolute pixel size (default screen dpi) +* `>0` : use auto absolute pixel size (monitor dpi) +* `<0` : use auto relative points size (monitor dpi) + +```c +MonitorConfig monitors_config[] = { + // {"eDP-1", ...}, // commented-out, uses default font size as usual + {"DP-1", 0}, // auto-scales font size based on global Xorg DPI + {"HDMI-0~1", -20}, // applies relative font size (e.g. "monospace-20") + {"HDMI-0~2", -14}, // uses smaller font for ultrawide side-screen + {"HDMI-1", 18}, // fixed font size for TV (i.e. "monospace:pixelsize=18") +}; +``` + +NOTE: font size is assigned separately per each output name, because there is +not much sense to scale font size strictly and **solely** by DPI. +And you may need *intentionally* different font sizes (like usecases above). + +Download +-------- +* [xrandrfontsize-0.8.4-20211224-2f6e597.diff](xrandrfontsize-0.8.4-20211224-2f6e597.diff) + +INFO: it clearly applies over at least 15 other patches. + +Authors +------- +* Dmytro Kolomoiets - <amerlyq+code@gmail.com> diff --git a/st.suckless.org/patches/xrandrfontsize/xrandrfontsize-0.8.4-20211224-2f6e597.diff b/st.suckless.org/patches/xrandrfontsize/xrandrfontsize-0.8.4-20211224-2f6e597.diff @@ -0,0 +1,288 @@ +diff --git a/config.def.h b/config.def.h +index f92798a..517abbb 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -9,5 +9,19 @@ + 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: + * 1: program passed with -e +@@ -272,6 +272,7 @@ static Shortcut shortcuts[] = { + { TERMMOD, XK_Prior, zoom, {.f = +1} }, + { TERMMOD, XK_Next, zoom, {.f = -1} }, + { TERMMOD, XK_Home, zoomreset, {.f = 0} }, ++ { TERMMOD, XK_End, refreshxrandr, {.i = 0} }, + { TERMMOD, XK_C, clipcopy, {.i = 0} }, + { TERMMOD, XK_V, clippaste, {.i = 0} }, + { TERMMOD, XK_Y, selpaste, {.i = 0} }, +diff --git a/config.mk b/config.mk +index aaa54ff..2d28597 100644 +--- a/config.mk ++++ b/config.mk +@@ -24,5 +24,7 @@ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ + ++LIBS += -lXrandr ++ + # flags + STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 + STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) + STLDFLAGS = $(LIBS) $(LDFLAGS) +diff --git a/x.c b/x.c +index 684311c..ce1c418 100644 +--- a/x.c ++++ b/x.c +@@ -14,6 +14,7 @@ + #include <X11/keysym.h> + #include <X11/Xft/Xft.h> + #include <X11/XKBlib.h> ++#include <X11/extensions/Xrandr.h> + + char *argv0; + #include "arg.h" +@@ -45,5 +46,18 @@ typedef struct { + signed char appcursor; /* application cursor */ + } Key; + ++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); ++ + /* X modifiers */ + #define XK_ANY_MOD UINT_MAX +@@ -233,7 +233,12 @@ static XWindow xw; + [PropertyNotify] = propnotify, + [SelectionRequest] = selrequest, + }; + ++static double defaultrelfontsize = 0; ++static MonitorInfo *monitors_info = NULL; ++static int monitors_num = 0; ++static int prev_mindex = -1; ++ + /* Globals */ + static DC dc; + static XWindow xw; +@@ -2280,6 +2280,144 @@ xseturgency(int add) + 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 + xbell(void) + { +@@ -2437,6 +2437,14 @@ cmessage(XEvent *e) + void + 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) + return; + +@@ -2469,6 +2469,22 @@ run(void) + } + } 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); + cresize(w, h); + +@@ -2500,6 +2500,16 @@ run(void) + XNextEvent(xw.dpy, &ev); + if (XFilterEvent(&ev, None)) + 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]) + (handler[ev.type])(&ev); + }