From f9c30db62b61672410f7729d88c85a99679a6f71 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 11 May 2018 13:28:06 +0200 Subject: [PATCH] =?UTF-8?q?Respect=20Xft.dpi=20for=20determining=20the=20u?= =?UTF-8?q?nlock=20indicator=E2=80=99s=20scale=20factor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes #184 --- Makefile.am | 4 ++ configure.ac | 1 + dpi.c | 109 +++++++++++++++++++++++++++++++++++++++++++++ dpi.h | 22 +++++++++ i3lock.c | 3 ++ unlock_indicator.c | 19 +++----- 6 files changed, 144 insertions(+), 14 deletions(-) create mode 100644 dpi.c create mode 100644 dpi.h diff --git a/Makefile.am b/Makefile.am index aa70ded..3c0d16a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/configure.ac b/configure.ac index 3402736..9171521 100644 --- a/configure.ac +++ b/configure.ac @@ -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 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 +#include +#include +#include +#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 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); diff --git a/i3lock.c b/i3lock.c index 2bf3901..602ccca 100644 --- 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); diff --git a/unlock_indicator.c b/unlock_indicator.c index da1a7f2..a6a603f 100644 --- a/unlock_indicator.c +++ b/unlock_indicator.c @@ -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,