diff --git a/src/event.c b/src/event.c
index 9d71e6d..d12d76e 100644
--- a/src/event.c
+++ b/src/event.c
@@ -472,6 +472,7 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
return;
}
+ ps->pending_updates = true;
// If WM_STATE changes
if (ev->atom == ps->atoms->aWM_STATE) {
// Check whether it could be a client window
@@ -486,17 +487,18 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
// would be NULL.
if (w_top) {
win_set_flags(w_top, WIN_FLAGS_CLIENT_STALE);
- ps->pending_updates = true;
}
}
+ return;
}
// If _NET_WM_WINDOW_TYPE changes... God knows why this would happen, but
// there are always some stupid applications. (#144)
if (ev->atom == ps->atoms->a_NET_WM_WINDOW_TYPE) {
struct managed_win *w = NULL;
- if ((w = find_toplevel(ps, ev->window)))
- win_update_wintype(ps, w);
+ if ((w = find_toplevel(ps, ev->window))) {
+ win_set_property_stale(w, ev->atom);
+ }
}
if (ev->atom == ps->atoms->a_NET_WM_BYPASS_COMPOSITOR) {
@@ -507,29 +509,22 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
// If _NET_WM_OPACITY changes
if (ev->atom == ps->atoms->a_NET_WM_WINDOW_OPACITY) {
auto w = find_managed_win(ps, ev->window) ?: find_toplevel(ps, ev->window);
- if (w) {
- win_update_opacity_prop(ps, w);
- // we cannot receive OPACITY change when window is destroyed
- assert(w->state != WSTATE_DESTROYING);
- win_update_opacity_target(ps, w);
- }
+ win_set_property_stale(w, ps->atoms->a_NET_WM_WINDOW_OPACITY);
}
// If frame extents property changes
if (ps->o.frame_opacity > 0 && ev->atom == ps->atoms->a_NET_FRAME_EXTENTS) {
auto w = find_toplevel(ps, ev->window);
if (w) {
- win_update_frame_extents(ps, w, ev->window);
- // If frame extents change, the window needs repaint
- add_damage_from_win(ps, w);
+ win_set_property_stale(w, ev->atom);
}
}
// If name changes
if (ps->atoms->aWM_NAME == ev->atom || ps->atoms->a_NET_WM_NAME == ev->atom) {
auto w = find_toplevel(ps, ev->window);
- if (w && win_update_name(ps, w) == 1) {
- win_on_factor_change(ps, w);
+ if (w) {
+ win_set_property_stale(w, ev->atom);
}
}
@@ -537,16 +532,15 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
if (ps->atoms->aWM_CLASS == ev->atom) {
auto w = find_toplevel(ps, ev->window);
if (w) {
- win_get_class(ps, w);
- win_on_factor_change(ps, w);
+ win_set_property_stale(w, ev->atom);
}
}
// If role changes
if (ps->atoms->aWM_WINDOW_ROLE == ev->atom) {
auto w = find_toplevel(ps, ev->window);
- if (w && 1 == win_get_role(ps, w)) {
- win_on_factor_change(ps, w);
+ if (w) {
+ win_set_property_stale(w, ev->atom);
}
}
@@ -554,7 +548,7 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
if (ps->atoms->a_COMPTON_SHADOW == ev->atom) {
auto w = find_managed_win(ps, ev->window);
if (w) {
- win_update_prop_shadow(ps, w);
+ win_set_property_stale(w, ev->atom);
}
}
@@ -563,7 +557,7 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
(ps->o.detect_client_leader && ps->atoms->aWM_CLIENT_LEADER == ev->atom)) {
auto w = find_toplevel(ps, ev->window);
if (w) {
- win_update_leader(ps, w);
+ win_set_property_stale(w, ev->atom);
}
}
@@ -571,10 +565,12 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
for (latom_t *platom = ps->track_atom_lst; platom; platom = platom->next) {
if (platom->atom == ev->atom) {
auto w = find_managed_win(ps, ev->window);
- if (!w)
+ if (!w) {
w = find_toplevel(ps, ev->window);
- if (w)
- win_on_factor_change(ps, w);
+ }
+ if (w) {
+ win_set_property_stale(w, ev->atom);
+ }
break;
}
}
diff --git a/src/win.c b/src/win.c
index 471c093..15fb602 100644
--- a/src/win.c
+++ b/src/win.c
@@ -53,6 +53,31 @@ static const int WIN_GET_LEADER_MAX_RECURSION = 20;
static const int ROUNDED_PIXELS = 1;
static const double ROUNDED_PERCENT = 0.05;
+/**
+ * Retrieve the WM_CLASS of a window and update its
+ * win structure.
+ */
+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 int win_update_name(session_t *ps, struct managed_win *w);
+/**
+ * Reread opacity property of a window.
+ */
+static void win_update_opacity_prop(session_t *ps, struct managed_win *w);
+static void win_update_opacity_target(session_t *ps, struct managed_win *w);
+/**
+ * Retrieve frame extents from a window.
+ */
+static void
+win_update_frame_extents(session_t *ps, struct managed_win *w, xcb_window_t client);
+static void win_update_prop_shadow_raw(session_t *ps, struct managed_win *w);
+static void win_update_prop_shadow(session_t *ps, struct managed_win *w);
+/**
+ * Update leader of a window.
+ */
+static void win_update_leader(session_t *ps, struct managed_win *w);
+
/// Generate a "return by value" function, from a function that returns the
/// region via a region_t pointer argument.
/// Function signature has to be (win *, region_t *)
@@ -320,11 +345,81 @@ void win_release_images(struct backend_base *backend, struct managed_win *w) {
}
}
+/// Returns true if the `prop` property is stale, as well as clears the stale flag.
+static bool win_fetch_and_unset_property_stale(struct managed_win *w, xcb_atom_t prop);
+/// Returns true if any of the properties are stale, as well as clear all the stale flags.
+static bool win_check_and_clear_all_properties_stale(struct managed_win *w);
+
void win_process_update_flags(session_t *ps, struct managed_win *w) {
if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) {
map_win_start(ps, w);
win_clear_flags(w, WIN_FLAGS_MAPPED);
}
+
+ // Check client first, because later property updates need accurate client window
+ // information
+ if (win_check_flags_all(w, WIN_FLAGS_CLIENT_STALE)) {
+ win_recheck_client(ps, w);
+ win_clear_flags(w, WIN_FLAGS_CLIENT_STALE);
+ }
+
+ if (win_check_flags_all(w, WIN_FLAGS_PROPERTY_STALE)) {
+ bool factor_change = false;
+
+ if (win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_WM_WINDOW_TYPE)) {
+ win_update_wintype(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
+ assert(w->state != WSTATE_DESTROYING);
+ win_update_opacity_target(ps, w);
+ }
+
+ if (win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_FRAME_EXTENTS)) {
+ win_update_frame_extents(ps, w, w->client_win);
+ add_damage_from_win(ps, w);
+ }
+
+ if (win_fetch_and_unset_property_stale(w, ps->atoms->aWM_NAME) ||
+ win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_WM_NAME)) {
+ if (win_update_name(ps, w) == 1) {
+ factor_change = true;
+ }
+ }
+
+ if (win_fetch_and_unset_property_stale(w, ps->atoms->aWM_CLASS)) {
+ if (win_update_class(ps, w)) {
+ factor_change = true;
+ }
+ }
+
+ if (win_fetch_and_unset_property_stale(w, ps->atoms->aWM_WINDOW_ROLE)) {
+ if (win_update_role(ps, w) == 1) {
+ factor_change = true;
+ }
+ }
+
+ if (win_fetch_and_unset_property_stale(w, ps->atoms->a_COMPTON_SHADOW)) {
+ win_update_prop_shadow(ps, w);
+ }
+
+ if (win_fetch_and_unset_property_stale(w, ps->atoms->aWM_CLIENT_LEADER) ||
+ win_fetch_and_unset_property_stale(w, ps->atoms->aWM_TRANSIENT_FOR)) {
+ win_update_leader(ps, w);
+ }
+
+ if (win_check_and_clear_all_properties_stale(w)) {
+ // Some other flags we didn't explicitly check has changed, must
+ // have been a tracked atom for the custom rules
+ factor_change = true;
+ }
+
+ if (factor_change) {
+ win_on_factor_change(ps, w);
+ }
+ }
}
void win_process_image_flags(session_t *ps, struct managed_win *w) {
@@ -374,11 +469,6 @@ void win_process_image_flags(session_t *ps, struct managed_win *w) {
if (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE)) {
win_clear_flags(w, WIN_FLAGS_IMAGES_STALE);
}
-
- if (win_check_flags_all(w, WIN_FLAGS_CLIENT_STALE)) {
- win_recheck_client(ps, w);
- win_clear_flags(w, WIN_FLAGS_CLIENT_STALE);
- }
}
/**
@@ -458,7 +548,7 @@ int win_update_name(session_t *ps, struct managed_win *w) {
return ret;
}
-int win_get_role(session_t *ps, struct managed_win *w) {
+static int win_update_role(session_t *ps, struct managed_win *w) {
char **strlst = NULL;
int nstr = 0;
@@ -788,8 +878,9 @@ void win_update_prop_shadow(session_t *ps, struct managed_win *w) {
win_update_prop_shadow_raw(ps, w);
- if (w->prop_shadow != attr_shadow_old)
+ if (w->prop_shadow != attr_shadow_old) {
win_determine_shadow(ps, w);
+ }
}
static void win_set_invert_color(session_t *ps, struct managed_win *w, bool invert_color_new) {
@@ -1020,8 +1111,8 @@ void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client)
// Get window name and class if we are tracking them
win_update_name(ps, w);
- win_get_class(ps, w);
- win_get_role(ps, w);
+ win_update_class(ps, w);
+ win_update_role(ps, w);
// Update everything related to conditions
win_on_factor_change(ps, w);
@@ -1142,6 +1233,10 @@ void free_win_res(session_t *ps, struct managed_win *w) {
free(w->class_instance);
free(w->class_general);
free(w->role);
+
+ free(w->stale_props);
+ w->stale_props = NULL;
+ w->stale_props_capacity = 0;
}
/// Insert a new window after list_node `prev`
@@ -1212,6 +1307,8 @@ struct win *fill_win(session_t *ps, struct win *w) {
.reg_ignore_valid = false, // set to true when damaged
.flags = WIN_FLAGS_IMAGES_NONE, // updated by property/attributes/etc
// change
+ .stale_props = NULL,
+ .stale_props_capacity = 0,
// Runtime variables, updated by dbus
.fade_force = UNSET,
@@ -1431,7 +1528,7 @@ static xcb_window_t win_get_leader_raw(session_t *ps, struct managed_win *w, int
* Retrieve the WM_CLASS of a window and update its
* win structure.
*/
-bool win_get_class(session_t *ps, struct managed_win *w) {
+bool win_update_class(session_t *ps, struct managed_win *w) {
char **strlst = NULL;
int nstr = 0;
@@ -2374,6 +2471,44 @@ void win_clear_flags(struct managed_win *w, uint64_t flags) {
w->flags = w->flags & (~flags);
}
+void win_set_property_stale(struct managed_win *w, xcb_atom_t prop) {
+ const auto bits_per_element = sizeof(*w->stale_props) * 8;
+ if (prop >= w->stale_props_capacity * bits_per_element) {
+ const auto new_size = prop / bits_per_element + 1;
+ w->stale_props = realloc(w->stale_props, new_size * sizeof(*w->stale_props));
+
+ // Clear the content of the newly allocated bytes
+ memset(w->stale_props + w->stale_props_capacity, 0,
+ (new_size - w->stale_props_capacity) * sizeof(*w->stale_props));
+ w->stale_props_capacity = new_size;
+ }
+
+ w->stale_props[prop / bits_per_element] |= 1UL << (prop % bits_per_element);
+ win_set_flags(w, WIN_FLAGS_PROPERTY_STALE);
+}
+
+static bool win_check_and_clear_all_properties_stale(struct managed_win *w) {
+ bool ret = false;
+ for (size_t i = 0; i < w->stale_props_capacity; i++) {
+ ret = ret || (w->stale_props[i] != 0);
+ w->stale_props[i] = 0;
+ }
+ win_clear_flags(w, WIN_FLAGS_PROPERTY_STALE);
+ return ret;
+}
+
+static bool win_fetch_and_unset_property_stale(struct managed_win *w, xcb_atom_t prop) {
+ const auto bits_per_element = sizeof(*w->stale_props) * 8;
+ if (prop >= w->stale_props_capacity * bits_per_element) {
+ return false;
+ }
+
+ const auto mask = 1UL << (prop % bits_per_element);
+ bool ret = w->stale_props[prop / bits_per_element] & mask;
+ w->stale_props[prop / bits_per_element] &= ~mask;
+ return ret;
+}
+
bool win_check_flags_any(struct managed_win *w, uint64_t flags) {
return (w->flags & flags) != 0;
}
diff --git a/src/win.h b/src/win.h
index 2171ad1..0b70c4c 100644
--- a/src/win.h
+++ b/src/win.h
@@ -126,6 +126,10 @@ struct managed_win {
xcb_damage_damage_t damage;
/// Paint info of the window.
paint_t paint;
+ /// bitmap for properties which needs to be updated
+ uint64_t *stale_props;
+ /// number of uint64_ts that has been allocated for stale_props
+ uint64_t stale_props_capacity;
/// Bounding shape of the window. In local coordinates.
/// See above about coordinate systems.
@@ -277,8 +281,6 @@ bool must_use destroy_win_start(session_t *ps, struct win *w);
/// Release images bound with a window, set the *_NONE flags on the window. Only to be
/// used when de-initializing the backend outside of win.c
void win_release_images(struct backend_base *base, struct managed_win *w);
-int win_update_name(session_t *ps, struct managed_win *w);
-int win_get_role(session_t *ps, struct managed_win *w);
winmode_t attr_pure win_calc_mode(const struct managed_win *w);
void win_set_shadow_force(session_t *ps, struct managed_win *w, switch_t val);
void win_set_fade_force(struct managed_win *w, switch_t val);
@@ -289,19 +291,13 @@ void win_set_invert_color_force(session_t *ps, struct managed_win *w, switch_t v
*/
void win_set_focused(session_t *ps, struct managed_win *w);
bool attr_pure win_should_fade(session_t *ps, const struct managed_win *w);
-void win_update_prop_shadow_raw(session_t *ps, struct managed_win *w);
-void win_update_prop_shadow(session_t *ps, struct managed_win *w);
-void win_update_opacity_target(session_t *ps, struct managed_win *w);
void win_on_factor_change(session_t *ps, struct managed_win *w);
/**
* Update cache data in struct _win that depends on window size.
*/
void win_on_win_size_change(session_t *ps, struct managed_win *w);
-void win_update_wintype(session_t *ps, struct managed_win *w);
-void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client);
void win_unmark_client(session_t *ps, struct managed_win *w);
void win_recheck_client(session_t *ps, struct managed_win *w);
-bool win_get_class(session_t *ps, struct managed_win *w);
/**
* Calculate and return the opacity target of a window.
@@ -319,14 +315,6 @@ bool win_get_class(session_t *ps, struct managed_win *w);
double attr_pure win_calc_opacity_target(session_t *ps, const struct managed_win *w);
bool attr_pure win_should_dim(session_t *ps, const struct managed_win *w);
void win_update_screen(session_t *, struct managed_win *);
-/**
- * Reread opacity property of a window.
- */
-void win_update_opacity_prop(session_t *ps, struct managed_win *w);
-/**
- * Update leader of a window.
- */
-void win_update_leader(session_t *ps, struct managed_win *w);
/**
* Retrieve the bounding shape of a window.
*/
@@ -363,10 +351,6 @@ void win_get_region_noframe_local(const struct managed_win *w, region_t *);
void win_get_region_frame_local(const struct managed_win *w, region_t *res);
/// Get the region for the frame of the window, by value
region_t win_get_region_frame_local_by_val(const struct managed_win *w);
-/**
- * Retrieve frame extents from a window.
- */
-void win_update_frame_extents(session_t *ps, struct managed_win *w, xcb_window_t client);
/// Insert a new window above window with id `below`, if there is no window, add to top
/// New window will be in unmapped state
struct win *add_win_above(session_t *ps, xcb_window_t id, xcb_window_t below);
@@ -442,6 +426,8 @@ void win_clear_flags(struct managed_win *w, uint64_t flags);
bool win_check_flags_any(struct managed_win *w, uint64_t flags);
/// Returns true if all of the flags in `flags` are set
bool win_check_flags_all(struct managed_win *w, uint64_t flags);
+/// Mark a property as stale for a window
+void win_set_property_stale(struct managed_win *w, xcb_atom_t prop);
/// Free all resources in a struct win
void free_win_res(session_t *ps, struct managed_win *w);
diff --git a/src/win_defs.h b/src/win_defs.h
index 6099ac2..278ccc0 100644
--- a/src/win_defs.h
+++ b/src/win_defs.h
@@ -85,6 +85,8 @@ enum win_flags {
WIN_FLAGS_CLIENT_STALE = 32,
/// the window is mapped by X, we need to call map_win_start for it
WIN_FLAGS_MAPPED = 64,
+ /// this window has properties which needs to be updated
+ WIN_FLAGS_PROPERTY_STALE = 128,
};
static const uint64_t WIN_FLAGS_IMAGES_STALE =