commits
tags
diff --git a/src/sxwm.c b/src/sxwm.c
index 354c865..749ad65 100644
--- a/src/sxwm.c
+++ b/src/sxwm.c
@@ -30,6 +30,7 @@
#include <X11/Xutil.h>
#include <X11/extensions/Xinerama.h>
+#include <X11/extensions/Xrandr.h>
#include <X11/Xcursor/Xcursor.h>
#include "defs.h"
@@ -72,6 +73,7 @@ void hdl_unmap_ntf(XEvent *xev);
/* void inc_gaps(void); */
void init_defaults(void);
Bool is_child_proc(pid_t pid1, pid_t pid2);
+void load_cursors(void);
/* void move_master_next(void); */
/* void move_master_prev(void); */
/* void move_next_mon(void); */
@@ -1035,6 +1037,7 @@ void hdl_config_ntf(XEvent *xev)
{
if (xev->xconfigure.window == root) {
update_mons();
+ load_cursors();
tile();
update_borders();
}
@@ -2250,10 +2253,7 @@ void setup(void)
grab_keys();
startup_exec();
- cursor_normal = XcursorLibraryLoadCursor(dpy, "left_ptr");
- cursor_move = XcursorLibraryLoadCursor(dpy, "fleur");
- cursor_resize = XcursorLibraryLoadCursor(dpy, "bottom_right_corner");
- XDefineCursor(dpy, root, cursor_normal);
+ load_cursors();
scr_width = XDisplayWidth(dpy, DefaultScreen(dpy));
scr_height = XDisplayHeight(dpy, DefaultScreen(dpy));
@@ -3055,6 +3055,60 @@ void update_modifier_masks(void)
XFreeModifiermap(mod_mapping);
}
+/* (re)loads the pointer cursors, sizing them from the *primary* monitor's
+ * real DPI instead of Xcursor's own default. Xinerama has no notion of
+ * physical monitor dimensions, so XcursorLibraryLoadCursor falls back to
+ * deriving DPI from the whole virtual screen's pixel height against
+ * whatever mm-height DisplayHeightMM() happens to report for it -- once a
+ * second monitor is added that no longer corresponds to any single
+ * monitor's real DPI, and the cursor renders far too large. Query the
+ * primary monitor's own size via RandR (which does expose mm dimensions)
+ * and set XCURSOR_SIZE from that instead, unless the user already set
+ * XCURSOR_SIZE themselves. Called once at startup and again from
+ * hdl_config_ntf() on every root ConfigureNotify (monitor hotplug/layout
+ * change), so docking/undocking keeps the cursor correctly sized. */
+void load_cursors(void)
+{
+ if (!getenv("XCURSOR_SIZE")) {
+ int rr_event_base, rr_err_base;
+ if (XRRQueryExtension(dpy, &rr_event_base, &rr_err_base)) {
+ int n_info;
+ XRRMonitorInfo *info = XRRGetMonitors(dpy, root, True, &n_info);
+ if (info) {
+ int idx = 0;
+ for (int i = 0; i < n_info; i++) {
+ if (info[i].primary) {
+ idx = i;
+ break;
+ }
+ }
+ if (n_info > 0 && info[idx].mheight > 0 && info[idx].height > 0) {
+ /* cursor_size = dpi * 16 / 96;
+ * dpi = height_px * 25.4 / mheight_mm */
+ int size = info[idx].height * 16 * 254 / (info[idx].mheight * 960);
+ size = MAX(16, (size + 7) & ~7); /* round up to a multiple of 8 */
+ char buf[16];
+ snprintf(buf, sizeof buf, "%d", size);
+ setenv("XCURSOR_SIZE", buf, 1);
+ }
+ XRRFreeMonitors(info);
+ }
+ }
+ }
+
+ if (cursor_normal)
+ XFreeCursor(dpy, cursor_normal);
+ if (cursor_move)
+ XFreeCursor(dpy, cursor_move);
+ if (cursor_resize)
+ XFreeCursor(dpy, cursor_resize);
+
+ cursor_normal = XcursorLibraryLoadCursor(dpy, "left_ptr");
+ cursor_move = XcursorLibraryLoadCursor(dpy, "fleur");
+ cursor_resize = XcursorLibraryLoadCursor(dpy, "bottom_right_corner");
+ XDefineCursor(dpy, root, cursor_normal);
+}
+
void update_mons(void)
{
XineramaScreenInfo *info;
diff --git a/Makefile b/Makefile
index 2653a39..46afb75 100644
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@ PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man
# libs
-LIBS = -lX11 -lXinerama -lXcursor
+LIBS = -lX11 -lXinerama -lXrandr -lXcursor
# flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700