]> git.sur5r.net Git - i3/i3lock/commitdiff
Respect Xft.dpi for determining the unlock indicator’s scale factor
authorMichael Stapelberg <michael@stapelberg.de>
Fri, 11 May 2018 11:28:06 +0000 (13:28 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Fri, 11 May 2018 11:28:06 +0000 (13:28 +0200)
fixes #184

Makefile.am
configure.ac
dpi.c [new file with mode: 0644]
dpi.h [new file with mode: 0644]
i3lock.c
unlock_indicator.c

index aa70ded89e08d7d797997064b73feef1a838b510..3c0d16abe3e456518916c3de32ef680768a15aff 100644 (file)
@@ -19,6 +19,7 @@ i3lock_CFLAGS = \
        $(XCB_CFLAGS) \
        $(XCB_IMAGE_CFLAGS) \
        $(XCB_UTIL_CFLAGS) \
+       $(XCB_UTIL_XRM_CFLAGS) \
        $(XKBCOMMON_CFLAGS) \
        $(CAIRO_CFLAGS) \
        $(CODE_COVERAGE_CFLAGS)
@@ -31,12 +32,15 @@ i3lock_LDADD = \
        $(XCB_LIBS) \
        $(XCB_IMAGE_LIBS) \
        $(XCB_UTIL_LIBS) \
+       $(XCB_UTIL_XRM_LIBS) \
        $(XKBCOMMON_LIBS) \
        $(CAIRO_LIBS) \
        $(CODE_COVERAGE_LDFLAGS)
 
 i3lock_SOURCES = \
        cursors.h \
+       dpi.c \
+       dpi.h \
        i3lock.c \
        i3lock.h \
        randr.c \
index 3402736ef89689ab90e948c7f64f62a9f10e8cb7..91715217dcd5994d4471e42044a8286e6194f2f0 100644 (file)
@@ -93,6 +93,7 @@ dnl downloaded in a newer version and would like to overwrite.
 PKG_CHECK_MODULES([XCB], [xcb xcb-xkb xcb-xinerama xcb-randr])
 PKG_CHECK_MODULES([XCB_IMAGE], [xcb-image])
 PKG_CHECK_MODULES([XCB_UTIL], [xcb-event xcb-util xcb-atom])
+PKG_CHECK_MODULES([XCB_UTIL_XRM], [xcb-xrm])
 PKG_CHECK_MODULES([XKBCOMMON], [xkbcommon xkbcommon-x11])
 PKG_CHECK_MODULES([CAIRO], [cairo])
 
diff --git a/dpi.c b/dpi.c
new file mode 100644 (file)
index 0000000..3cb08ee
--- /dev/null
+++ b/dpi.c
@@ -0,0 +1,109 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ */
+#include "dpi.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <xcb/xcb_xrm.h>
+#include "xcb.h"
+#include "i3lock.h"
+
+extern bool debug_mode;
+
+static long dpi;
+
+extern xcb_screen_t *screen;
+
+static long init_dpi_fallback(void) {
+    return (double)screen->height_in_pixels * 25.4 / (double)screen->height_in_millimeters;
+}
+
+/*
+ * Initialize the DPI setting.
+ * This will use the 'Xft.dpi' X resource if available and fall back to
+ * guessing the correct value otherwise.
+ */
+void init_dpi(void) {
+    xcb_xrm_database_t *database = NULL;
+    char *resource = NULL;
+
+    if (conn == NULL) {
+        goto init_dpi_end;
+    }
+
+    database = xcb_xrm_database_from_default(conn);
+    if (database == NULL) {
+        DEBUG("Failed to open the resource database.\n");
+        goto init_dpi_end;
+    }
+
+    xcb_xrm_resource_get_string(database, "Xft.dpi", NULL, &resource);
+    if (resource == NULL) {
+        DEBUG("Resource Xft.dpi not specified, skipping.\n");
+        goto init_dpi_end;
+    }
+
+    char *endptr;
+    double in_dpi = strtod(resource, &endptr);
+    if (in_dpi == HUGE_VAL || dpi < 0 || *endptr != '\0' || endptr == resource) {
+        DEBUG("Xft.dpi = %s is an invalid number and couldn't be parsed.\n", resource);
+        dpi = 0;
+        goto init_dpi_end;
+    }
+    dpi = (long)round(in_dpi);
+
+    DEBUG("Found Xft.dpi = %ld.\n", dpi);
+
+init_dpi_end:
+    if (resource != NULL) {
+        free(resource);
+    }
+
+    if (database != NULL) {
+        xcb_xrm_database_free(database);
+    }
+
+    if (dpi == 0) {
+        DEBUG("Using fallback for calculating DPI.\n");
+        dpi = init_dpi_fallback();
+        DEBUG("Using dpi = %ld\n", dpi);
+    }
+}
+
+/*
+ * This function returns the value of the DPI setting.
+ *
+ */
+long get_dpi_value(void) {
+    return dpi;
+}
+
+/*
+ * Convert a logical amount of pixels (e.g. 2 pixels on a “standard” 96 DPI
+ * screen) to a corresponding amount of physical pixels on a standard or retina
+ * screen, e.g. 5 pixels on a 227 DPI MacBook Pro 13" Retina screen.
+ *
+ */
+int logical_px(const int logical) {
+    if (screen == NULL) {
+        /* Dpi info may not be available when parsing a config without an X
+         * server, such as for config file validation. */
+        return logical;
+    }
+
+    /* There are many misconfigurations out there, i.e. systems with screens
+     * whose dpi is in fact higher than 96 dpi, but not significantly higher,
+     * so software was never adapted. We could tell people to reconfigure their
+     * systems to 96 dpi in order to get the behavior they expect/are used to,
+     * but since we can easily detect this case in code, let’s do it for them.
+     */
+    if ((dpi / 96.0) < 1.25)
+        return logical;
+    return ceil((dpi / 96.0) * logical);
+}
diff --git a/dpi.h b/dpi.h
new file mode 100644 (file)
index 0000000..35840d2
--- /dev/null
+++ b/dpi.h
@@ -0,0 +1,22 @@
+#pragma once
+
+/**
+ * Initialize the DPI setting.
+ * This will use the 'Xft.dpi' X resource if available and fall back to
+ * guessing the correct value otherwise.
+ */
+void init_dpi(void);
+
+/**
+ * This function returns the value of the DPI setting.
+ *
+ */
+long get_dpi_value(void);
+
+/**
+ * Convert a logical amount of pixels (e.g. 2 pixels on a “standard” 96 DPI
+ * screen) to a corresponding amount of physical pixels on a standard or retina
+ * screen, e.g. 5 pixels on a 227 DPI MacBook Pro 13" Retina screen.
+ *
+ */
+int logical_px(const int logical);
index 2bf3901bf82a792a9666653d7c99046b74975e50..602ccca78a91d5171de81eed2a95ccee9b37f006 100644 (file)
--- a/i3lock.c
+++ b/i3lock.c
@@ -46,6 +46,7 @@
 #include "cursors.h"
 #include "unlock_indicator.h"
 #include "randr.h"
+#include "dpi.h"
 
 #define TSTAMP_N_SECS(n) (n * 1.0)
 #define TSTAMP_N_MINS(n) (60 * TSTAMP_N_SECS(n))
@@ -1033,6 +1034,8 @@ int main(int argc, char *argv[]) {
 
     screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
 
+    init_dpi();
+
     randr_init(&randr_base, screen->root);
     randr_query(screen->root);
 
index da1a7f23663c3ced725be12311d60be243b24c4b..a6a603f13c8cf0d7beb72028b96927002a6943e2 100644 (file)
@@ -20,6 +20,7 @@
 #include "xcb.h"
 #include "unlock_indicator.h"
 #include "randr.h"
+#include "dpi.h"
 
 #define BUTTON_RADIUS 90
 #define BUTTON_SPACE (BUTTON_RADIUS + 5)
@@ -80,17 +81,6 @@ static xcb_visualtype_t *vistype;
 unlock_state_t unlock_state;
 auth_state_t auth_state;
 
-/*
- * Returns the scaling factor of the current screen. E.g., on a 227 DPI MacBook
- * Pro 13" Retina screen, the scaling factor is 227/96 = 2.36.
- *
- */
-static double scaling_factor(void) {
-    const int dpi = (double)screen->height_in_pixels * 25.4 /
-                    (double)screen->height_in_millimeters;
-    return (dpi / 96.0);
-}
-
 /*
  * Draws global image with fill color onto a pixmap with the given
  * resolution and returns it.
@@ -98,9 +88,10 @@ static double scaling_factor(void) {
  */
 xcb_pixmap_t draw_image(uint32_t *resolution) {
     xcb_pixmap_t bg_pixmap = XCB_NONE;
-    int button_diameter_physical = ceil(scaling_factor() * BUTTON_DIAMETER);
+    const double scaling_factor = get_dpi_value() / 96.0;
+    int button_diameter_physical = ceil(scaling_factor * BUTTON_DIAMETER);
     DEBUG("scaling_factor is %.f, physical diameter is %d px\n",
-          scaling_factor(), button_diameter_physical);
+          scaling_factor, button_diameter_physical);
 
     if (!vistype)
         vistype = get_root_visual_type(screen);
@@ -142,7 +133,7 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
 
     if (unlock_indicator &&
         (unlock_state >= STATE_KEY_PRESSED || auth_state > STATE_AUTH_IDLE)) {
-        cairo_scale(ctx, scaling_factor(), scaling_factor());
+        cairo_scale(ctx, scaling_factor, scaling_factor);
         /* Draw a (centered) circle with transparent background. */
         cairo_set_line_width(ctx, 10.0);
         cairo_arc(ctx,