16#include <X11/extensions/randr.h>
17#include <X11/extensions/Xinerama.h>
18#include <X11/extensions/Xrandr.h>
22#include <X11/Xresource.h>
33bool dunst_follow_errored =
false;
35static int randr_major_version = 0;
36static int randr_minor_version = 0;
39void randr_update(
void);
40void xinerama_update(
void);
41void screen_update_fallback(
void);
42static void x_follow_setup_error_handler(
void);
43static int x_follow_tear_down_error_handler(
void);
44static int FollowXErrorHandler(Display *display, XErrorEvent *e);
45static Window get_focused_window(
void);
59void screen_dpi_xft_cache_purge(
void)
64static double screen_dpi_get_from_xft(
void)
71 XrmDatabase db = XrmGetDatabase(xctx.dpy);
73 if (XrmGetResource(db,
"Xft.dpi",
"Xft.dpi", &xrmType, &xrmValue))
79static double screen_dpi_get_from_monitor(
const struct screen_info *scr)
81 return (
double)scr->h * 25.4 / (double)scr->mmh;
88 return screen_dpi_get_from_monitor(scr);
90 if (screen_dpi_get_from_xft() > 0)
91 return screen_dpi_get_from_xft();
99 return ((((
double)DisplayWidth (xctx.dpy, XDefaultScreen(xctx.dpy))) * 25.4) /
100 ((
double)DisplayWidthMM(xctx.dpy, XDefaultScreen(xctx.dpy))));
103void init_screens(
void)
113void free_screen_ar(
struct screen_info *scr,
int len)
118 for (
int i = 0; i < len; ++i) {
124void alloc_screen_ar(
int n)
127 free_screen_ar(screens, screens_len);
128 screens = g_malloc0(n *
sizeof(
struct screen_info));
135 if (!XRRQueryExtension(xctx.dpy, &ignored, &ignored)) {
136 LOG_W(
"Could not initialize the RandR extension. "
137 "Falling back to single monitor mode.");
140 XRRQueryVersion(xctx.dpy, &randr_major_version, &randr_minor_version);
141 XRRSelectInput(xctx.dpy, RootWindow(xctx.dpy, DefaultScreen(xctx.dpy)), RRScreenChangeNotifyMask);
144void randr_update(
void)
146 if (randr_major_version < 1
147 || (randr_major_version == 1 && randr_minor_version < 5)) {
148 LOG_W(
"Server RandR version too low (%i.%i). "
149 "Falling back to single monitor mode.",
151 randr_minor_version);
152 screen_update_fallback();
157 XRRMonitorInfo *m = XRRGetMonitors(xctx.dpy, RootWindow(xctx.dpy, DefaultScreen(xctx.dpy)),
true, &n);
160 LOG_C(
"Get monitors reported %i monitors. "
161 "Falling back to single monitor mode.", n);
162 screen_update_fallback();
170 for (
int i = 0; i < n; i++) {
171 screens[i].name = XGetAtomName(xctx.dpy, m[i].name);
173 screens[i].x = m[i].x;
174 screens[i].y = m[i].y;
175 screens[i].w = m[i].width;
176 screens[i].h = m[i].height;
177 screens[i].mmh = m[i].mheight;
178 screens[i].dpi = screen_dpi_get(&screens[i]);
179 LOG_I(
"Detected screen %d (%s)", screens[i].
id, screens[i].name);
185bool screen_check_event(XEvent *ev)
187 if (XRRUpdateConfiguration(ev)) {
188 LOG_D(
"XEvent: processing 'RRScreenChangeNotify'");
196void xinerama_update(
void)
199 XineramaScreenInfo *info = XineramaQueryScreens(xctx.dpy, &n);
202 LOG_W(
"Could not get xinerama screen info. "
203 "Falling back to single monitor mode.");
204 screen_update_fallback();
210 for (
int i = 0; i < n; i++) {
212 screens[i].x = info[i].x_org;
213 screens[i].y = info[i].y_org;
214 screens[i].h = info[i].height;
215 screens[i].w = info[i].width;
216 LOG_I(
"Detected screen %d", screens[i].
id);
221void screen_update_fallback(
void)
229 LOG_D(
"Ignoring settings.monitor '%s' in fallback mode",
settings.monitor);
230 screen = DefaultScreen(xctx.dpy);
233 screens[0].w = DisplayWidth(xctx.dpy, screen);
234 screens[0].h = DisplayHeight(xctx.dpy, screen);
249 if (e->error_code == BadWindow) {
253 char err_buf[BUFSIZ];
254 XGetErrorText(display, e->error_code, err_buf, BUFSIZ);
255 fputs(err_buf, stderr);
268 Atom has_wm_state = XInternAtom(xctx.dpy,
"_NET_WM_STATE", True);
269 if (has_wm_state == None) {
276 Atom actual_type_return;
277 int actual_format_return;
278 unsigned long bytes_after_return;
279 unsigned char *prop_to_return;
280 unsigned long n_items;
281 int result = XGetWindowProperty(
290 &actual_format_return,
296 XSync(xctx.dpy,
false);
297 XSetErrorHandler(NULL);
299 if (result == Success) {
300 for(
size_t i = 0; i < n_items; i++) {
301 Atom atom = ((Atom*) prop_to_return)[i];
305 char *s = XGetAtomName(xctx.dpy, atom);
309 if (
STR_EQ(s,
"_NET_WM_STATE_FULLSCREEN"))
318 XFree(prop_to_return);
323static int get_screen_by_name(
const char *name,
int defval)
325 for (
int i = 0; i < screens_len; ++i) {
326 if (g_strcmp0(screens[i].name, name) == 0) {
330 LOG_D(
"Screen '%s' not found", name);
341 bool force_follow_mouse =
false;
343 if (
settings.f_mode == FOLLOW_NONE) {
347 ret = get_screen_by_name(
settings.monitor, 0);
352 assert(
settings.f_mode == FOLLOW_MOUSE
353 ||
settings.f_mode == FOLLOW_KEYBOARD);
355 x_follow_setup_error_handler();
358 RootWindow(xctx.dpy, DefaultScreen(xctx.dpy));
360 if (
settings.f_mode == FOLLOW_KEYBOARD) {
361 Window focused = get_focused_window();
370 force_follow_mouse =
true;
378 force_follow_mouse = !XTranslateCoordinates(
379 xctx.dpy, focused,root,
385 if (
settings.f_mode == FOLLOW_MOUSE || force_follow_mouse) {
387 unsigned int dummy_ui;
390 XQueryPointer(xctx.dpy,
401 for (
int i = 0; i < screens_len; i++) {
402 if (INRECT(x, y, screens[i].x, screens[i].y,
403 screens[i].w, screens[i].h)) {
416 x_follow_tear_down_error_handler();
418 assert(ret >= 0 && ret < screens_len);
419 return &screens[ret];
426static Window get_focused_window(
void)
428 Window focused, root = RootWindow(xctx.dpy, DefaultScreen(xctx.dpy));
431 XGetInputFocus(xctx.dpy, &focused, &ignored);
433 if (focused == None || focused == PointerRoot || focused == root)
438static void x_follow_setup_error_handler(
void)
440 dunst_follow_errored =
false;
443 XSetErrorHandler(FollowXErrorHandler);
446static int x_follow_tear_down_error_handler(
void)
449 XSync(xctx.dpy,
false);
450 XSetErrorHandler(NULL);
451 return dunst_follow_errored;
454static int FollowXErrorHandler(Display *display, XErrorEvent *e)
456 dunst_follow_errored =
true;
457 char err_buf[BUFSIZ];
458 XGetErrorText(display, e->error_code, err_buf, BUFSIZ);
459 LOG_W(
"%s", err_buf);
Logging subsystem and helpers.
double screen_dpi_xft_cache
A cache variable to cache the Xft.dpi xrdb values.
bool have_fullscreen_window(void)
Find the currently focused window and check if it's in fullscreen mode.
bool window_is_fullscreen(Window window)
Check if window is in fullscreen mode.
static int XErrorHandlerFullscreen(Display *display, XErrorEvent *e)
X11 ErrorHandler to mainly discard BadWindow parameter error.
Type definitions for settings.
String, time and other various helpers.
#define STR_EQ(a, b)
Test if string a and b contain the same chars.
#define ASSERT_OR_RET(expr, val)
Assert that expr evaluates to true, if not return val.