commit ad680b40192f665dd7af4d62ab6996318e275cb6
parent ae72b675ca4f104fc210e5ad476a8b91acf6beb3
Author: Ingo Bürk <admin@airblader.de>
Date: Fri, 11 May 2018 16:06:20 +0200
Merge pull request #185 from stapelberg/dpi
Respect Xft.dpi for determining the unlock indicator’s scale factor
Diffstat:
8 files changed, 175 insertions(+), 32 deletions(-)
diff --git a/.travis.yml b/.travis.yml
@@ -1,6 +1,8 @@
# Use Ubuntu 14.04 (trusty), as per http://blog.travis-ci.com/2015-10-14-opening-up-ubuntu-trusty-beta/
-sudo: required
+sudo: false
dist: trusty
+services:
+ - docker
language: c
compiler:
- gcc
@@ -9,21 +11,7 @@ addons:
apt:
packages:
- clang-format-3.5
- - pkg-config
- - libpam0g-dev
- - libcairo2-dev
- - libxcb1-dev
- - libxcb-dpms0-dev
- - libxcb-image0-dev
- - libxcb-util0-dev
- - libev-dev
- - libxcb-xinerama0-dev
- - libxcb-xkb-dev
-before_install:
- - "echo 'APT::Default-Release \"trusty\";' | sudo tee /etc/apt/apt.conf.d/default-release"
- - "echo 'deb http://archive.ubuntu.com/ubuntu/ xenial main universe' | sudo tee /etc/apt/sources.list.d/wily.list"
- - sudo apt-get update
- - sudo apt-get --force-yes -y install -t xenial libxkbcommon-dev libxkbcommon-x11-dev
script:
- - autoreconf -fi && mkdir -p build && cd build && (../configure || (cat config.log; false)) && make -j CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Werror"
- clang-format-3.5 -i *.[ch] && git diff --exit-code || (echo 'Code was not formatted using clang-format!'; false)
+ - docker build --pull --no-cache --rm -t=i3lock -f travis/Dockerfile .
+ - docker run -e CC=$CC -v $PWD:/usr/src:rw i3lock /bin/sh -c 'autoreconf -fi && mkdir -p build && cd build && (../configure || (cat config.log; false)) && make -j V=1 CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Werror"'
diff --git 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
@@ -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
@@ -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
@@ -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
@@ -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))
@@ -655,7 +656,7 @@ static bool verify_png_image(const char *image_path) {
// Check PNG header according to the specification, available at:
// https://www.w3.org/TR/2003/REC-PNG-20031110/#5PNG-file-signature
- static unsigned char PNG_REFERENCE_HEADER[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
+ static unsigned char PNG_REFERENCE_HEADER[8] = {137, 80, 78, 71, 13, 10, 26, 10};
if (memcmp(PNG_REFERENCE_HEADER, png_header, sizeof(png_header)) != 0) {
fprintf(stderr, "File \"%s\" does not start with a PNG header. i3lock currently only supports loading PNG files.\n", image_path);
return false;
@@ -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/travis/Dockerfile b/travis/Dockerfile
@@ -0,0 +1,25 @@
+# vim:ft=Dockerfile
+FROM debian:sid
+
+RUN echo force-unsafe-io > /etc/dpkg/dpkg.cfg.d/docker-apt-speedup
+# Paper over occasional network flakiness of some mirrors.
+RUN echo 'APT::Acquire::Retries "5";' > /etc/apt/apt.conf.d/80retry
+
+# NOTE: I tried exclusively using gce_debian_mirror.storage.googleapis.com
+# instead of httpredir.debian.org, but the results (Fetched 123 MB in 36s (3357
+# kB/s)) are not any better than httpredir.debian.org (Fetched 123 MB in 34s
+# (3608 kB/s)). Hence, let’s stick with httpredir.debian.org (default) for now.
+
+# Install mk-build-deps (for installing the i3 build dependencies),
+# clang and clang-format-3.8 (for checking formatting and building with clang),
+# lintian (for checking spelling errors),
+# test suite dependencies (for running tests)
+RUN apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ build-essential clang git autoconf automake libxcb-randr0-dev pkg-config libpam0g-dev \
+ libcairo2-dev libxcb1-dev libxcb-dpms0-dev libxcb-image0-dev libxcb-util0-dev \
+ libxcb-xrm-dev libev-dev libxcb-xinerama0-dev libxcb-xkb-dev libxkbcommon-dev \
+ libxkbcommon-x11-dev && \
+ rm -rf /var/lib/apt/lists/*
+
+WORKDIR /usr/src
diff --git 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)
@@ -81,26 +82,16 @@ 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.
*
*/
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,