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 }