Merge pull request #608 from bebehei/xrandr-dpi

XRandR DPI Online Changes
This commit is contained in:
Nikos Tsipinakis 2019-03-23 20:04:50 +02:00 committed by GitHub
commit 591c6f91f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 133 additions and 60 deletions

View File

@ -237,7 +237,7 @@ static PangoLayout *layout_create(cairo_t *c)
struct screen_info *screen = get_active_screen();
PangoContext *context = pango_cairo_create_context(c);
pango_cairo_context_set_resolution(context, get_dpi_for_screen(screen));
pango_cairo_context_set_resolution(context, screen_dpi_get(screen));
PangoLayout *layout = pango_layout_new(context);

View File

@ -25,8 +25,6 @@ int screens_len;
bool dunst_follow_errored = false;
int randr_event_base = 0;
static int randr_major_version = 0;
static int randr_minor_version = 0;
@ -39,59 +37,84 @@ static int x_follow_tear_down_error_handler(void);
static int FollowXErrorHandler(Display *display, XErrorEvent *e);
static Window get_focused_window(void);
static double get_xft_dpi_value(void)
/**
* A cache variable to cache the Xft.dpi xrdb values.
* We do not expect to change xrdb often, but there's much
* overhead to query it once.
*
* @retval -DBL_MAX: uncached
* @retval <=0: Invalid and unusable value
* @retval >0: valid
*/
double screen_dpi_xft_cache = -DBL_MAX;
void screen_dpi_xft_cache_purge(void)
{
static double dpi = -1;
//Only run this once, we don't expect dpi changes during runtime
if (dpi <= -1) {
XrmInitialize();
char *xRMS = XResourceManagerString(xctx.dpy);
screen_dpi_xft_cache = -DBL_MAX;
}
if (!xRMS) {
dpi = 0;
return 0;
}
static double screen_dpi_get_from_xft(void)
{
if (screen_dpi_xft_cache == -DBL_MAX) {
screen_dpi_xft_cache = 0;
XrmDatabase xDB = XrmGetStringDatabase(xRMS);
char *xrmType;
XrmValue xrmValue;
if (XrmGetResource(xDB, "Xft.dpi", "Xft.dpi", &xrmType, &xrmValue)) {
dpi = strtod(xrmValue.addr, NULL);
} else {
dpi = 0;
}
XrmDestroyDatabase(xDB);
XrmDatabase db = XrmGetDatabase(xctx.dpy);
ASSERT_OR_RET(db, screen_dpi_xft_cache);
if (XrmGetResource(db, "Xft.dpi", "Xft.dpi", &xrmType, &xrmValue))
screen_dpi_xft_cache = strtod(xrmValue.addr, NULL);
}
return dpi;
return screen_dpi_xft_cache;
}
static double screen_dpi_get_from_monitor(struct screen_info *scr)
{
return (double)scr->h * 25.4 / (double)scr->mmh;
}
double screen_dpi_get(struct screen_info *scr)
{
if ( ! settings.force_xinerama
&& settings.per_monitor_dpi)
return screen_dpi_get_from_monitor(scr);
if (screen_dpi_get_from_xft() > 0)
return screen_dpi_get_from_xft();
// Calculate the DPI on the overall screen size.
// xrandr --dpi <DPI> does only change the overall screen's millimeters,
// but not the physical screen's sizes.
//
// The screen parameter is XDefaultScreen(), as our scr->id references
// the xrandr monitor and not the xrandr screen
return ((((double)DisplayWidth (xctx.dpy, XDefaultScreen(xctx.dpy))) * 25.4) /
((double)DisplayWidthMM(xctx.dpy, XDefaultScreen(xctx.dpy))));
}
void init_screens(void)
{
if (!settings.force_xinerama) {
if (settings.force_xinerama) {
xinerama_update();
} else {
randr_init();
randr_update();
} else {
xinerama_update();
}
}
void alloc_screen_ar(int n)
{
assert(n > 0);
if (n <= screens_len) return;
screens = g_realloc(screens, n * sizeof(struct screen_info));
memset(screens, 0, n * sizeof(struct screen_info));
g_free(screens);
screens = g_malloc0(n * sizeof(struct screen_info));
screens_len = n;
}
void randr_init(void)
{
int randr_error_base = 0;
if (!XRRQueryExtension(xctx.dpy, &randr_event_base, &randr_error_base)) {
int ignored;
if (!XRRQueryExtension(xctx.dpy, &ignored, &ignored)) {
LOG_W("Could not initialize the RandR extension. "
"Falling back to single monitor mode.");
return;
@ -138,20 +161,15 @@ void randr_update(void)
XRRFreeMonitors(m);
}
static int autodetect_dpi(struct screen_info *scr)
bool screen_check_event(XEvent *ev)
{
return (double)scr->h * 25.4 / (double)scr->mmh;
}
void screen_check_event(XEvent event)
{
if (event.type == randr_event_base + RRScreenChangeNotify) {
if (XRRUpdateConfiguration(ev)) {
LOG_D("XEvent: processing 'RRScreenChangeNotify'");
randr_update();
} else {
LOG_D("XEvent: Ignoring '%d'", event.type);
return true;
}
return false;
}
void xinerama_update(void)
@ -355,18 +373,6 @@ sc_cleanup:
return &screens[ret];
}
double get_dpi_for_screen(struct screen_info *scr)
{
double dpi = 0;
if ((!settings.force_xinerama && settings.per_monitor_dpi &&
(dpi = autodetect_dpi(scr))))
return dpi;
else if ((dpi = get_xft_dpi_value()))
return dpi;
else
return 96;
}
/*
* Return the window that currently has
* the keyboard focus.

View File

@ -17,10 +17,11 @@ struct screen_info {
};
void init_screens(void);
void screen_check_event(XEvent event);
void screen_dpi_xft_cache_purge(void);
bool screen_check_event(XEvent *ev);
struct screen_info *get_active_screen(void);
double get_dpi_for_screen(struct screen_info *scr);
double screen_dpi_get(struct screen_info *scr);
/**
* Find the currently focused window and check if it's in

View File

@ -17,6 +17,7 @@
#include <X11/X.h>
#include <X11/XKBlib.h>
#include <X11/Xlib.h>
#include <X11/Xresource.h>
#include <X11/Xutil.h>
#include "../dbus.h"
@ -55,6 +56,8 @@ bool dunst_grab_errored = false;
static bool fullscreen_last = false;
static void XRM_update_db(void);
static void x_shortcut_init(struct keyboard_shortcut *ks);
static int x_shortcut_grab(struct keyboard_shortcut *ks);
static void x_shortcut_ungrab(struct keyboard_shortcut *ks);
@ -333,9 +336,18 @@ gboolean x_mainloop_fd_dispatch(GSource *source, GSourceFunc callback, gpointer
ev.xcreatewindow.override_redirect == 0)
XRaiseWindow(xctx.dpy, win->xwin);
break;
case PropertyNotify:
if (ev.xproperty.atom == XA_RESOURCE_MANAGER) {
LOG_D("XEvent: processing PropertyNotify for Resource manager");
XRM_update_db();
screen_dpi_xft_cache_purge();
draw();
break;
}
/* Explicitly fallthrough. Other PropertyNotify events, e.g. catching
* _NET_WM get handled in the Focus(In|Out) section */
case FocusIn:
case FocusOut:
case PropertyNotify:
LOG_D("XEvent: Checking for active screen changes");
fullscreen_now = have_fullscreen_window();
scr = get_active_screen();
@ -355,7 +367,10 @@ gboolean x_mainloop_fd_dispatch(GSource *source, GSourceFunc callback, gpointer
}
break;
default:
screen_check_event(ev);
if (!screen_check_event(&ev)) {
LOG_D("XEvent: Ignoring '%d'", ev.type);
}
break;
}
}
@ -437,6 +452,47 @@ void x_free(void)
XCloseDisplay(xctx.dpy);
}
static int XErrorHandlerDB(Display *display, XErrorEvent *e)
{
char err_buf[BUFSIZ];
XGetErrorText(display, e->error_code, err_buf, BUFSIZ);
LOG_W("%s", err_buf);
return 0;
}
static void XRM_update_db(void)
{
XrmDatabase db;
XTextProperty prop;
Window root;
// We shouldn't destroy the first DB coming
// from the display object itself
static bool runonce = false;
XFlush(xctx.dpy);
XSetErrorHandler(XErrorHandlerDB);
root = RootWindow(xctx.dpy, DefaultScreen(xctx.dpy));
XLockDisplay(xctx.dpy);
if (XGetTextProperty(xctx.dpy, root, &prop, XA_RESOURCE_MANAGER)) {
if (runonce) {
db = XrmGetDatabase(xctx.dpy);
XrmDestroyDatabase(db);
}
db = XrmGetStringDatabase((const char*)prop.value);
XrmSetDatabase(xctx.dpy, db);
}
XUnlockDisplay(xctx.dpy);
runonce = true;
XFlush(xctx.dpy);
XSync(xctx.dpy, false);
XSetErrorHandler(NULL);
}
/*
* Setup X11 stuff
*/
@ -468,6 +524,8 @@ void x_setup(void)
init_screens();
x_shortcut_grab(&settings.history_ks);
XrmInitialize();
}
struct geometry x_parse_geometry(const char *geom_str)
@ -616,9 +674,17 @@ struct window_x11 *x_win_create(void)
win->esrc = x_win_reg_source(win);
long root_event_mask = SubstructureNotifyMask;
/* SubstructureNotifyMask is required for receiving CreateNotify events
* in order to raise the window when something covers us. See #160
*
* PropertyChangeMask is requred for getting screen change events when follow_mode != none
* and it's also needed to receive
* XA_RESOURCE_MANAGER events to update the dpi when
* the xresource value is updated
*/
long root_event_mask = SubstructureNotifyMask | PropertyChangeMask;
if (settings.f_mode != FOLLOW_NONE) {
root_event_mask |= FocusChangeMask | PropertyChangeMask;
root_event_mask |= FocusChangeMask;
}
XSelectInput(xctx.dpy, root, root_event_mask);