xrandrfontsize-0.8.4-20211224-2f6e597.diff - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       xrandrfontsize-0.8.4-20211224-2f6e597.diff (8924B)
       ---
            1 diff --git a/config.def.h b/config.def.h
            2 index f92798a..517abbb 100644
            3 --- a/config.def.h
            4 +++ b/config.def.h
            5 @@ -9,5 +9,19 @@
            6  static int borderpx = 2;
            7  
            8 +/*
            9 + * Override/adjust fontsize of choosen monitors:
           10 + */
           11 +MonitorConfig monitors_config[] = {
           12 +        // skip = fixed relative points size (monitor dpi)
           13 +        //   =0 : fixed absolute pixel size (default screen dpi)
           14 +        //   >0 : auto absolute pixel size (monitor dpi)
           15 +        //   <0 : auto relative points size (monitor dpi)
           16 +        // {"DP-1", 0}, // BUG:(size=0): not restored to default after back'n'forth
           17 +        {"HDMI-0~1", -20},  // BUG:(ignored DPI=220): = 20 is eqv to 10pt (DPI=110)
           18 +        {"HDMI-0~2", -14},
           19 +};
           20 +float winmovethreshold = 0.6;
           21 +
           22  /*
           23   * What program is execed by st depends of these precedence rules:
           24   * 1: program passed with -e
           25 @@ -272,6 +272,7 @@ static Shortcut shortcuts[] = {
           26          { TERMMOD,              XK_Prior,       zoom,           {.f = +1} },
           27          { TERMMOD,              XK_Next,        zoom,           {.f = -1} },
           28          { TERMMOD,              XK_Home,        zoomreset,      {.f =  0} },
           29 +        { TERMMOD,              XK_End,         refreshxrandr,  {.i =  0} },
           30          { TERMMOD,              XK_C,           clipcopy,       {.i =  0} },
           31          { TERMMOD,              XK_V,           clippaste,      {.i =  0} },
           32          { TERMMOD,              XK_Y,           selpaste,       {.i =  0} },
           33 diff --git a/config.mk b/config.mk
           34 index aaa54ff..2d28597 100644
           35 --- a/config.mk
           36 +++ b/config.mk
           37 @@ -24,5 +24,7 @@ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
           38  
           39 +LIBS += -lXrandr
           40 +
           41  # flags
           42  STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
           43  STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
           44  STLDFLAGS = $(LIBS) $(LDFLAGS)
           45 diff --git a/x.c b/x.c
           46 index 684311c..ce1c418 100644
           47 --- a/x.c
           48 +++ b/x.c
           49 @@ -14,6 +14,7 @@
           50  #include <X11/keysym.h>
           51  #include <X11/Xft/Xft.h>
           52  #include <X11/XKBlib.h>
           53 +#include <X11/extensions/Xrandr.h>
           54  
           55  char *argv0;
           56  #include "arg.h"
           57 @@ -45,5 +46,18 @@ typedef struct {
           58          signed char appcursor; /* application cursor */
           59  } Key;
           60  
           61 +typedef struct {
           62 +        const char *name;
           63 +        float defaultfontsize;
           64 +} MonitorConfig;
           65 +
           66 +typedef struct {
           67 +        Atom name;
           68 +        int x, y, w, h;
           69 +        float defaultfontsize, usedfontsize;
           70 +} MonitorInfo;
           71 +
           72 +static void refreshxrandr(const Arg *dummy);
           73 +
           74  /* X modifiers */
           75  #define XK_ANY_MOD    UINT_MAX
           76 @@ -233,7 +233,12 @@ static XWindow xw;
           77          [PropertyNotify] = propnotify,
           78          [SelectionRequest] = selrequest,
           79  };
           80  
           81 +static double defaultrelfontsize = 0;
           82 +static MonitorInfo *monitors_info = NULL;
           83 +static int monitors_num = 0;
           84 +static int prev_mindex = -1;
           85 +
           86  /* Globals */
           87  static DC dc;
           88  static XWindow xw;
           89 @@ -2280,6 +2280,144 @@ xseturgency(int add)
           90          XFree(h);
           91  }
           92  
           93 +static void
           94 +cachemonitorinfo()
           95 +{
           96 +        int prev_num = monitors_num;
           97 +        MonitorInfo *prev_info = monitors_info;
           98 +        XRRMonitorInfo *xmonitors = XRRGetMonitors(xw.dpy, XRootWindow(xw.dpy, xw.scr), 1, &monitors_num);
           99 +        if (!monitors_num)
          100 +                die("xrandr found no monitors");
          101 +
          102 +        monitors_info = xmalloc(monitors_num * sizeof(MonitorInfo));
          103 +
          104 +        for (int i = 0; i < monitors_num; ++i) {
          105 +                XRRMonitorInfo *xm = &xmonitors[i];
          106 +                MonitorInfo *m = &monitors_info[i];
          107 +
          108 +                m->name = xm->name;
          109 +                m->x = xm->x;
          110 +                m->y = xm->y;
          111 +                m->w = xm->width;
          112 +                m->h = xm->height;
          113 +
          114 +                float px_mm = ((float)m->w / xm->mwidth + (float)m->h / xm->mheight) / 2;
          115 +                float px_pt = 25.4 * px_mm / 72;
          116 +                m->defaultfontsize = defaultrelfontsize * px_pt;
          117 +
          118 +                // Override defaultfontsize (dpi) by user config
          119 +                char *name = XGetAtomName(xw.dpy, xm->name);
          120 +                for (int j = 0; j < LEN(monitors_config); ++j)
          121 +                        if (!strcmp(name, monitors_config[j].name)) {
          122 +                                m->defaultfontsize = monitors_config[j].defaultfontsize;
          123 +                                if (m->defaultfontsize < 0)
          124 +                                        m->defaultfontsize *= -px_pt;
          125 +                                break;
          126 +                        }
          127 +                // fprintf(stderr, "%s: %fpx, %f\n", name, m->defaultfontsize, m->usedfontsize);
          128 +                XFree(name);
          129 +
          130 +                // Restore usedfontsize (zoom) after re-cache for monitors with the same name
          131 +                m->usedfontsize = m->defaultfontsize;
          132 +                for (int j = 0; j < prev_num; ++j)
          133 +                        if (prev_info[j].name == m->name) {
          134 +                                m->usedfontsize = prev_info[j].usedfontsize;
          135 +                                break;
          136 +                        }
          137 +        }
          138 +
          139 +        XRRFreeMonitors(xmonitors);
          140 +        free(prev_info);
          141 +}
          142 +
          143 +static int
          144 +getmonitorindex_threshold(int w, int h, int x, int y)
          145 +{
          146 +        int mindex = -1;
          147 +        float fontsize = 0;
          148 +        int thresholdarea = winmovethreshold * w * h;
          149 +
          150 +        for (int i = 0; i < monitors_num; ++i) {
          151 +                MonitorInfo *m = &monitors_info[i];
          152 +                int overlap_w = MAX(0, MIN(x + w, m->x + m->w) - MAX(x, m->x));
          153 +                int overlap_h = MAX(0, MIN(y + h, m->y + m->h) - MAX(y, m->y));
          154 +                int area = overlap_w * overlap_h;
          155 +                // Choose monitor with largest dpi (defaultfontsize)
          156 +                //  from all "mirrored"/overlapped (e.g. projector)
          157 +                if (area >= thresholdarea && fontsize < m->defaultfontsize) {
          158 +                        fontsize = m->defaultfontsize;
          159 +                        mindex = i;
          160 +                }
          161 +        }
          162 +        return mindex;
          163 +}
          164 +
          165 +static int
          166 +getmonitorindex_nearest(int w, int h, int x, int y)
          167 +{
          168 +        int mindex = -1;
          169 +        float fontsize = 0;
          170 +        int overlaparea = 0;
          171 +
          172 +        for (int i = 0; i < monitors_num; ++i) {
          173 +                MonitorInfo *m = &monitors_info[i];
          174 +                int overlap_w = MAX(0, MIN(x + w, m->x + m->w) - MAX(x, m->x));
          175 +                int overlap_h = MAX(0, MIN(y + h, m->y + m->h) - MAX(y, m->y));
          176 +                int area = overlap_w * overlap_h;
          177 +                // Choose monitor with largest overlapping area
          178 +                //  e.g. when "st" is initially spawned in-between monitors
          179 +                if (area > overlaparea) {
          180 +                        overlaparea = area;
          181 +                        mindex = i;
          182 +                }
          183 +        }
          184 +        return mindex;
          185 +}
          186 +
          187 +static void
          188 +adjustmonitorfontsize(int mindex)
          189 +{
          190 +        if (mindex < 0 || prev_mindex == mindex)
          191 +                return;
          192 +        // Save zoom of current monitor before switching
          193 +        if (prev_mindex >= 0)
          194 +                monitors_info[prev_mindex].usedfontsize = usedfontsize;
          195 +
          196 +        defaultfontsize = monitors_info[mindex].defaultfontsize;
          197 +        // fprintf(stderr, "Crossing: %fpx\n", defaultfontsize);
          198 +
          199 +        // NOTE: do nothing if font size differs by less than 1%
          200 +        double fontsize = monitors_info[mindex].usedfontsize;
          201 +        double delta = 0.01 * usedfontsize;
          202 +        if (!BETWEEN(fontsize - usedfontsize, -delta, delta)) {
          203 +                // fprintf(stderr, "Adjusted: %fpx\n", fontsize);
          204 +                xunloadfonts();
          205 +                xloadfonts(usedfont, fontsize);
          206 +        }
          207 +        prev_mindex = mindex;
          208 +}
          209 +
          210 +void
          211 +refreshxrandr(const Arg *dummy)
          212 +{
          213 +        // Reset index to detect change of window association on "xrandr ... --primary"
          214 +        //  otherwise: zoom won't be saved on switching and new font size won't be loaded
          215 +        // CRIT!!! event from xrandr may place another monitor into same index
          216 +        if (prev_mindex >= 0)
          217 +                monitors_info[prev_mindex].usedfontsize = usedfontsize;
          218 +        prev_mindex = -1;
          219 +
          220 +        XWindowAttributes xattr = {0};
          221 +        cachemonitorinfo();
          222 +        XGetWindowAttributes(xw.dpy, xw.win, &xattr);
          223 +
          224 +        int mindex = getmonitorindex_threshold(xattr.width, xattr.height, xattr.x, xattr.y);
          225 +        if (mindex < 0)
          226 +                mindex = getmonitorindex_nearest(xattr.width, xattr.height, xattr.x, xattr.y);
          227 +        adjustmonitorfontsize(mindex);
          228 +}
          229 +
          230 +
          231  void
          232  xbell(void)
          233  {
          234 @@ -2437,6 +2437,14 @@ cmessage(XEvent *e)
          235  void
          236  resize(XEvent *e)
          237  {
          238 +        // BAD: no resize on monitor plug/unplug/reconfigure -- until window itself is kept in the same place
          239 +        // NOTE: no resize event on zoomabs()
          240 +        // fprintf(stderr, "Resize: %dx%d+%d+%d\n",
          241 +        //     e->xconfigure.width, e->xconfigure.height, e->xconfigure.x, e->xconfigure.y);
          242 +
          243 +        adjustmonitorfontsize(getmonitorindex_threshold(
          244 +            e->xconfigure.width, e->xconfigure.height, e->xconfigure.x, e->xconfigure.y));
          245 +
          246          if (e->xconfigure.width == win.w && e->xconfigure.height == win.h)
          247                  return;
          248  
          249 @@ -2469,6 +2469,22 @@ run(void)
          250                  }
          251          } while (ev.type != MapNotify);
          252  
          253 +        int rr_event_base, rr_error_base, rr_major, rr_minor;
          254 +        if (!XRRQueryExtension (xw.dpy, &rr_event_base, &rr_error_base) ||
          255 +            !XRRQueryVersion (xw.dpy, &rr_major, &rr_minor) ||
          256 +            rr_major < 1 || (rr_major == 1 && rr_minor < 5))
          257 +        {
          258 +                die("RandR 1.5 extension isn't available\n");
          259 +        }
          260 +        XRRSelectInput(xw.dpy, xw.win, RRCrtcChangeNotifyMask);
          261 +
          262 +        // WARN: can query actual window size/pos only after window is mapped and its width/height are adjusted by WM
          263 +        //  * x/y are WM-dependent and can't be determined beforehand anyway
          264 +        //  * defaultfontsize isn't available until font is loaded and actual Fc*() size queried
          265 +        // BAD: fonts on startup are always reloaded -- how to specify their size beforehand ?
          266 +        FcPatternGetDouble(dc.font.match->pattern, FC_SIZE, 0, &defaultrelfontsize);
          267 +        refreshxrandr(0);
          268 +
          269          ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd);
          270          cresize(w, h);
          271  
          272 @@ -2500,6 +2500,16 @@ run(void)
          273                          XNextEvent(xw.dpy, &ev);
          274                          if (XFilterEvent(&ev, None))
          275                                  continue;
          276 +                        if (LASTEvent <= ev.type) {
          277 +                                if (rr_event_base + RRNotify == ev.type &&
          278 +                                        RRNotify_CrtcChange == ((XRRNotifyEvent *)&ev)->subtype)
          279 +                                {
          280 +                                        XRRUpdateConfiguration(&ev);
          281 +                                        // fprintf(stderr, "Monitor change: %d > %d\n", rr_event_base, LASTEvent);
          282 +                                        refreshxrandr(0);
          283 +                                }
          284 +                                continue;
          285 +                        }
          286                          if (handler[ev.type])
          287                                  (handler[ev.type])(&ev);
          288                  }