Refactor monitor handling to keep track of all screens
Previously, we were getting screen info every time we tried to move the window. To improve the situation, information about the available screens is initialised once when dunst starts and further behaviour depends on the compile-time options used. If Xrandr is enabled, screen information is updated when an XRRScreenChangeNotify event is received, meaning only when the screen layout changes. If Xinerama is enabled, screen information is only updated on startup. This behaviour might be changed later. If none are enabled, then dunst assumes only one screen and ignores all multi-monitor options.
This commit is contained in:
parent
0a6b105f4a
commit
52600cdfb0
337
src/x11/screen.c
337
src/x11/screen.c
@ -5,24 +5,28 @@
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xresource.h>
|
||||
#ifdef XRANDR
|
||||
#include <X11/extensions/randr.h>
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#include <assert.h>
|
||||
#elif XINERAMA
|
||||
#include <X11/extensions/Xinerama.h>
|
||||
#include <assert.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <glib.h>
|
||||
#include <locale.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#include <pango/pango-types.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "src/settings.h"
|
||||
#include "x.h"
|
||||
|
||||
screen_info *screens;
|
||||
int screens_len;
|
||||
|
||||
bool dunst_follow_errored = false;
|
||||
|
||||
void x_update_screens_fallback();
|
||||
static void x_follow_setup_error_handler(void);
|
||||
static int x_follow_tear_down_error_handler(void);
|
||||
static int FollowXErrorHandler(Display *display, XErrorEvent *e);
|
||||
@ -30,193 +34,231 @@ static Window get_focused_window(void);
|
||||
|
||||
static double get_xft_dpi_value()
|
||||
{
|
||||
double dpi = 0.0;
|
||||
XrmInitialize();
|
||||
char * xRMS = XResourceManagerString(xctx.dpy);
|
||||
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);
|
||||
|
||||
if (xRMS == NULL) {
|
||||
dpi = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( xRMS != NULL ) {
|
||||
XrmDatabase xDB = XrmGetStringDatabase(xRMS);
|
||||
char * xrmType;
|
||||
char *xrmType;
|
||||
XrmValue xrmValue;
|
||||
|
||||
if ( XrmGetResource(xDB, "Xft.dpi", NULL, &xrmType, &xrmValue))
|
||||
if (XrmGetResource(xDB, "Xft.dpi", NULL, &xrmType, &xrmValue)) {
|
||||
dpi = strtod(xrmValue.addr, NULL);
|
||||
|
||||
} else {
|
||||
dpi = 0;
|
||||
}
|
||||
XrmDestroyDatabase(xDB);
|
||||
}
|
||||
|
||||
return dpi;
|
||||
}
|
||||
static void set_dpi_value(screen_info * scr, double dpi)
|
||||
|
||||
void alloc_screen_ar(int n)
|
||||
{
|
||||
if (dpi > 0.0) {
|
||||
PangoFontMap *font_map = pango_cairo_font_map_get_default();
|
||||
pango_cairo_font_map_set_resolution((PangoCairoFontMap *) font_map, dpi);
|
||||
}
|
||||
#ifdef XRANDR
|
||||
//fallback to auto-detect method
|
||||
else {
|
||||
dpi = (double)scr->dim.h * 25.4 / (double)scr->dim.mmh;
|
||||
PangoFontMap *font_map = pango_cairo_font_map_get_default();
|
||||
pango_cairo_font_map_set_resolution((PangoCairoFontMap *) font_map, dpi);
|
||||
}
|
||||
#endif
|
||||
assert(n > 0);
|
||||
if (n <= screens_len) return;
|
||||
|
||||
screens = g_realloc(screens, n * sizeof(screen_info));
|
||||
|
||||
memset(screens, 0, n * sizeof(screen_info));
|
||||
|
||||
screens_len = n;
|
||||
}
|
||||
|
||||
#ifdef XRANDR
|
||||
static int lookup_active_screen(XRRMonitorInfo *info, int n, int x, int y)
|
||||
int randr_event_base = 0;
|
||||
|
||||
void init_screens()
|
||||
{
|
||||
int ret = -1;
|
||||
int randr_error_base = 0;
|
||||
XRRQueryExtension(xctx.dpy, &randr_event_base, &randr_error_base);
|
||||
XRRSelectInput(xctx.dpy, RootWindow(xctx.dpy, DefaultScreen(xctx.dpy)), RRScreenChangeNotifyMask);
|
||||
x_update_screens();
|
||||
}
|
||||
|
||||
void x_update_screens()
|
||||
{
|
||||
int n;
|
||||
XRRMonitorInfo *m = XRRGetMonitors(xctx.dpy, RootWindow(xctx.dpy, DefaultScreen(xctx.dpy)), true, &n);
|
||||
|
||||
if (n == -1) {
|
||||
x_update_screens_fallback();
|
||||
return;
|
||||
}
|
||||
|
||||
alloc_screen_ar(n);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (INRECT(x, y, info[i].x, info[i].y,
|
||||
info[i].width, info[i].height)) {
|
||||
ret = i;
|
||||
}
|
||||
screens[i].dim.x = m[i].x;
|
||||
screens[i].dim.y = m[i].y;
|
||||
screens[i].dim.w = m[i].width;
|
||||
screens[i].dim.h = m[i].height;
|
||||
screens[i].dim.mmh = m[i].mheight;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
XRRFreeMonitors(m);
|
||||
}
|
||||
|
||||
static double autodetect_dpi(screen_info *scr)
|
||||
{
|
||||
return (double)scr->dim.h * 25.4 / (double)scr->dim.mmh;
|
||||
}
|
||||
|
||||
void screen_check_event(XEvent event)
|
||||
{
|
||||
if (event.type == randr_event_base + RRScreenChangeNotify)
|
||||
x_update_screens();
|
||||
}
|
||||
|
||||
#elif XINERAMA
|
||||
static int lookup_active_screen(XineramaScreenInfo * info, int n, int x, int y)
|
||||
|
||||
void init_screens()
|
||||
{
|
||||
int ret = -1;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (INRECT(x, y, info[i].x_org, info[i].y_org,
|
||||
info[i].width, info[i].height)) {
|
||||
ret = i;
|
||||
}
|
||||
x_update_screens();
|
||||
}
|
||||
|
||||
void x_update_screens()
|
||||
{
|
||||
int n;
|
||||
XineramaScreenInfo *info = XineramaQueryScreens(xctx.dpy, &n);
|
||||
|
||||
if (!info) {
|
||||
x_update_screens_fallback();
|
||||
return;
|
||||
}
|
||||
|
||||
return ret;
|
||||
alloc_screen_ar(n);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
screens[i].dim.x = info[i].x_org;
|
||||
screens[i].dim.y = info[i].y_org;
|
||||
screens[i].dim.h = info[i].height;
|
||||
screens[i].dim.w = info[i].width;
|
||||
}
|
||||
XFree(info);
|
||||
}
|
||||
|
||||
void screen_check_event(XEvent event) {} //No-op
|
||||
|
||||
#define autodetect_dpi(x) 0
|
||||
|
||||
#else
|
||||
|
||||
void init_screens()
|
||||
{
|
||||
x_update_screens_fallback();
|
||||
}
|
||||
|
||||
void x_update_screens()
|
||||
{
|
||||
x_update_screens_fallback();
|
||||
}
|
||||
|
||||
void screen_check_event(XEvent event) {} //No-op
|
||||
|
||||
#define autodetect_dpi(x) 0
|
||||
|
||||
#endif
|
||||
|
||||
void x_update_screens_fallback()
|
||||
{
|
||||
alloc_screen_ar(1);
|
||||
|
||||
int screen;
|
||||
if (settings.monitor >= 0)
|
||||
screen = settings.monitor;
|
||||
else
|
||||
screen = DefaultScreen(xctx.dpy);
|
||||
|
||||
screens[0].dim.w = DisplayWidth(xctx.dpy, screen);
|
||||
screens[0].dim.h = DisplayHeight(xctx.dpy, screen);
|
||||
|
||||
}
|
||||
|
||||
#ifdef XRANDR
|
||||
/*
|
||||
* Select the screen on which the Window
|
||||
* should be displayed.
|
||||
*/
|
||||
static int select_screen(XRRMonitorInfo *info, int n)
|
||||
#elif XINERAMA
|
||||
static int select_screen(XineramaScreenInfo * info, int info_len)
|
||||
#endif
|
||||
#if defined(XRANDR) || defined(XINERAMA)
|
||||
screen_info *get_active_screen()
|
||||
{
|
||||
int ret = 0;
|
||||
x_follow_setup_error_handler();
|
||||
if (settings.f_mode == FOLLOW_NONE) {
|
||||
ret = settings.monitor >=
|
||||
0 ? settings.monitor : XDefaultScreen(xctx.dpy);
|
||||
goto sc_cleanup;
|
||||
int ret = 0;
|
||||
if (settings.monitor > 0 && settings.monitor < screens_len) {
|
||||
ret = settings.monitor;
|
||||
goto sc_cleanup;
|
||||
}
|
||||
|
||||
} else {
|
||||
int x, y;
|
||||
assert(settings.f_mode == FOLLOW_MOUSE
|
||||
|| settings.f_mode == FOLLOW_KEYBOARD);
|
||||
Window root =
|
||||
RootWindow(xctx.dpy, DefaultScreen(xctx.dpy));
|
||||
x_follow_setup_error_handler();
|
||||
|
||||
if (settings.f_mode == FOLLOW_MOUSE) {
|
||||
int dummy;
|
||||
unsigned int dummy_ui;
|
||||
Window dummy_win;
|
||||
if (settings.f_mode == FOLLOW_NONE) {
|
||||
ret = XDefaultScreen(xctx.dpy);
|
||||
goto sc_cleanup;
|
||||
|
||||
XQueryPointer(xctx.dpy, root, &dummy_win,
|
||||
&dummy_win, &x, &y, &dummy,
|
||||
&dummy, &dummy_ui);
|
||||
}
|
||||
} else {
|
||||
int x, y;
|
||||
assert(settings.f_mode == FOLLOW_MOUSE
|
||||
|| settings.f_mode == FOLLOW_KEYBOARD);
|
||||
Window root =
|
||||
RootWindow(xctx.dpy, DefaultScreen(xctx.dpy));
|
||||
|
||||
if (settings.f_mode == FOLLOW_KEYBOARD) {
|
||||
if (settings.f_mode == FOLLOW_MOUSE) {
|
||||
int dummy;
|
||||
unsigned int dummy_ui;
|
||||
Window dummy_win;
|
||||
|
||||
Window focused = get_focused_window();
|
||||
XQueryPointer(xctx.dpy, root, &dummy_win,
|
||||
&dummy_win, &x, &y, &dummy,
|
||||
&dummy, &dummy_ui);
|
||||
}
|
||||
|
||||
if (focused == 0) {
|
||||
/* something went wrong. Fallback to default */
|
||||
ret = settings.monitor >=
|
||||
0 ? settings.monitor : XDefaultScreen(xctx.dpy);
|
||||
goto sc_cleanup;
|
||||
}
|
||||
if (settings.f_mode == FOLLOW_KEYBOARD) {
|
||||
|
||||
Window child_return;
|
||||
XTranslateCoordinates(xctx.dpy, focused, root,
|
||||
0, 0, &x, &y, &child_return);
|
||||
}
|
||||
Window focused = get_focused_window();
|
||||
|
||||
ret = lookup_active_screen(info, n, x, y);
|
||||
if (focused == 0) {
|
||||
/* something went wrong. Fallback to default */
|
||||
ret = XDefaultScreen(xctx.dpy);
|
||||
goto sc_cleanup;
|
||||
}
|
||||
|
||||
if (ret > 0)
|
||||
Window child_return;
|
||||
XTranslateCoordinates(xctx.dpy, focused, root,
|
||||
0, 0, &x, &y, &child_return);
|
||||
}
|
||||
|
||||
for (int i = 0; i < screens_len; i++) {
|
||||
if (INRECT(x, y, screens[i].dim.x, screens[i].dim.y,
|
||||
screens[i].dim.w, screens[i].dim.h)) {
|
||||
ret = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret > 0)
|
||||
goto sc_cleanup;
|
||||
|
||||
/* something seems to be wrong. Fallback to default */
|
||||
ret = settings.monitor >=
|
||||
0 ? settings.monitor : XDefaultScreen(xctx.dpy);
|
||||
goto sc_cleanup;
|
||||
}
|
||||
sc_cleanup:
|
||||
x_follow_tear_down_error_handler();
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
/* something seems to be wrong. Fallback to default */
|
||||
ret = XDefaultScreen(xctx.dpy);
|
||||
goto sc_cleanup;
|
||||
}
|
||||
sc_cleanup:
|
||||
x_follow_tear_down_error_handler();
|
||||
assert(screens);
|
||||
assert(ret >= 0 && ret < screens_len);
|
||||
return &screens[ret];
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the information about the monitor
|
||||
* geometry.
|
||||
*/
|
||||
void x_screen_info(screen_info * scr)
|
||||
double get_dpi_for_screen(screen_info *scr)
|
||||
{
|
||||
#ifdef XRANDR
|
||||
int n;
|
||||
XRRMonitorInfo *m;
|
||||
|
||||
m = XRRGetMonitors(xctx.dpy, RootWindow(xctx.dpy, DefaultScreen(xctx.dpy)), true, &n);
|
||||
int screen = select_screen(m, n);
|
||||
if (screen >= n) {
|
||||
/* invalid monitor, fallback to default */
|
||||
screen = 0;
|
||||
}
|
||||
|
||||
scr->dim.x = m[screen].x;
|
||||
scr->dim.y = m[screen].y;
|
||||
scr->dim.w = m[screen].width;
|
||||
scr->dim.h = m[screen].height;
|
||||
scr->dim.mmh = m[screen].mheight;
|
||||
XRRFreeMonitors(m);
|
||||
#elif XINERAMA
|
||||
int n;
|
||||
XineramaScreenInfo *info;
|
||||
if ((info = XineramaQueryScreens(xctx.dpy, &n))) {
|
||||
int screen = select_screen(info, n);
|
||||
if (screen >= n) {
|
||||
/* invalid monitor, fallback to default */
|
||||
screen = 0;
|
||||
}
|
||||
scr->dim.x = info[screen].x_org;
|
||||
scr->dim.y = info[screen].y_org;
|
||||
scr->dim.h = info[screen].height;
|
||||
scr->dim.w = info[screen].width;
|
||||
XFree(info);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
scr->dim.x = 0;
|
||||
scr->dim.y = 0;
|
||||
|
||||
int screen;
|
||||
if (settings.monitor >= 0)
|
||||
screen = settings.monitor;
|
||||
else
|
||||
screen = DefaultScreen(xctx.dpy);
|
||||
|
||||
scr->dim.w = DisplayWidth(xctx.dpy, screen);
|
||||
scr->dim.h = DisplayHeight(xctx.dpy, screen);
|
||||
}
|
||||
|
||||
//Update dpi
|
||||
double dpi = 0.0;
|
||||
|
||||
dpi = get_xft_dpi_value();
|
||||
set_dpi_value(scr, dpi);
|
||||
double dpi = 0;
|
||||
if ((dpi = get_xft_dpi_value()) || (dpi = autodetect_dpi(scr)))
|
||||
return dpi;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -271,3 +313,4 @@ static int FollowXErrorHandler(Display * display, XErrorEvent * e)
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
@ -2,6 +2,8 @@
|
||||
#ifndef DUNST_SCREEN_H
|
||||
#define DUNST_SCREEN_H
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh))
|
||||
|
||||
typedef struct _dimension_t {
|
||||
@ -19,6 +21,12 @@ typedef struct _screen_info {
|
||||
dimension_t dim;
|
||||
} screen_info;
|
||||
|
||||
void x_screen_info(screen_info * scr);
|
||||
void init_screens();
|
||||
void x_update_screens();
|
||||
void screen_check_event(XEvent event);
|
||||
|
||||
screen_info *get_active_screen();
|
||||
double get_dpi_for_screen(screen_info *scr);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
55
src/x11/x.c
55
src/x11/x.c
@ -6,12 +6,18 @@
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xresource.h>
|
||||
#include <cairo.h>
|
||||
#include <cairo-xlib.h>
|
||||
#include <gdk/gdk.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
#include <glib-object.h>
|
||||
#include <locale.h>
|
||||
#include <math.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#include <pango/pango-attributes.h>
|
||||
#include <pango/pango-font.h>
|
||||
#include <pango/pango-layout.h>
|
||||
#include <pango/pango-types.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -204,21 +210,20 @@ static dimension_t calculate_dimensions(GSList *layouts)
|
||||
dim.y = 0;
|
||||
dim.mask = xctx.geometry.mask;
|
||||
|
||||
screen_info scr;
|
||||
x_screen_info(&scr);
|
||||
screen_info *scr = get_active_screen();
|
||||
if (have_dynamic_width()) {
|
||||
/* dynamic width */
|
||||
dim.w = 0;
|
||||
} else if (xctx.geometry.mask & WidthValue) {
|
||||
/* fixed width */
|
||||
if (xctx.geometry.negative_width) {
|
||||
dim.w = scr.dim.w - xctx.geometry.w;
|
||||
dim.w = scr->dim.w - xctx.geometry.w;
|
||||
} else {
|
||||
dim.w = xctx.geometry.w;
|
||||
}
|
||||
} else {
|
||||
/* across the screen */
|
||||
dim.w = scr.dim.w;
|
||||
dim.w = scr->dim.w;
|
||||
}
|
||||
|
||||
dim.h += 2 * settings.frame_width;
|
||||
@ -244,9 +249,9 @@ static dimension_t calculate_dimensions(GSList *layouts)
|
||||
/* subtract height from the unwrapped text */
|
||||
dim.h -= h;
|
||||
|
||||
if (total_width > scr.dim.w) {
|
||||
if (total_width > scr->dim.w) {
|
||||
/* set width to screen width */
|
||||
dim.w = scr.dim.w - xctx.geometry.x * 2;
|
||||
dim.w = scr->dim.w - xctx.geometry.x * 2;
|
||||
} else if (have_dynamic_width() || (total_width < xctx.geometry.w && settings.shrink)) {
|
||||
/* set width to text width */
|
||||
dim.w = total_width + 2 * settings.frame_width;
|
||||
@ -378,10 +383,24 @@ static GdkPixbuf *get_pixbuf_from_raw_image(const RawImage *raw_image)
|
||||
return pixbuf;
|
||||
}
|
||||
|
||||
static PangoLayout *create_layout(cairo_t *c)
|
||||
{
|
||||
screen_info *screen = get_active_screen();
|
||||
|
||||
PangoContext *context = pango_cairo_create_context(c);
|
||||
pango_cairo_context_set_resolution(context, get_dpi_for_screen(screen));
|
||||
|
||||
PangoLayout *layout = pango_layout_new(context);
|
||||
|
||||
g_object_unref(context);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
static colored_layout *r_init_shared(cairo_t *c, notification *n)
|
||||
{
|
||||
colored_layout *cl = g_malloc(sizeof(colored_layout));
|
||||
cl->l = pango_cairo_create_layout(c);
|
||||
cl->l = create_layout(c);
|
||||
|
||||
if (!settings.word_wrap) {
|
||||
pango_layout_set_ellipsize(cl->l, PANGO_ELLIPSIZE_MIDDLE);
|
||||
@ -670,19 +689,18 @@ static void x_win_move(int width, int height)
|
||||
{
|
||||
|
||||
int x, y;
|
||||
screen_info scr;
|
||||
x_screen_info(&scr);
|
||||
screen_info *scr = get_active_screen();
|
||||
/* calculate window position */
|
||||
if (xctx.geometry.mask & XNegative) {
|
||||
x = (scr.dim.x + (scr.dim.w - width)) + xctx.geometry.x;
|
||||
x = (scr->dim.x + (scr->dim.w - width)) + xctx.geometry.x;
|
||||
} else {
|
||||
x = scr.dim.x + xctx.geometry.x;
|
||||
x = scr->dim.x + xctx.geometry.x;
|
||||
}
|
||||
|
||||
if (xctx.geometry.mask & YNegative) {
|
||||
y = scr.dim.y + (scr.dim.h + xctx.geometry.y) - height;
|
||||
y = scr->dim.y + (scr->dim.h + xctx.geometry.y) - height;
|
||||
} else {
|
||||
y = scr.dim.y + xctx.geometry.y;
|
||||
y = scr->dim.y + xctx.geometry.y;
|
||||
}
|
||||
|
||||
/* move and resize */
|
||||
@ -844,6 +862,9 @@ gboolean x_mainloop_fd_dispatch(GSource * source, GSourceFunc callback,
|
||||
case PropertyNotify:
|
||||
wake_up();
|
||||
break;
|
||||
default:
|
||||
screen_check_event(ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -964,6 +985,7 @@ void x_setup(void)
|
||||
|
||||
xctx.screensaver_info = XScreenSaverAllocInfo();
|
||||
|
||||
init_screens();
|
||||
x_win_setup();
|
||||
x_cairo_setup();
|
||||
x_shortcut_grab(&settings.history_ks);
|
||||
@ -1034,10 +1056,9 @@ static void x_win_setup(void)
|
||||
ExposureMask | KeyPressMask | VisibilityChangeMask |
|
||||
ButtonPressMask | FocusChangeMask| StructureNotifyMask;
|
||||
|
||||
screen_info scr;
|
||||
x_screen_info(&scr);
|
||||
screen_info *scr = get_active_screen();
|
||||
xctx.win =
|
||||
XCreateWindow(xctx.dpy, root, scr.dim.x, scr.dim.y, scr.dim.w,
|
||||
XCreateWindow(xctx.dpy, root, scr->dim.x, scr->dim.y, scr->dim.w,
|
||||
1, 0, DefaultDepth(xctx.dpy,
|
||||
DefaultScreen(xctx.dpy)),
|
||||
CopyFromParent, DefaultVisual(xctx.dpy,
|
||||
|
@ -2,6 +2,7 @@
|
||||
#ifndef DUNST_X_H
|
||||
#define DUNST_X_H
|
||||
|
||||
#include <X11/X.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/scrnsaver.h>
|
||||
#include <glib.h>
|
||||
|
Loading…
x
Reference in New Issue
Block a user