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(); struct screen_info *screen = get_active_screen();
PangoContext *context = pango_cairo_create_context(c); 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); PangoLayout *layout = pango_layout_new(context);

View File

@ -25,8 +25,6 @@ int screens_len;
bool dunst_follow_errored = false; bool dunst_follow_errored = false;
int randr_event_base = 0;
static int randr_major_version = 0; static int randr_major_version = 0;
static int randr_minor_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 int FollowXErrorHandler(Display *display, XErrorEvent *e);
static Window get_focused_window(void); 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; screen_dpi_xft_cache = -DBL_MAX;
//Only run this once, we don't expect dpi changes during runtime }
if (dpi <= -1) {
XrmInitialize();
char *xRMS = XResourceManagerString(xctx.dpy);
if (!xRMS) { static double screen_dpi_get_from_xft(void)
dpi = 0; {
return 0; if (screen_dpi_xft_cache == -DBL_MAX) {
} screen_dpi_xft_cache = 0;
XrmDatabase xDB = XrmGetStringDatabase(xRMS);
char *xrmType; char *xrmType;
XrmValue xrmValue; XrmValue xrmValue;
XrmDatabase db = XrmGetDatabase(xctx.dpy);
if (XrmGetResource(xDB, "Xft.dpi", "Xft.dpi", &xrmType, &xrmValue)) { ASSERT_OR_RET(db, screen_dpi_xft_cache);
dpi = strtod(xrmValue.addr, NULL); if (XrmGetResource(db, "Xft.dpi", "Xft.dpi", &xrmType, &xrmValue))
} else { screen_dpi_xft_cache = strtod(xrmValue.addr, NULL);
dpi = 0;
}
XrmDestroyDatabase(xDB);
} }
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) void init_screens(void)
{ {
if (!settings.force_xinerama) { if (settings.force_xinerama) {
xinerama_update();
} else {
randr_init(); randr_init();
randr_update(); randr_update();
} else {
xinerama_update();
} }
} }
void alloc_screen_ar(int n) void alloc_screen_ar(int n)
{ {
assert(n > 0); assert(n > 0);
if (n <= screens_len) return; g_free(screens);
screens = g_malloc0(n * sizeof(struct screen_info));
screens = g_realloc(screens, n * sizeof(struct screen_info));
memset(screens, 0, n * sizeof(struct screen_info));
screens_len = n; screens_len = n;
} }
void randr_init(void) void randr_init(void)
{ {
int randr_error_base = 0; int ignored;
if (!XRRQueryExtension(xctx.dpy, &randr_event_base, &randr_error_base)) { if (!XRRQueryExtension(xctx.dpy, &ignored, &ignored)) {
LOG_W("Could not initialize the RandR extension. " LOG_W("Could not initialize the RandR extension. "
"Falling back to single monitor mode."); "Falling back to single monitor mode.");
return; return;
@ -138,20 +161,15 @@ void randr_update(void)
XRRFreeMonitors(m); 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; if (XRRUpdateConfiguration(ev)) {
}
void screen_check_event(XEvent event)
{
if (event.type == randr_event_base + RRScreenChangeNotify) {
LOG_D("XEvent: processing 'RRScreenChangeNotify'"); LOG_D("XEvent: processing 'RRScreenChangeNotify'");
randr_update(); randr_update();
} else { return true;
LOG_D("XEvent: Ignoring '%d'", event.type);
} }
return false;
} }
void xinerama_update(void) void xinerama_update(void)
@ -355,18 +373,6 @@ sc_cleanup:
return &screens[ret]; 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 * Return the window that currently has
* the keyboard focus. * the keyboard focus.

View File

@ -17,10 +17,11 @@ struct screen_info {
}; };
void init_screens(void); 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); 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 * Find the currently focused window and check if it's in

View File

@ -17,6 +17,7 @@
#include <X11/X.h> #include <X11/X.h>
#include <X11/XKBlib.h> #include <X11/XKBlib.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xresource.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include "../dbus.h" #include "../dbus.h"
@ -55,6 +56,8 @@ bool dunst_grab_errored = false;
static bool fullscreen_last = false; static bool fullscreen_last = false;
static void XRM_update_db(void);
static void x_shortcut_init(struct keyboard_shortcut *ks); static void x_shortcut_init(struct keyboard_shortcut *ks);
static int x_shortcut_grab(struct keyboard_shortcut *ks); static int x_shortcut_grab(struct keyboard_shortcut *ks);
static void x_shortcut_ungrab(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) ev.xcreatewindow.override_redirect == 0)
XRaiseWindow(xctx.dpy, win->xwin); XRaiseWindow(xctx.dpy, win->xwin);
break; 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 FocusIn:
case FocusOut: case FocusOut:
case PropertyNotify:
LOG_D("XEvent: Checking for active screen changes"); LOG_D("XEvent: Checking for active screen changes");
fullscreen_now = have_fullscreen_window(); fullscreen_now = have_fullscreen_window();
scr = get_active_screen(); scr = get_active_screen();
@ -355,7 +367,10 @@ gboolean x_mainloop_fd_dispatch(GSource *source, GSourceFunc callback, gpointer
} }
break; break;
default: default:
screen_check_event(ev); if (!screen_check_event(&ev)) {
LOG_D("XEvent: Ignoring '%d'", ev.type);
}
break; break;
} }
} }
@ -437,6 +452,47 @@ void x_free(void)
XCloseDisplay(xctx.dpy); 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 * Setup X11 stuff
*/ */
@ -468,6 +524,8 @@ void x_setup(void)
init_screens(); init_screens();
x_shortcut_grab(&settings.history_ks); x_shortcut_grab(&settings.history_ks);
XrmInitialize();
} }
struct geometry x_parse_geometry(const char *geom_str) 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); 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) { if (settings.f_mode != FOLLOW_NONE) {
root_event_mask |= FocusChangeMask | PropertyChangeMask; root_event_mask |= FocusChangeMask;
} }
XSelectInput(xctx.dpy, root, root_event_mask); XSelectInput(xctx.dpy, root, root_event_mask);