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 }