Skip to content

Commit

Permalink
win: cache _NET_WM_STATE property for each window
Browse files Browse the repository at this point in the history
Keep track of the `_NET_WM_STATE` property for each managed window. Use
the cached state to determine if window is fullscreen instead of
querying the Xserver every time.
  • Loading branch information
tryone144 committed Nov 29, 2020
1 parent 0e57e5e commit 9125693
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 43 deletions.
22 changes: 17 additions & 5 deletions src/atom.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

#include <xcb/xcb.h>

#include "meta.h"
#include "cache.h"
#include "meta.h"

// clang-format off
// Splitted into 2 lists because of the limitation of our macros
Expand All @@ -23,9 +23,7 @@
WM_CLIENT_MACHINE, \
_NET_ACTIVE_WINDOW, \
_COMPTON_SHADOW, \
_NET_WM_WINDOW_TYPE

#define ATOM_LIST2 \
_NET_WM_WINDOW_TYPE, \
_NET_WM_WINDOW_TYPE_DESKTOP, \
_NET_WM_WINDOW_TYPE_DOCK, \
_NET_WM_WINDOW_TYPE_TOOLBAR, \
Expand All @@ -39,9 +37,23 @@
_NET_WM_WINDOW_TYPE_TOOLTIP, \
_NET_WM_WINDOW_TYPE_NOTIFICATION, \
_NET_WM_WINDOW_TYPE_COMBO, \
_NET_WM_WINDOW_TYPE_DND, \
_NET_WM_WINDOW_TYPE_DND

#define ATOM_LIST2 \
_NET_WM_STATE, \
_NET_WM_STATE_MODAL, \
_NET_WM_STATE_STICKY, \
_NET_WM_STATE_MAXIMIZED_VERT, \
_NET_WM_STATE_MAXIMIZED_HORZ, \
_NET_WM_STATE_SHADED, \
_NET_WM_STATE_SKIP_TASKBAR, \
_NET_WM_STATE_SKIP_PAGER, \
_NET_WM_STATE_HIDDEN, \
_NET_WM_STATE_FULLSCREEN, \
_NET_WM_STATE_ABOVE, \
_NET_WM_STATE_BELOW, \
_NET_WM_STATE_DEMANDS_ATTENTION, \
_NET_WM_STATE_FOCUSED, \
_NET_WM_BYPASS_COMPOSITOR, \
UTF8_STRING, \
C_STRING
Expand Down
12 changes: 7 additions & 5 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@
#include <X11/Xlib.h>
#include <ev.h>
#include <pixman.h>
#include <xcb/xproto.h>
#include <xcb/render.h>
#include <xcb/sync.h>
#include <xcb/xproto.h>

#include "uthash_extra.h"
#ifdef CONFIG_OPENGL
Expand All @@ -55,11 +55,11 @@
#include "backend/driver.h"
#include "compiler.h"
#include "config.h"
#include "list.h"
#include "region.h"
#include "render.h"
#include "types.h"
#include "utils.h"
#include "list.h"
#include "render.h"
#include "win_defs.h"
#include "x.h"

Expand Down Expand Up @@ -254,11 +254,11 @@ typedef struct session {
// Cached blur convolution kernels.
struct x_convolution_kernel **blur_kerns_cache;
/// If we should quit
bool quit:1;
bool quit : 1;
// TODO(yshui) use separate flags for dfferent kinds of updates so we don't
// waste our time.
/// Whether there are pending updates, like window creation, etc.
bool pending_updates:1;
bool pending_updates : 1;

// === Expose event related ===
/// Pointer to an array of <code>XRectangle</code>-s of exposed region.
Expand Down Expand Up @@ -369,6 +369,8 @@ typedef struct session {
struct atom *atoms;
/// Array of atoms of all possible window types.
xcb_atom_t atoms_wintypes[NUM_WINTYPES];
/// Array of atoms of all possible window wm_states.
xcb_atom_t atoms_wmstates[NUM_WMSTATES];
/// Linked list of additional atoms to track.
latom_t *track_atom_lst;

Expand Down
8 changes: 8 additions & 0 deletions src/event.c
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,14 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
}
}

// If _NET_WM_STATE changes
if (ev->atom == ps->atoms->a_NET_WM_STATE) {
struct managed_win *w = NULL;
if ((w = find_toplevel(ps, ev->window))) {
win_set_property_stale(w, ev->atom);
}
}

if (ev->atom == ps->atoms->a_NET_WM_BYPASS_COMPOSITOR) {
// Unnecessay until we remove the queue_redraw in ev_handle
queue_redraw(ps);
Expand Down
18 changes: 18 additions & 0 deletions src/picom.c
Original file line number Diff line number Diff line change
Expand Up @@ -1687,6 +1687,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
.xrfilter_convolution_exists = false,

.atoms_wintypes = {0},
.atoms_wmstates = {0},
.track_atom_lst = NULL,

#ifdef CONFIG_DBUS
Expand Down Expand Up @@ -1877,6 +1878,23 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
SET_WM_TYPE_ATOM(COMBO);
SET_WM_TYPE_ATOM(DND);
#undef SET_WM_TYPE_ATOM
ps->atoms_wmstates[WMSTATE_UNKNOWN] = 0;
#define SET_WM_STATE_ATOM(x) \
ps->atoms_wmstates[WMSTATE_##x] = ps->atoms->a_NET_WM_STATE_##x
SET_WM_STATE_ATOM(MODAL);
SET_WM_STATE_ATOM(STICKY);
SET_WM_STATE_ATOM(MAXIMIZED_VERT);
SET_WM_STATE_ATOM(MAXIMIZED_HORZ);
SET_WM_STATE_ATOM(SHADED);
SET_WM_STATE_ATOM(SKIP_TASKBAR);
SET_WM_STATE_ATOM(SKIP_PAGER);
SET_WM_STATE_ATOM(HIDDEN);
SET_WM_STATE_ATOM(FULLSCREEN);
SET_WM_STATE_ATOM(ABOVE);
SET_WM_STATE_ATOM(BELOW);
SET_WM_STATE_ATOM(DEMANDS_ATTENTION);
SET_WM_STATE_ATOM(FOCUSED);
#undef SET_WM_STATE_ATOM

// Get needed atoms for c2 condition lists
if (!(c2_list_postprocess(ps, ps->o.unredir_if_possible_blacklist) &&
Expand Down
76 changes: 46 additions & 30 deletions src/win.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ static const double ROUNDED_PERCENT = 0.05;
static bool win_update_class(session_t *ps, struct managed_win *w);
static int win_update_role(session_t *ps, struct managed_win *w);
static void win_update_wintype(session_t *ps, struct managed_win *w);
static void win_update_wm_state(session_t *ps, struct managed_win *w);
static int win_update_name(session_t *ps, struct managed_win *w);
/**
* Reread opacity property of a window.
Expand Down Expand Up @@ -362,6 +363,10 @@ static void win_update_properties(session_t *ps, struct managed_win *w) {
win_update_wintype(ps, w);
}

if (win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_WM_STATE)) {
win_update_wm_state(ps, w);
}

if (win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_WM_WINDOW_OPACITY)) {
win_update_opacity_prop(ps, w);
// we cannot receive OPACITY change when window has been destroyed
Expand Down Expand Up @@ -643,6 +648,23 @@ static wintype_t wid_get_prop_wintype(session_t *ps, xcb_window_t wid) {
return WINTYPE_UNKNOWN;
}

static win_wmstate_t wid_get_prop_wm_state(session_t *ps, xcb_window_t wid) {
winprop_t prop =
x_get_prop(ps, wid, ps->atoms->a_NET_WM_STATE, 32L, XCB_ATOM_ATOM, 32);

win_wmstate_t wm_state = WMSTATE_UNKNOWN;
for (unsigned i = 0; i < prop.nitems; ++i) {
for (enum wm_state j = 1; j < NUM_WMSTATES; ++j) {
if (ps->atoms_wmstates[j] == (xcb_atom_t)prop.p32[i]) {
wm_state |= (1 << (j - 1));
}
}
}

free_winprop(&prop);
return wm_state;
}

static bool
wid_get_opacity_prop(session_t *ps, xcb_window_t wid, opacity_t def, opacity_t *out) {
bool ret = false;
Expand Down Expand Up @@ -1117,6 +1139,27 @@ void win_update_wintype(session_t *ps, struct managed_win *w) {
}
}

/**
* Update window wm_state.
*/
void win_update_wm_state(session_t *ps, struct managed_win *w) {
const win_wmstate_t wmstate_old = w->wm_state;

// Detect window wm_state here
w->wm_state = wid_get_prop_wm_state(ps, w->client_win);

if (w->wm_state != wmstate_old) {
win_on_factor_change(ps, w);
}
}

bool win_check_wm_state(const struct managed_win *w, enum wm_state state) {
if (state == WMSTATE_UNKNOWN) {
return false;
}
return (w->wm_state & (1 << (state - 1))) != 0;
}

/**
* Mark a window as the client window of another.
*
Expand All @@ -1143,6 +1186,7 @@ void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client)
}

win_update_wintype(ps, w);
win_update_wm_state(ps, w);

// Get frame widths. The window is in damaged area already.
win_update_frame_extents(ps, w, client);
Expand Down Expand Up @@ -1388,6 +1432,7 @@ struct win *fill_win(session_t *ps, struct win *w) {
.leader = XCB_NONE,
.cache_leader = XCB_NONE,
.window_type = WINTYPE_UNKNOWN,
.wm_state = WMSTATE_UNKNOWN,
.wmwin = false,
.focused = false,
.opacity = 0,
Expand Down Expand Up @@ -2478,34 +2523,6 @@ static inline bool rect_is_fullscreen(const session_t *ps, int x, int y, int wid
return (x <= 0 && y <= 0 && (x + wid) >= ps->root_width && (y + hei) >= ps->root_height);
}

/**
* Check if a window is fulscreen using EWMH
*
* TODO(yshui) cache this property
*/
static inline bool
win_is_fullscreen_xcb(xcb_connection_t *c, const struct atom *a, const xcb_window_t w) {
xcb_get_property_cookie_t prop =
xcb_get_property(c, 0, w, a->a_NET_WM_STATE, XCB_ATOM_ATOM, 0, 12);
xcb_get_property_reply_t *reply = xcb_get_property_reply(c, prop, NULL);
if (!reply) {
return false;
}

if (reply->length) {
xcb_atom_t *val = xcb_get_property_value(reply);
for (uint32_t i = 0; i < reply->length; i++) {
if (val[i] != a->a_NET_WM_STATE_FULLSCREEN) {
continue;
}
free(reply);
return true;
}
}
free(reply);
return false;
}

/// Set flags on a window. Some sanity checks are performed
void win_set_flags(struct managed_win *w, uint64_t flags) {
log_debug("Set flags %" PRIu64 " to window %#010x (%s)", flags, w->base.id, w->name);
Expand Down Expand Up @@ -2591,8 +2608,7 @@ bool win_check_flags_all(struct managed_win *w, uint64_t flags) {
* It's not using w->border_size for performance measures.
*/
bool win_is_fullscreen(const session_t *ps, const struct managed_win *w) {
if (!ps->o.no_ewmh_fullscreen &&
win_is_fullscreen_xcb(ps->c, ps->atoms, w->client_win)) {
if (!ps->o.no_ewmh_fullscreen && win_check_wm_state(w, WMSTATE_FULLSCREEN)) {
return true;
}
return rect_is_fullscreen(ps, w->g.x, w->g.y, w->widthb, w->heightb) &&
Expand Down
2 changes: 2 additions & 0 deletions src/win.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ struct managed_win {
xcb_window_t client_win;
/// Type of the window.
wintype_t window_type;
/// State of the window as described by the window manager in _NET_WM_STATE.
win_wmstate_t wm_state;
/// Whether it looks like a WM window. We consider a window WM window if
/// it does not have a decedent with WM_STATE and it is not override-
/// redirected itself.
Expand Down
24 changes: 21 additions & 3 deletions src/win_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include <stdint.h>

typedef enum {
WINTYPE_UNKNOWN,
WINTYPE_UNKNOWN = 0,
WINTYPE_DESKTOP,
WINTYPE_DOCK,
WINTYPE_TOOLBAR,
Expand All @@ -20,6 +20,25 @@ typedef enum {
NUM_WINTYPES
} wintype_t;

enum wm_state {
WMSTATE_UNKNOWN = 0,
WMSTATE_MODAL,
WMSTATE_STICKY,
WMSTATE_MAXIMIZED_VERT,
WMSTATE_MAXIMIZED_HORZ,
WMSTATE_SHADED,
WMSTATE_SKIP_TASKBAR,
WMSTATE_SKIP_PAGER,
WMSTATE_HIDDEN,
WMSTATE_FULLSCREEN,
WMSTATE_ABOVE,
WMSTATE_BELOW,
WMSTATE_DEMANDS_ATTENTION,
WMSTATE_FOCUSED,
NUM_WMSTATES,
};
typedef uint64_t win_wmstate_t;

/// Enumeration type of window painting mode.
typedef enum {
WMODE_TRANS, // The window body is (potentially) transparent
Expand Down Expand Up @@ -96,7 +115,6 @@ enum win_flags {
WIN_FLAGS_FACTOR_CHANGED = 1024,
};

static const uint64_t WIN_FLAGS_IMAGES_STALE =
WIN_FLAGS_PIXMAP_STALE | WIN_FLAGS_SHADOW_STALE;
static const uint64_t WIN_FLAGS_IMAGES_STALE = WIN_FLAGS_PIXMAP_STALE | WIN_FLAGS_SHADOW_STALE;

#define WIN_FLAGS_IMAGES_NONE (WIN_FLAGS_PIXMAP_NONE | WIN_FLAGS_SHADOW_NONE)

0 comments on commit 9125693

Please sign in to comment.