summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzachir <zachir@librem.one>2023-02-20 04:16:34 -0600
committerzachir <zachir@librem.one>2023-02-20 04:16:34 -0600
commitc445a3349c6b4afa0b655369d307eef948d7bc73 (patch)
tree62099e0565aa72fa7de9eeba06566984fb458de9
parent2dbfaa34e165ba7a49100c8117b4c563895a21ef (diff)
add xrandrfontsize patch
-rw-r--r--config.def.h15
-rw-r--r--config.mk2
-rw-r--r--patches/xrandrfontsize-0.8.4-20211224-2f6e597.diff288
-rw-r--r--x.c191
4 files changed, 496 insertions, 0 deletions
diff --git a/config.def.h b/config.def.h
index 5f304ab..febc88d 100644
--- a/config.def.h
+++ b/config.def.h
@@ -1,6 +1,20 @@
/* See LICENSE file for copyright and license details. */
/*
+ * 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;
+
+/*
* appearance
*
* font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
@@ -235,6 +249,7 @@ static Shortcut shortcuts[] = {
{ ControlMask, XK_equal, zoom, {.f = +1} },
{ ControlMask, XK_minus, zoom, {.f = -1} },
{ TERMMOD, XK_plus, zoomreset, {.f = 0} },
+ { TERMMOD, XK_underscore, 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 c23c2ab..fd8c4d7 100644
--- a/config.mk
+++ b/config.mk
@@ -22,6 +22,8 @@ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender -lgd\
`$(PKG_CONFIG) --libs fontconfig` \
`$(PKG_CONFIG) --libs freetype2`
+LIBS += -lXrandr
+
# flags
STCPPFLAGS = -DVERSION=\"$(VERSION)\" -DICON=\"$(ICONPREFIX)/$(ICONNAME)\" -D_XOPEN_SOURCE=600
STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
diff --git a/patches/xrandrfontsize-0.8.4-20211224-2f6e597.diff b/patches/xrandrfontsize-0.8.4-20211224-2f6e597.diff
new file mode 100644
index 0000000..d7817a0
--- /dev/null
+++ b/patches/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);
+ }
diff --git a/x.c b/x.c
index 0e07dae..7f8caa4 100644
--- a/x.c
+++ b/x.c
@@ -15,6 +15,7 @@
#include <X11/keysym.h>
#include <X11/Xft/Xft.h>
#include <X11/XKBlib.h>
+#include <X11/extensions/Xrandr.h>
char *argv0;
#include "arg.h"
@@ -55,6 +56,19 @@ enum undercurl_slope_type {
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);
+
/* X modifiers */
#define XK_ANY_MOD UINT_MAX
#define XK_NO_MOD 0
@@ -237,6 +251,11 @@ static void (*handler[LASTEvent])(XEvent *) = {
[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;
@@ -2396,6 +2415,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)
{
@@ -2561,6 +2718,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;
@@ -2596,6 +2761,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);
@@ -2628,6 +2809,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);
}