commit 194d3409bb68d0dc19b2089b27d1d88b309e6d15
Author: Dmytro Kolomoiets <[email protected]>
Date:   Fri Dec 24 15:08:50 2021 +0200

    st - add patch xrandrfontsize

diff --git a/st.suckless.org/patches/xrandrfontsize/index.md 
b/st.suckless.org/patches/xrandrfontsize/index.md
new file mode 100644
index 00000000..99967e55
--- /dev/null
+++ 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 - <[email protected]>
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
new file mode 100644
index 00000000..d7817a02
--- /dev/null
+++ 
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
", 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
", 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
", 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
",
++      //     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
");
++      }
++      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
", rr_event_base, LASTEvent);
++                                      refreshxrandr(0);
++                              }
++                              continue;
++                      }
+                       if (handler[ev.type])
+                               (handler[ev.type])(&ev);
+               }


Reply via email to