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 =