event: delayed handling of window property changes

Move handling window property changes into the X critical section.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2020-10-22 15:48:11 +01:00
parent 8f78b9b631
commit f3ff7eff8c
No known key found for this signature in database
GPG Key ID: 37C999F617EA1A47
4 changed files with 172 additions and 53 deletions

View File

@ -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;
}
}

155
src/win.c
View File

@ -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 <code>WM_CLASS</code> of a window and update its
* <code>win</code> 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 <code>WM_CLASS</code> of a window and update its
* <code>win</code> 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;
}

View File

@ -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);

View File

@ -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 =