From a826e0ce79c552e48257fedd2e35019a22551afd Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sat, 9 Feb 2019 15:50:40 +0000 Subject: [PATCH] Window state tracking refactor Window state tracking is basically the back bond of window fading. We add a new enum field to the win struct to track the state of the window, instead of using a set of boolean variables. We also remove the fading callbacks, since it is only used for fading, so the potential of code reuse is lost. And it makes the code slightly harder to understand. Also fixed a problem that --no-fading-openclose is not behaving as advertised (from my observation, enabling this flag disables fading entirely, instead of just diabling it for open/close). Also uses double for opacity everywhere internally. Use opacity_t only when setting X opacity prop. TODO: Remove win::*_last Signed-off-by: Yuxuan Shui --- src/backend/backend.c | 9 +- src/backend/xrender.c | 7 +- src/c2.c | 20 +- src/c2.h | 3 +- src/common.h | 12 +- src/compiler.h | 8 +- src/compton.c | 435 ++++++++----------------------------- src/compton.h | 10 - src/config.h | 11 +- src/config_libconfig.c | 8 +- src/dbus.c | 11 +- src/options.c | 8 +- src/render.c | 12 +- src/types.h | 2 +- src/win.c | 480 ++++++++++++++++++++++++++++++++--------- src/win.h | 92 +++++--- 16 files changed, 573 insertions(+), 555 deletions(-) diff --git a/src/backend/backend.c b/src/backend/backend.c index 852c336..5e166da 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -3,11 +3,11 @@ #include #include "backend.h" -#include "config.h" -#include "win.h" -#include "region.h" #include "common.h" #include "compiler.h" +#include "config.h" +#include "region.h" +#include "win.h" backend_info_t *backend_list[NUM_BKEND] = { [BKEND_XRENDER] = &xrender_backend, @@ -107,8 +107,7 @@ void paint_all_new(session_t *ps, win *const t, bool ignore_damage) { ®_noframe); pixman_region32_fini(®_noframe); } - bi->blur(ps->backend_data, ps, - (double)w->opacity / OPAQUE, ®_blur); + bi->blur(ps->backend_data, ps, w->opacity, ®_blur); pixman_region32_fini(®_blur); } diff --git a/src/backend/xrender.c b/src/backend/xrender.c index fb2b258..b03fc55 100644 --- a/src/backend/xrender.c +++ b/src/backend/xrender.c @@ -89,7 +89,7 @@ static void compose(void *backend_data, session_t *ps, win *w, void *win_data, i bool blend = default_is_frame_transparent(NULL, w, win_data) || default_is_win_transparent(NULL, w, win_data); int op = (blend ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC); - auto alpha_pict = xd->alpha_pict[(int)(((double)w->opacity / OPAQUE) * 255.0)]; + auto alpha_pict = xd->alpha_pict[(int)(w->opacity * 255.0)]; // XXX Move shadow drawing into a separate function, // also do shadow excluding outside of backend @@ -290,7 +290,6 @@ render_win(void *backend_data, session_t *ps, win *w, void *win_data, const regi 0, 0, 0, 0, w->widthb, w->heightb); } - const double dopacity = get_opacity_percent(w); if (w->frame_opacity != 1) { // Handle transparent frame // Step 1: clip paint area to frame @@ -303,7 +302,7 @@ render_win(void *backend_data, session_t *ps, win *w, void *win_data, const regi // Draw the frame with frame opacity xcb_render_picture_t alpha_pict = - xd->alpha_pict[(int)(w->frame_opacity * dopacity * 255)]; + xd->alpha_pict[(int)(w->frame_opacity * w->opacity * 255)]; x_set_picture_clip_region(ps->c, wd->rendered_pict, 0, 0, &frame_reg); // Step 2: multiply alpha value @@ -317,7 +316,7 @@ render_win(void *backend_data, session_t *ps, win *w, void *win_data, const regi double dim_opacity = ps->o.inactive_dim; if (!ps->o.inactive_dim_fixed) - dim_opacity *= get_opacity_percent(w); + dim_opacity *= w->opacity; xcb_render_color_t color = { .red = 0, .green = 0, .blue = 0, .alpha = 0xffff * dim_opacity}; diff --git a/src/c2.c b/src/c2.c index 7ae66c8..fb0f98e 100644 --- a/src/c2.c +++ b/src/c2.c @@ -353,7 +353,7 @@ static xcb_atom_t c2_get_atom_type(const c2_l_t *pleaf); static bool -c2_match_once(session_t *ps, win *w, const c2_ptr_t cond); +c2_match_once(session_t *ps, const win *w, const c2_ptr_t cond); /** * Parse a condition string. @@ -1365,7 +1365,7 @@ c2_get_atom_type(const c2_l_t *pleaf) { * For internal use. */ static inline void -c2_match_once_leaf(session_t *ps, win *w, const c2_l_t *pleaf, +c2_match_once_leaf(session_t *ps, const win *w, const c2_l_t *pleaf, bool *pres, bool *perr) { assert(pleaf); @@ -1567,7 +1567,7 @@ c2_match_once_leaf(session_t *ps, win *w, const c2_l_t *pleaf, * @return true if matched, false otherwise. */ static bool -c2_match_once(session_t *ps, win *w, const c2_ptr_t cond) { +c2_match_once(session_t *ps, const win *w, const c2_ptr_t cond) { bool result = false; bool error = true; @@ -1644,22 +1644,10 @@ c2_match_once(session_t *ps, win *w, const c2_ptr_t cond) { * @return true if matched, false otherwise. */ bool -c2_match(session_t *ps, win *w, const c2_lptr_t *condlst, - const c2_lptr_t **cache, void **pdata) { - assert(w->a.map_state == XCB_MAP_STATE_VIEWABLE); - - // Check if the cached entry matches firstly - if (cache && *cache && c2_match_once(ps, w, (*cache)->ptr)) { - if (pdata) - *pdata = (*cache)->data; - return true; - } - +c2_match(session_t *ps, const win *w, const c2_lptr_t *condlst, void **pdata) { // Then go through the whole linked list for (; condlst; condlst = condlst->next) { if (c2_match_once(ps, w, condlst->ptr)) { - if (cache) - *cache = condlst; if (pdata) *pdata = condlst->data; return true; diff --git a/src/c2.h b/src/c2.h index 370241d..21eb112 100644 --- a/src/c2.h +++ b/src/c2.h @@ -21,7 +21,6 @@ c2_lptr_t *c2_parse(c2_lptr_t **pcondlst, const char *pattern, void *data); c2_lptr_t *c2_free_lptr(c2_lptr_t *lp); -bool c2_match(session_t *ps, win *w, const c2_lptr_t *condlst, const c2_lptr_t **cache, - void **pdata); +bool c2_match(session_t *ps, const win *w, const c2_lptr_t *condlst, void **pdata); bool c2_list_postprocess(session_t *ps, c2_lptr_t *list); diff --git a/src/common.h b/src/common.h index 77a2dfd..e15c9a0 100644 --- a/src/common.h +++ b/src/common.h @@ -98,7 +98,6 @@ #define ROUNDED_PERCENT 0.05 #define ROUNDED_PIXELS 10 -#define OPAQUE 0xffffffff #define REGISTER_PROP "_NET_WM_CM_S" #define TIME_MS_MAX LONG_MAX @@ -687,11 +686,6 @@ timespec_subtract(struct timespec *result, return x->tv_sec < y->tv_sec; } -static inline double -get_opacity_percent(win *w) { - return ((double) w->opacity) / OPAQUE; -} - /** * Get current time in struct timeval. */ @@ -792,8 +786,9 @@ find_win(session_t *ps, xcb_window_t id) { win *w; for (w = ps->list; w; w = w->next) { - if (w->id == id && !w->destroying) + if (w->id == id && w->state != WSTATE_DESTROYING) { return w; + } } return 0; @@ -811,8 +806,9 @@ find_toplevel(session_t *ps, xcb_window_t id) { return NULL; for (win *w = ps->list; w; w = w->next) { - if (w->client_win == id && !w->destroying) + if (w->client_win == id && w->state != WSTATE_DESTROYING) { return w; + } } return NULL; diff --git a/src/compiler.h b/src/compiler.h index fe36ed3..b44562b 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -4,9 +4,11 @@ #include -#define auto __auto_type -#define likely(x) __builtin_expect(!!(x), 1) -#define unlikely(x) __builtin_expect(!!(x), 0) +#define auto __auto_type +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#define likely_if(x) if (likely(x)) +#define unlikely_if(x) if (unlikely(x)) #ifndef __has_attribute # if __GNUC__ >= 4 diff --git a/src/compton.c b/src/compton.c index a5fa007..c2b97ae 100644 --- a/src/compton.c +++ b/src/compton.c @@ -59,12 +59,6 @@ (session_t *)((char *)__mptr - offsetof(session_t, member)); \ }) -static void -finish_destroy_win(session_t *ps, win **_w); - -static void -configure_win(session_t *ps, xcb_configure_notify_event_t *ce); - static void update_refresh_rate(session_t *ps); @@ -91,9 +85,6 @@ redir_stop(session_t *ps); static win * recheck_focus(session_t *ps); -static double -get_opacity_percent(win *w); - static void restack_win(session_t *ps, win *w, xcb_window_t new_above); @@ -169,25 +160,6 @@ free_xinerama_info(session_t *ps) { #endif } -/** - * Destroy all resources in a struct _win. - */ -static inline void -free_win_res(session_t *ps, win *w) { - free_win_res_glx(ps, w); - free_paint(ps, &w->paint); - pixman_region32_fini(&w->bounding_shape); - free_paint(ps, &w->shadow_paint); - // BadDamage may be thrown if the window is destroyed - set_ignore_cookie(ps, - xcb_damage_destroy(ps->c, w->damage)); - rc_region_unref(&w->reg_ignore); - free(w->name); - free(w->class_instance); - free(w->class_general); - free(w->role); -} - /** * Get current system clock in milliseconds. */ @@ -200,35 +172,6 @@ get_time_ms(void) { return tv.tv_sec % SEC_WRAP * 1000 + tv.tv_usec / 1000; } -/** - * Get the Xinerama screen a window is on. - * - * Return an index >= 0, or -1 if not found. - * - * XXX move to x.c - */ -static inline void -cxinerama_win_upd_scr(session_t *ps, win *w) { -#ifdef CONFIG_XINERAMA - w->xinerama_scr = -1; - - if (!ps->xinerama_scrs) - return; - - xcb_xinerama_screen_info_t *scrs = xcb_xinerama_query_screens_screen_info(ps->xinerama_scrs); - int length = xcb_xinerama_query_screens_screen_info_length(ps->xinerama_scrs); - for (int i = 0; i < length; i++) { - xcb_xinerama_screen_info_t *s = &scrs[i]; - if (s->x_org <= w->g.x && s->y_org <= w->g.y - && s->x_org + s->width >= w->g.x + w->widthb - && s->y_org + s->height >= w->g.y + w->heightb) { - w->xinerama_scr = i; - return; - } - } -#endif -} - // XXX Move to x.c static void cxinerama_upd_scrs(session_t *ps) { @@ -336,25 +279,35 @@ fade_timeout(session_t *ps) { * @param steps steps of fading */ static void -run_fade(session_t *ps, win *w, unsigned steps) { - // If we have reached target opacity, return - if (w->opacity == w->opacity_tgt) { +run_fade(session_t *ps, win **_w, unsigned steps) { + win *w = *_w; + if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) { + // We are not fading + assert(w->opacity_tgt == w->opacity); return; } - if (!w->fade) + if (!win_should_fade(ps, w)) { + log_debug("Window %#010x %s doesn't need fading", w->id, w->name); w->opacity = w->opacity_tgt; - else if (steps) { - // Use double below because opacity_t will probably overflow during - // calculations - if (w->opacity < w->opacity_tgt) + } + if (w->opacity == w->opacity_tgt) { + // We have reached target opacity, wrapping up + log_debug("Fading finished for window %#010x %s", w->id, w->name); + win_check_fade_finished(ps, _w); + return; + } + + if (steps) { + if (w->opacity < w->opacity_tgt) { w->opacity = normalize_d_range( - (double) w->opacity + (double) ps->o.fade_in_step * steps, + w->opacity + ps->o.fade_in_step * steps, 0.0, w->opacity_tgt); - else + } else { w->opacity = normalize_d_range( - (double) w->opacity - (double) ps->o.fade_out_step * steps, - w->opacity_tgt, OPAQUE); + w->opacity - ps->o.fade_out_step * steps, + w->opacity_tgt, 1); + } } if (w->opacity != w->opacity_tgt) { @@ -539,40 +492,47 @@ paint_preprocess(session_t *ps, win *list) { const bool was_painted = w->to_paint; const opacity_t opacity_old = w->opacity; // Restore flags from last paint if the window is being faded out - if (w->a.map_state == XCB_MAP_STATE_UNMAPPED) { + // TODO probably should just stop updating window flags when window + // is fading out + if (w->state == WSTATE_UNMAPPING || w->state == WSTATE_DESTROYING) { win_set_shadow(ps, w, w->shadow_last); - w->fade = w->fade_last; win_set_invert_color(ps, w, w->invert_color_last); win_set_blur_background(ps, w, w->blur_background_last); } - // Update window opacity target and dim state if asked - if (WFLAG_OPCT_CHANGE & w->flags) { - win_calc_opacity(ps, w); - win_calc_dim(ps, w); + if (win_should_dim(ps, w) != w->dim) { + w->dim = win_should_dim(ps, w); + add_damage_from_win(ps, w); } // Run fading - run_fade(ps, w, steps); + run_fade(ps, &w, steps); + if (!w) { + // the window might have been destroyed because fading finished + continue; + } - if (win_has_frame(w)) + if (win_has_frame(w)) { w->frame_opacity = ps->o.frame_opacity; - else + } else { w->frame_opacity = 1.0; + } // Update window mode win_determine_mode(ps, w); // Destroy all reg_ignore above when frame opaque state changes on // SOLID mode - if (was_painted && w->mode != mode_old) + if (was_painted && w->mode != mode_old) { w->reg_ignore_valid = false; + } // Add window to damaged area if its opacity changes // If was_painted == false, and to_paint is also false, we don't care // If was_painted == false, but to_paint is true, damage will be added in the loop below - if (was_painted && w->opacity != opacity_old) + if (was_painted && w->opacity != opacity_old) { add_damage_from_win(ps, w); + } } // Opacity will not change, from now on. @@ -603,8 +563,8 @@ paint_preprocess(session_t *ps, win *list) { if (!w->ever_damaged || w->g.x + w->g.width < 1 || w->g.y + w->g.height < 1 || w->g.x >= ps->root_width || w->g.y >= ps->root_height - || ((w->a.map_state == XCB_MAP_STATE_UNMAPPED || w->destroying) && !w->paint.pixmap) - || (double) w->opacity / OPAQUE * MAX_ALPHA < 1 + || w->state == WSTATE_UNMAPPED + || (double) w->opacity * MAX_ALPHA < 1 || w->paint_excluded) to_paint = false; //log_trace("%s %d %d %d", w->name, to_paint, w->opacity, w->paint_excluded); @@ -621,7 +581,7 @@ paint_preprocess(session_t *ps, win *list) { goto skip_window; // Calculate shadow opacity - w->shadow_opacity = ps->o.shadow_opacity * get_opacity_percent(w) * ps->o.frame_opacity; + w->shadow_opacity = ps->o.shadow_opacity * w->opacity * ps->o.frame_opacity; // Generate ignore region for painting to reduce GPU load if (!w->reg_ignore) @@ -673,7 +633,6 @@ paint_preprocess(session_t *ps, win *list) { reg_ignore_valid = reg_ignore_valid && w->reg_ignore_valid; w->reg_ignore_valid = true; - assert(w->destroying == (w->fade_callback == finish_destroy_win)); win_check_fade_finished(ps, &w); // Avoid setting w->to_paint if w is freed @@ -683,7 +642,6 @@ paint_preprocess(session_t *ps, win *list) { if (w->to_paint) { // Save flags w->shadow_last = w->shadow; - w->fade_last = w->fade; w->invert_color_last = w->invert_color; w->blur_background_last = w->blur_background; } @@ -693,7 +651,7 @@ paint_preprocess(session_t *ps, win *list) { rc_region_unref(&last_reg_ignore); // If possible, unredirect all windows and stop painting - if (UNSET != ps->o.redirected_force) + if (ps->o.redirected_force != UNSET) unredir_possible = !ps->o.redirected_force; else if (ps->o.unredir_if_possible && is_highest && !ps->redirected) // If there's no window to paint, and the screen isn't redirected, @@ -791,170 +749,6 @@ repair_win(session_t *ps, win *w) { add_damage(ps, &parts); pixman_region32_fini(&parts); } - -static void -finish_map_win(session_t *ps, win **_w) { - win *w = *_w; - w->in_openclose = false; - if (ps->o.no_fading_openclose) { - win_determine_fade(ps, w); - } -} - -void -map_win(session_t *ps, xcb_window_t id) { - // Unmap overlay window if it got mapped but we are currently not - // in redirected state. - if (ps->overlay && id == ps->overlay && !ps->redirected) { - auto e = xcb_request_check(ps->c, xcb_unmap_window(ps->c, ps->overlay)); - if (e) { - log_error("Failed to unmap the overlay window"); - free(e); - } - // We don't track the overlay window, so we can return - return; - } - - win *w = find_win(ps, id); - - log_trace("(%#010x \"%s\"): %p", id, (w ? w->name: NULL), w); - - // Don't care about window mapping if it's an InputOnly window - // Try avoiding mapping a window twice - if (!w || InputOnly == w->a._class - || w->a.map_state == XCB_MAP_STATE_VIEWABLE) - return; - - assert(!win_is_focused_real(ps, w)); - - w->a.map_state = XCB_MAP_STATE_VIEWABLE; - - cxinerama_win_upd_scr(ps, w); - - // Set window event mask before reading properties so that no property - // changes are lost - xcb_change_window_attributes(ps->c, id, XCB_CW_EVENT_MASK, - (const uint32_t[]) { determine_evmask(ps, id, WIN_EVMODE_FRAME) }); - - // Notify compton when the shape of a window changes - if (ps->shape_exists) { - xcb_shape_select_input(ps->c, id, 1); - } - - // Make sure the select input requests are sent - x_sync(ps->c); - - // Update window mode here to check for ARGB windows - win_determine_mode(ps, w); - - // Detect client window here instead of in add_win() as the client - // window should have been prepared at this point - if (!w->client_win) { - win_recheck_client(ps, w); - } else { - // Re-mark client window here - win_mark_client(ps, w, w->client_win); - } - - assert(w->client_win); - - log_trace("(%#010x): type %s", w->id, WINTYPES[w->window_type]); - - // FocusIn/Out may be ignored when the window is unmapped, so we must - // recheck focus here - if (ps->o.track_focus) - recheck_focus(ps); - - // Update window focus state - win_update_focused(ps, w); - - // Update opacity and dim state - win_update_opacity_prop(ps, w); - w->flags |= WFLAG_OPCT_CHANGE; - - // Check for _COMPTON_SHADOW - if (ps->o.respect_prop_shadow) - win_update_prop_shadow_raw(ps, w); - - // Many things above could affect shadow - win_determine_shadow(ps, w); - - // Set fading state - w->in_openclose = true; - win_set_fade_callback(ps, &w, finish_map_win, true); - win_determine_fade(ps, w); - - win_determine_blur_background(ps, w); - - w->ever_damaged = false; - - /* if any configure events happened while - the window was unmapped, then configure - the window to its correct place */ - if (w->need_configure) - configure_win(ps, &w->queue_configure); - - // We stopped listening on ShapeNotify events - // when the window is unmapped (XXX we shouldn't), - // so the shape of the window might have changed, - // update. (Issue #35) - win_update_bounding_shape(ps, w); - -#ifdef CONFIG_DBUS - // Send D-Bus signal - if (ps->o.dbus) { - cdbus_ev_win_mapped(ps, w); - } -#endif -} - -static void -finish_unmap_win(session_t *ps, win **_w) { - win *w = *_w; - w->ever_damaged = false; - w->in_openclose = false; - w->reg_ignore_valid = false; - - /* damage region */ - add_damage_from_win(ps, w); - - free_paint(ps, &w->paint); - free_paint(ps, &w->shadow_paint); -} - -static void -unmap_win(session_t *ps, win **_w) { - win *w = *_w; - if (!w || w->a.map_state == XCB_MAP_STATE_UNMAPPED) return; - - if (w->destroying) return; - - // Set focus out - win_set_focused(ps, w, false); - - w->a.map_state = XCB_MAP_STATE_UNMAPPED; - - // Fading out - w->flags |= WFLAG_OPCT_CHANGE; - win_set_fade_callback(ps, _w, finish_unmap_win, false); - w->in_openclose = true; - win_determine_fade(ps, w); - - // Validate pixmap if we have to do fading - if (w->fade) - win_validate_pixmap(ps, w); - - // don't care about properties anymore - win_ev_stop(ps, w); - -#ifdef CONFIG_DBUS - // Send D-Bus signal - if (ps->o.dbus) { - cdbus_ev_win_unmapped(ps, w); - } -#endif -} - static void restack_win(session_t *ps, win *w, xcb_window_t new_above) { xcb_window_t old_above; @@ -986,7 +780,7 @@ restack_win(session_t *ps, win *w, xcb_window_t new_above) { // rehook for (prev = &ps->list; *prev; prev = &(*prev)->next) { - if ((*prev)->id == new_above && !(*prev)->destroying) { + if ((*prev)->id == new_above && (*prev)->state != WSTATE_DESTROYING) { found = true; break; } @@ -1037,7 +831,7 @@ restack_win(session_t *ps, win *w, xcb_window_t new_above) { } } -static void +void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) { // On root window changes if (ce->window == ps->root) { @@ -1131,7 +925,7 @@ configure_win(session_t *ps, xcb_configure_notify_event_t *ce) { if (factor_change) { win_on_factor_change(ps, w); add_damage(ps, &damage); - cxinerama_win_upd_scr(ps, w); + win_update_screen(ps, w); } } @@ -1158,67 +952,6 @@ circulate_win(session_t *ps, xcb_circulate_notify_event_t *ce) { restack_win(ps, w, new_above); } -// TODO move to win.c -static void -finish_destroy_win(session_t *ps, win **_w) { - win *w = *_w; - assert(w->destroying); - win **prev = NULL, *i = NULL; - - log_trace("(%#010x): Starting...", w->id); - - for (prev = &ps->list; (i = *prev); prev = &i->next) { - if (w == i) { - log_trace("(%#010x \"%s\"): %p", w->id, w->name, w); - - finish_unmap_win(ps, _w); - *prev = w->next; - - // Clear active_win if it's pointing to the destroyed window - if (w == ps->active_win) - ps->active_win = NULL; - - free_win_res(ps, w); - - // Drop w from all prev_trans to avoid accessing freed memory in - // repair_win() - for (win *w2 = ps->list; w2; w2 = w2->next) - if (w == w2->prev_trans) - w2->prev_trans = NULL; - - free(w); - *_w = NULL; - break; - } - } -} - -static void -destroy_win(session_t *ps, xcb_window_t id) { - win *w = find_win(ps, id); - - log_trace("%#010x \"%s\": %p", id, (w ? w->name: NULL), w); - - if (w) { - unmap_win(ps, &w); - - w->destroying = true; - - if (ps->o.no_fading_destroyed_argb) - win_determine_fade(ps, w); - - // Set fading callback - win_set_fade_callback(ps, &w, finish_destroy_win, false); - -#ifdef CONFIG_DBUS - // Send D-Bus signal - if (ps->o.dbus) { - cdbus_ev_win_destroyed(ps, w); - } -#endif - } -} - static inline void root_damaged(session_t *ps) { if (ps->root_tile_paint.pixmap) { @@ -1284,14 +1017,12 @@ win_set_shadow_force(session_t *ps, win *w, switch_t val) { /** * Set w->fade_force of a window. + * + * Doesn't affect fading already in progress */ void win_set_fade_force(session_t *ps, win *w, switch_t val) { - if (val != w->fade_force) { - w->fade_force = val; - win_determine_fade(ps, w); - queue_redraw(ps); - } + w->fade_force = val; } /** @@ -1343,15 +1074,12 @@ opts_init_track_focus(session_t *ps) { /** * Set no_fading_openclose option. + * + * Don't affect fading already in progress */ void opts_set_no_fading_openclose(session_t *ps, bool newval) { - if (newval != ps->o.no_fading_openclose) { - ps->o.no_fading_openclose = newval; - for (win *w = ps->list; w; w = w->next) - win_determine_fade(ps, w); - queue_redraw(ps); - } + ps->o.no_fading_openclose = newval; } //!@} @@ -1518,20 +1246,28 @@ ev_configure_notify(session_t *ps, xcb_configure_notify_event_t *ev) { inline static void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *ev) { - destroy_win(ps, ev->window); + win *w = find_win(ps, ev->window); + if (w) { + unmap_win(ps, &w, true); + } } inline static void ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) { map_win(ps, ev->window); + // FocusIn/Out may be ignored when the window is unmapped, so we must + // recheck focus here + if (ps->o.track_focus) { + recheck_focus(ps); + } } inline static void ev_unmap_notify(session_t *ps, xcb_unmap_notify_event_t *ev) { win *w = find_win(ps, ev->window); - - if (w) - unmap_win(ps, &w); + if (w) { + unmap_win(ps, &w, false); + } } inline static void @@ -1540,9 +1276,14 @@ ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t *ev) { ev->parent, ev->override_redirect); if (ev->parent == ps->root) { + // new window add_win(ps, ev->window, 0); } else { - destroy_win(ps, ev->window); + // otherwise, find and destroy the window first + win *w = find_win(ps, ev->window); + if (w) { + unmap_win(ps, &w, true); + } // Reset event mask in case something wrong happens xcb_change_window_attributes(ps->c, ev->window, XCB_CW_EVENT_MASK, @@ -1687,7 +1428,13 @@ ev_property_notify(session_t *ps, xcb_property_notify_event_t *ev) { win *w = find_win(ps, ev->window) ?: find_toplevel(ps, ev->window); if (w) { win_update_opacity_prop(ps, w); - w->flags |= WFLAG_OPCT_CHANGE; + // we cannot receive OPACITY change when window is destroyed + assert(w->state != WSTATE_DESTROYING); + if (w->state == WSTATE_MAPPED) { + // See the winstate_t transition table + w->state = WSTATE_FADING; + } + w->opacity_tgt = win_get_opacity_target(ps, w); } } @@ -2228,7 +1975,7 @@ init_overlay(session_t *ps) { static void redir_start(session_t *ps) { if (!ps->redirected) { - log_trace("Screen redirected."); + log_debug("Screen redirected."); // Map overlay window. Done firstly according to this: // https://bugzilla.gnome.org/show_bug.cgi?id=597014 @@ -2262,7 +2009,7 @@ redir_start(session_t *ps) { static void redir_stop(session_t *ps) { if (ps->redirected) { - log_trace("Screen unredirected."); + log_debug("Screen unredirected."); // Destroy all Pictures as they expire once windows are unredirected // If we don't destroy them here, looks like the resources are just // kept inaccessible somehow @@ -2516,16 +2263,16 @@ session_init(int argc, char **argv, Display *dpy, const char *config_file, .respect_prop_shadow = false, .xinerama_shadow_crop = false, - .fade_in_step = 0.028 * OPAQUE, - .fade_out_step = 0.03 * OPAQUE, + .fade_in_step = 0.028, + .fade_out_step = 0.03, .fade_delta = 10, .no_fading_openclose = false, .no_fading_destroyed_argb = false, .fade_blacklist = NULL, - .inactive_opacity = OPAQUE, + .inactive_opacity = 1.0, .inactive_opacity_override = false, - .active_opacity = OPAQUE, + .active_opacity = 1.0, .frame_opacity = 1.0, .detect_client_opacity = false, @@ -3167,20 +2914,16 @@ session_destroy(session_t *ps) { */ static void session_run(session_t *ps) { - win *t; - if (ps->o.sw_opti) ps->paint_tm_offset = get_time_timeval().tv_usec; - t = paint_preprocess(ps, ps->list); - - if (ps->redirected) - paint_all(ps, t, true); - // In benchmark mode, we want draw_idle handler to always be active - if (ps->o.benchmark) + if (ps->o.benchmark) { ev_idle_start(ps->loop, &ps->draw_idle); - + } else { + // Let's draw our first frame! + queue_redraw(ps); + } ev_run(ps->loop, 0); } diff --git a/src/compton.h b/src/compton.h index 9bc6ccf..f55257a 100644 --- a/src/compton.h +++ b/src/compton.h @@ -148,14 +148,4 @@ dump_drawable(session_t *ps, xcb_drawable_t drawable) { free(r); } -/** - * Validate pixmap of a window, and destroy pixmap and picture if invalid. - */ -static inline void -win_validate_pixmap(session_t *ps, win *w) { - // Destroy pixmap and picture, if invalid - if (!x_validate_pixmap(ps->c, w->paint.pixmap)) - free_paint(ps, &w->paint); -} - // vim: set et sw=2 : diff --git a/src/config.h b/src/config.h index 785d153..af9d220 100644 --- a/src/config.h +++ b/src/config.h @@ -167,9 +167,9 @@ typedef struct options_t { // === Fading === /// How much to fade in in a single fading step. - opacity_t fade_in_step; + double fade_in_step; /// How much to fade out in a single fading step. - opacity_t fade_out_step; + double fade_out_step; /// Fading time delta. In milliseconds. unsigned long fade_delta; /// Whether to disable fading on window open/close. @@ -181,11 +181,10 @@ typedef struct options_t { // === Opacity === /// Default opacity for inactive windows. - /// 32-bit integer with the format of _NET_WM_OPACITY. 0 stands for - /// not enabled, default. - opacity_t inactive_opacity; + /// 32-bit integer with the format of _NET_WM_OPACITY. + double inactive_opacity; /// Default opacity for inactive windows. - opacity_t active_opacity; + double active_opacity; /// Whether inactive_opacity overrides the opacity set by window /// attributes. bool inactive_opacity_override; diff --git a/src/config_libconfig.c b/src/config_libconfig.c index a7d3413..eebce86 100644 --- a/src/config_libconfig.c +++ b/src/config_libconfig.c @@ -218,10 +218,10 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad opt->fade_delta = ival; // -I (fade_in_step) if (config_lookup_float(&cfg, "fade-in-step", &dval)) - opt->fade_in_step = normalize_d(dval) * OPAQUE; + opt->fade_in_step = normalize_d(dval); // -O (fade_out_step) if (config_lookup_float(&cfg, "fade-out-step", &dval)) - opt->fade_out_step = normalize_d(dval) * OPAQUE; + opt->fade_out_step = normalize_d(dval); // -r (shadow_radius) config_lookup_int(&cfg, "shadow-radius", &opt->shadow_radius); // -o (shadow_opacity) @@ -232,10 +232,10 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad config_lookup_int(&cfg, "shadow-offset-y", &opt->shadow_offset_y); // -i (inactive_opacity) if (config_lookup_float(&cfg, "inactive-opacity", &dval)) - opt->inactive_opacity = normalize_d(dval) * OPAQUE; + opt->inactive_opacity = normalize_d(dval); // --active_opacity if (config_lookup_float(&cfg, "active-opacity", &dval)) - opt->active_opacity = normalize_d(dval) * OPAQUE; + opt->active_opacity = normalize_d(dval); // -e (frame_opacity) config_lookup_float(&cfg, "frame-opacity", &opt->frame_opacity); // -c (shadow_enable) diff --git a/src/dbus.c b/src/dbus.c index f5ea614..3cfd5d1 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -493,8 +493,9 @@ cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data) { // Get the number of wids we are to include unsigned count = 0; for (win *w = ps->list; w; w = w->next) { - if (!w->destroying) + if (w->state != WSTATE_DESTROYING) { ++count; + } } if (!count) { @@ -509,7 +510,7 @@ cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data) { { cdbus_window_t *pcur = arr; for (win *w = ps->list; w; w = w->next) { - if (!w->destroying) { + if (w->state != WSTATE_DESTROYING) { *pcur = w->id; ++pcur; assert(pcur <= arr + count); @@ -812,7 +813,6 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg) { cdbus_m_win_get_do(mode, cdbus_reply_enum); cdbus_m_win_get_do(client_win, cdbus_reply_wid); cdbus_m_win_get_do(ever_damaged, cdbus_reply_bool); - cdbus_m_win_get_do(destroying, cdbus_reply_bool); cdbus_m_win_get_do(window_type, cdbus_reply_enum); cdbus_m_win_get_do(wmwin, cdbus_reply_bool); cdbus_m_win_get_do(leader, cdbus_reply_wid); @@ -856,7 +856,6 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg) { } cdbus_m_win_get_do(shadow, cdbus_reply_bool); - cdbus_m_win_get_do(fade, cdbus_reply_bool); cdbus_m_win_get_do(invert_color, cdbus_reply_bool); cdbus_m_win_get_do(blur_background, cdbus_reply_bool); #undef cdbus_m_win_get_do @@ -1140,7 +1139,7 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) { double val = 0.0; if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val)) return false; - ps->o.fade_in_step = normalize_d(val) * OPAQUE; + ps->o.fade_in_step = normalize_d(val); goto cdbus_process_opts_set_success; } @@ -1149,7 +1148,7 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) { double val = 0.0; if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val)) return false; - ps->o.fade_out_step = normalize_d(val) * OPAQUE; + ps->o.fade_out_step = normalize_d(val); goto cdbus_process_opts_set_success; } diff --git a/src/options.c b/src/options.c index 23dc8eb..ed83078 100644 --- a/src/options.c +++ b/src/options.c @@ -570,8 +570,8 @@ void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable, // These options are handled by get_early_config() break; P_CASELONG('D', fade_delta); - case 'I': opt->fade_in_step = normalize_d(atof(optarg)) * OPAQUE; break; - case 'O': opt->fade_out_step = normalize_d(atof(optarg)) * OPAQUE; break; + case 'I': opt->fade_in_step = normalize_d(atof(optarg)); break; + case 'O': opt->fade_out_step = normalize_d(atof(optarg)); break; case 'c': shadow_enable = true; break; case 'C': winopt_mask[WINTYPE_DOCK].shadow = true; @@ -600,7 +600,7 @@ void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable, P_CASELONG('l', shadow_offset_x); P_CASELONG('t', shadow_offset_y); case 'i': - opt->inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE); + opt->inactive_opacity = normalize_d(atof(optarg)); break; case 'e': opt->frame_opacity = atof(optarg); break; case 'z': @@ -718,7 +718,7 @@ void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable, break; case 297: // --active-opacity - opt->active_opacity = (normalize_d(atof(optarg)) * OPAQUE); + opt->active_opacity = normalize_d(atof(optarg)); break; P_CASEBOOL(298, glx_no_rebind_pixmap); case 299: diff --git a/src/render.c b/src/render.c index cc65937..00c33e7 100644 --- a/src/render.c +++ b/src/render.c @@ -307,10 +307,8 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) { } } - const double dopacity = get_opacity_percent(w); - if (w->frame_opacity == 1) { - paint_region(ps, w, 0, 0, wid, hei, dopacity, reg_paint, pict); + paint_region(ps, w, 0, 0, wid, hei, w->opacity, reg_paint, pict); } else { // Painting parameters const margin_t extents = win_calc_frame_extents(w); @@ -320,7 +318,7 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) { const int r = extents.right; #define COMP_BDR(cx, cy, cwid, chei) \ - paint_region(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity *dopacity, \ + paint_region(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity * w->opacity, \ reg_paint, pict) // Sanitize the margins, in case some broken WM makes @@ -372,7 +370,7 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) { // body paint_region(ps, w, cleft, ctop, body_width, body_height, - dopacity, reg_paint, pict); + w->opacity, reg_paint, pict); } while (0); } @@ -385,7 +383,7 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) { if (w->dim) { double dim_opacity = ps->o.inactive_dim; if (!ps->o.inactive_dim_fixed) - dim_opacity *= get_opacity_percent(w); + dim_opacity *= w->opacity; switch (ps->o.backend) { case BKEND_XRENDER: @@ -678,7 +676,7 @@ static inline void win_blur_background(session_t *ps, win *w, xcb_render_picture // Adjust blur strength according to window opacity, to make it appear // better during fading if (!ps->o.blur_background_fixed) { - double pct = 1.0 - get_opacity_percent(w) * (1.0 - 1.0 / 9.0); + double pct = 1.0 - w->opacity * (1.0 - 1.0 / 9.0); factor_center = pct * 8.0 / (1.1 - pct); } diff --git a/src/types.h b/src/types.h index f9bf14c..a879b80 100644 --- a/src/types.h +++ b/src/types.h @@ -9,7 +9,7 @@ /// Enumeration type to represent switches. typedef enum { - OFF, // false + OFF = 0, // false ON, // true UNSET } switch_t; diff --git a/src/win.c b/src/win.c index 9d74bee..f0a9691 100644 --- a/src/win.c +++ b/src/win.c @@ -30,8 +30,15 @@ #include "dbus.h" #endif +#ifdef CONFIG_OPENGL +// TODO remove this include +#include "opengl.h" +#endif + #include "win.h" +#define OPAQUE 0xffffffff + /// 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 *) @@ -74,7 +81,7 @@ group_update_focused(session_t *ps, xcb_window_t leader) { return; for (win *w = ps->list; w; w = w->next) { - if (win_get_leader(ps, w) == leader && !w->destroying) + if (win_get_leader(ps, w) == leader && w->state != WSTATE_DESTROYING) win_update_focused(ps, w); } @@ -93,7 +100,7 @@ group_is_focused(session_t *ps, xcb_window_t leader) { return false; for (win *w = ps->list; w; w = w->next) { - if (win_get_leader(ps, w) == leader && !w->destroying + if (win_get_leader(ps, w) == leader && w->state != WSTATE_DESTROYING && win_is_focused_real(ps, w)) return true; } @@ -292,14 +299,14 @@ bool wid_get_opacity_prop(session_t *ps, xcb_window_t wid, opacity_t def, } // XXX should distinguish between frame has alpha and window body has alpha -bool win_has_alpha(win *w) { +bool win_has_alpha(const win *w) { return w->pictfmt && w->pictfmt->type == XCB_RENDER_PICT_TYPE_DIRECT && w->pictfmt->direct.alpha_mask; } void win_determine_mode(session_t *ps, win *w) { - if (win_has_alpha(w) || w->opacity != OPAQUE) { + if (win_has_alpha(w) || w->opacity < 1.0) { w->mode = WMODE_TRANS; } else if (w->frame_opacity != 1.0) { w->mode = WMODE_FRAME_TRANS; @@ -309,7 +316,7 @@ void win_determine_mode(session_t *ps, win *w) { } /** - * Calculate and set the opacity of a window. + * Calculate and return the opacity target of a window. * * If window is inactive and inactive_opacity_override is set, the * priority is: (Simulates the old behavior) @@ -325,78 +332,81 @@ void win_determine_mode(session_t *ps, win *w) { * * @param ps current session * @param w struct _win object representing the window + * + * @return target opacity */ -void win_calc_opacity(session_t *ps, win *w) { - opacity_t opacity = OPAQUE; +double win_get_opacity_target(session_t *ps, const win *w) { + double opacity = 1; - if (w->destroying || w->a.map_state != XCB_MAP_STATE_VIEWABLE) - opacity = 0; - else { - // Try obeying opacity property and window type opacity firstly - if (w->has_opacity_prop) - opacity = w->opacity_prop; - else if (!safe_isnan(ps->o.wintype_option[w->window_type].opacity)) - opacity = ps->o.wintype_option[w->window_type].opacity * OPAQUE; - else { - // Respect active_opacity only when the window is physically focused - if (win_is_focused_real(ps, w)) - opacity = ps->o.active_opacity; - else if (false == w->focused) - // Respect inactive_opacity in some cases - opacity = ps->o.inactive_opacity; - } - - // respect inactive override - if (ps->o.inactive_opacity_override && false == w->focused) + if (w->state == WSTATE_UNMAPPED) { + // be consistent + return 0; + } + if (w->state == WSTATE_UNMAPPING || w->state == WSTATE_DESTROYING) { + return 0; + } + // Try obeying opacity property and window type opacity firstly + if (w->has_opacity_prop) { + opacity = ((double)w->opacity_prop) / OPAQUE; + } else if (!safe_isnan(ps->o.wintype_option[w->window_type].opacity)) { + opacity = ps->o.wintype_option[w->window_type].opacity; + } else { + // Respect active_opacity only when the window is physically focused + if (win_is_focused_real(ps, w)) + opacity = ps->o.active_opacity; + else if (!w->focused) + // Respect inactive_opacity in some cases opacity = ps->o.inactive_opacity; } - w->opacity_tgt = opacity; + // respect inactive override + if (ps->o.inactive_opacity_override && !w->focused) + opacity = ps->o.inactive_opacity; + + return opacity; } /** * Determine whether a window is to be dimmed. */ -void win_calc_dim(session_t *ps, win *w) { - bool dim; - +bool win_should_dim(session_t *ps, const win *w) { // Make sure we do nothing if the window is unmapped / being destroyed - if (w->destroying || w->a.map_state != XCB_MAP_STATE_VIEWABLE) - return; - - if (ps->o.inactive_dim && !(w->focused)) { - dim = true; - } else { - dim = false; + if (w->state == WSTATE_UNMAPPED) { + return false; } - if (dim != w->dim) { - w->dim = dim; - add_damage_from_win(ps, w); + if (ps->o.inactive_dim && !(w->focused)) { + return true; + } else { + return false; } } /** * Determine if a window should fade on opacity change. */ -void win_determine_fade(session_t *ps, win *w) { +bool win_should_fade(session_t *ps, const win *w) { // To prevent it from being overwritten by last-paint value if the window is // unmapped on next frame, write w->fade_last as well - if (UNSET != w->fade_force) - w->fade_last = w->fade = w->fade_force; - else if (ps->o.no_fading_openclose && w->in_openclose) - w->fade_last = w->fade = false; - else if (ps->o.no_fading_destroyed_argb && w->destroying && - win_has_alpha(w) && w->client_win && w->client_win != w->id) { - w->fade_last = w->fade = false; + if (w->fade_force != UNSET) { + return w->fade_force; + } + if (ps->o.no_fading_openclose && w->in_openclose) { + return false; + } + if (ps->o.no_fading_destroyed_argb && w->state == WSTATE_DESTROYING && + win_has_alpha(w) && w->client_win && w->client_win != w->id) { + // deprecated + return false; } // Ignore other possible causes of fading state changes after window // gets unmapped - else if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) { - } else if (c2_match(ps, w, ps->o.fade_blacklist, &w->cache_fblst, NULL)) - w->fade = false; - else - w->fade = ps->o.wintype_option[w->window_type].fade; + //if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) { + //} + if (c2_match(ps, w, ps->o.fade_blacklist, NULL)) { + return false; + } + return ps->o.wintype_option[w->window_type].fade; } /** @@ -467,7 +477,7 @@ void win_determine_shadow(session_t *ps, win *w) { shadow_new = w->shadow_force; else if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) shadow_new = (ps->o.wintype_option[w->window_type].shadow && - !c2_match(ps, w, ps->o.shadow_blacklist, &w->cache_sblst, NULL) && + !c2_match(ps, w, ps->o.shadow_blacklist, NULL) && !(ps->o.shadow_ignore_shaped && w->bounding_shaped && !w->rounded_corners) && !(ps->o.respect_prop_shadow && 0 == w->prop_shadow)); @@ -494,7 +504,7 @@ void win_determine_invert_color(session_t *ps, win *w) { invert_color_new = w->invert_color_force; else if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) invert_color_new = - c2_match(ps, w, ps->o.invert_color_list, &w->cache_ivclst, NULL); + c2_match(ps, w, ps->o.invert_color_list, NULL); win_set_invert_color(ps, w, invert_color_new); } @@ -520,23 +530,26 @@ void win_determine_blur_background(session_t *ps, win *w) { bool blur_background_new = ps->o.blur_background && - !c2_match(ps, w, ps->o.blur_background_blacklist, &w->cache_bbblst, NULL); + !c2_match(ps, w, ps->o.blur_background_blacklist, NULL); win_set_blur_background(ps, w, blur_background_new); } /** * Update window opacity according to opacity rules. + * + * TODO This override the window's opacity property, may not be + * a good idea. */ void win_update_opacity_rule(session_t *ps, win *w) { if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) return; - opacity_t opacity = OPAQUE; + double opacity = 1.0; bool is_set = false; void *val = NULL; - if (c2_match(ps, w, ps->o.opacity_rules, &w->cache_oparule, &val)) { - opacity = ((double)(long)val) / 100.0 * OPAQUE; + if (c2_match(ps, w, ps->o.opacity_rules, &val)) { + opacity = ((double)(long)val) / 100.0; is_set = true; } @@ -548,7 +561,7 @@ void win_update_opacity_rule(session_t *ps, win *w) { if (!is_set) wid_rm_opacity_prop(ps, w->id); else - wid_set_opacity_prop(ps, w->id, opacity); + wid_set_opacity_prop(ps, w->id, opacity * OPAQUE); } /** @@ -556,7 +569,6 @@ void win_update_opacity_rule(session_t *ps, win *w) { */ void win_on_wtype_change(session_t *ps, win *w) { win_determine_shadow(ps, w); - win_determine_fade(ps, w); win_update_focused(ps, w); if (ps->o.invert_color_list) win_determine_invert_color(ps, w); @@ -566,12 +578,12 @@ void win_on_wtype_change(session_t *ps, win *w) { /** * Function to be called on window data changes. + * + * TODO need better name */ void win_on_factor_change(session_t *ps, win *w) { if (ps->o.shadow_blacklist) win_determine_shadow(ps, w); - if (ps->o.fade_blacklist) - win_determine_fade(ps, w); if (ps->o.invert_color_list) win_determine_invert_color(ps, w); if (ps->o.focus_blacklist) @@ -582,10 +594,10 @@ void win_on_factor_change(session_t *ps, win *w) { win_update_opacity_rule(ps, w); if (w->a.map_state == XCB_MAP_STATE_VIEWABLE && ps->o.paint_blacklist) w->paint_excluded = - c2_match(ps, w, ps->o.paint_blacklist, &w->cache_pblst, NULL); + c2_match(ps, w, ps->o.paint_blacklist, NULL); if (w->a.map_state == XCB_MAP_STATE_VIEWABLE && ps->o.unredir_if_possible_blacklist) w->unredir_if_possible_excluded = c2_match( - ps, w, ps->o.unredir_if_possible_blacklist, &w->cache_uipblst, NULL); + ps, w, ps->o.unredir_if_possible_blacklist, NULL); w->reg_ignore_valid = false; } @@ -733,6 +745,31 @@ void win_recheck_client(session_t *ps, win *w) { win_mark_client(ps, w, cw); } +/** + * Free all resources in a struct _win. + */ +void free_win_res(session_t *ps, win *w) { + // No need to call backend release_win here because + // finish_unmap_win should've done that for us. + // XXX unless we are called by session_destroy + // assert(w->win_data == NULL); + free_win_res_glx(ps, w); + free_paint(ps, &w->paint); + free_paint(ps, &w->shadow_paint); + // Above should be done during unmapping + // Except when we are called by session_destroy + + pixman_region32_fini(&w->bounding_shape); + // BadDamage may be thrown if the window is destroyed + set_ignore_cookie(ps, + xcb_damage_destroy(ps->c, w->damage)); + rc_region_unref(&w->reg_ignore); + free(w->name); + free(w->class_instance); + free(w->class_general); + free(w->role); +} + // TODO: probably split into win_new (in win.c) and add_win (in compton.c) bool add_win(session_t *ps, xcb_window_t id, xcb_window_t prev) { static const win win_def = { @@ -759,11 +796,14 @@ bool add_win(session_t *ps, xcb_window_t id, xcb_window_t prev) { .widthb = 0, .heightb = 0, - .destroying = false, + .state = WSTATE_UNMAPPED, .bounding_shaped = false, .rounded_corners = false, .to_paint = false, - .in_openclose = false, + + // true when the window is first created. + // set to false after the first map is done + .in_openclose = true, .client_win = XCB_NONE, .window_type = WINTYPE_UNKNOWN, @@ -778,23 +818,15 @@ bool add_win(session_t *ps, xcb_window_t id, xcb_window_t prev) { .class_instance = NULL, .class_general = NULL, .role = NULL, - .cache_sblst = NULL, - .cache_fblst = NULL, - .cache_fcblst = NULL, - .cache_ivclst = NULL, - .cache_bbblst = NULL, - .cache_oparule = NULL, .opacity = 0, .opacity_tgt = 0, .has_opacity_prop = false, .opacity_prop = OPAQUE, .opacity_is_set = false, - .opacity_set = OPAQUE, + .opacity_set = 1, - .fade = false, .fade_force = UNSET, - .fade_callback = NULL, .frame_opacity = 1.0, .frame_extents = MARGIN_INIT, @@ -834,7 +866,7 @@ bool add_win(session_t *ps, xcb_window_t id, xcb_window_t prev) { win **p = NULL; if (prev) { for (p = &ps->list; *p; p = &(*p)->next) { - if ((*p)->id == prev && !(*p)->destroying) + if ((*p)->id == prev && (*p)->state != WSTATE_DESTROYING) break; } } else { @@ -874,7 +906,7 @@ bool add_win(session_t *ps, xcb_window_t id, xcb_window_t prev) { assert(map_state == XCB_MAP_STATE_VIEWABLE || map_state == XCB_MAP_STATE_UNMAPPED); new->a.map_state = XCB_MAP_STATE_UNMAPPED; - if (InputOutput == new->a._class) { + if (new->a._class == XCB_WINDOW_CLASS_INPUT_OUTPUT) { // Create Damage for window new->damage = xcb_generate_id(ps->c); xcb_generic_error_t *e = xcb_request_check(ps->c, @@ -924,7 +956,7 @@ void win_update_focused(session_t *ps, win *w) { || (ps->o.mark_ovredir_focused && w->id == w->client_win && !w->wmwin) || (w->a.map_state == XCB_MAP_STATE_VIEWABLE && - c2_match(ps, w, ps->o.focus_blacklist, &w->cache_fcblst, NULL))) + c2_match(ps, w, ps->o.focus_blacklist, NULL))) w->focused = true; // If window grouping detection is enabled, mark the window active if @@ -938,7 +970,8 @@ void win_update_focused(session_t *ps, win *w) { // Always recalculate the window target opacity, since some opacity-related // options depend on the output value of win_is_focused_real() instead of // w->focused - w->flags |= WFLAG_OPCT_CHANGE; + w->opacity_tgt = win_get_opacity_target(ps, w); + w->state = WSTATE_FADING; } /** @@ -1288,24 +1321,120 @@ void win_ev_stop(session_t *ps, win *w) { } } -/** - * Set fade callback of a window, and possibly execute the previous - * callback. - * - * If a callback can cause rendering result to change, it should call - * `queue_redraw`. - * - * @param exec_callback whether the previous callback is to be executed - */ -void win_set_fade_callback(session_t *ps, win **_w, - void (*callback) (session_t *ps, win **w), bool exec_callback) { +static void +finish_unmap_win(session_t *ps, win **_w) { win *w = *_w; - void (*old_callback) (session_t *ps, win **w) = w->fade_callback; + w->ever_damaged = false; + w->reg_ignore_valid = false; + w->state = WSTATE_UNMAPPED; + w->flags = 0; - w->fade_callback = callback; - // Must be the last line as the callback could destroy w! - if (exec_callback && old_callback) - old_callback(ps, _w); + free_paint(ps, &w->paint); + free_paint(ps, &w->shadow_paint); +} + +static void +finish_destroy_win(session_t *ps, win **_w) { + win *w = *_w; + win **prev = NULL; + + // Window can't go from UNMAPPED to DESTROYING, and + // UNMAPPED is the only state where the window resource + // is freed. That means the window resources have not + // been freed at this point. call finish_unmap_win to + // free them. + finish_unmap_win(ps, _w); + + log_trace("Trying to destroy (%#010x)", w->id); + for (prev = &ps->list; *prev; prev = &(*prev)->next) { + if (w == *prev) { + log_trace("Found (%#010x \"%s\")", w->id, w->name); + *prev = w->next; + + if (w == ps->active_win) { + ps->active_win = NULL; + } + + free_win_res(ps, w); + + // Drop w from all prev_trans to avoid accessing freed memory in + // repair_win() + // TODO there can only be one prev_trans pointing to w + for (win *w2 = ps->list; w2; w2 = w2->next) { + if (w == w2->prev_trans) { + w2->prev_trans = NULL; + } + } + free(w); + *_w = NULL; + return; + } + } + log_warn("Destroyed window is not in window list"); + assert(false); +} + +static void +finish_map_win(session_t *ps, win **_w) { + win *w = *_w; + w->in_openclose = false; + w->state = WSTATE_MAPPED; +} + +void +unmap_win(session_t *ps, win **_w, bool destroy) { + win *w = *_w; + + log_trace("Unmapping %#010x \"%s\", destroy = %d", w->id, (w ? w->name: NULL), destroy); + winstate_t target_state = destroy ? WSTATE_DESTROYING : WSTATE_UNMAPPING; + + if (unlikely(!w)) { + return; + } + + if (unlikely(w->state == WSTATE_DESTROYING && !destroy)) { + log_warn("Trying to undestroy a window?"); + assert(false); + } + + // If the window is already in the state we want + if (unlikely(w->state == target_state)) { + log_warn("%s a window twice", destroy ? "Destroying" : "Unmapping"); + return; + } + + if (unlikely(w->state == WSTATE_UNMAPPED)) { + if (unlikely(!destroy)) { + log_warn("Unmapping an already unmapped window twice"); + return; + } + // Window is already unmapped, just destroy it + finish_destroy_win(ps, _w); + return; + } + + // Set focus out + win_set_focused(ps, w, false); + + w->a.map_state = XCB_MAP_STATE_UNMAPPED; + w->state = target_state; + w->opacity_tgt = win_get_opacity_target(ps, w); + + w->in_openclose = destroy; + + // don't care about properties anymore + win_ev_stop(ps, w); + +#ifdef CONFIG_DBUS + // Send D-Bus signal + if (ps->o.dbus) { + if (destroy) { + cdbus_ev_win_destroyed(ps, w); + } else { + cdbus_ev_win_unmapped(ps, w); + } + } +#endif } /** @@ -1314,8 +1443,161 @@ void win_set_fade_callback(session_t *ps, win **_w, void win_check_fade_finished(session_t *ps, win **_w) { win *w = *_w; - if (w->fade_callback && w->opacity == w->opacity_tgt) { - // Must be the last line as the callback could destroy w! - win_set_fade_callback(ps, _w, NULL, true); + if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) { + // No fading in progress + assert(w->opacity_tgt == w->opacity); + return; + } + if (w->opacity == w->opacity_tgt) { + switch (w->state) { + case WSTATE_UNMAPPING: return finish_unmap_win(ps, _w); + case WSTATE_DESTROYING: return finish_destroy_win(ps, _w); + case WSTATE_MAPPING: return finish_map_win(ps, _w); + case WSTATE_FADING: w->state = WSTATE_MAPPED; break; + default: unreachable; + } } } + +/** + * Get the Xinerama screen a window is on. + * + * Return an index >= 0, or -1 if not found. + * + * XXX move to x.c + */ +void win_update_screen(session_t *ps, win *w) { +#ifdef CONFIG_XINERAMA + w->xinerama_scr = -1; + + if (!ps->xinerama_scrs) + return; + + xcb_xinerama_screen_info_t *scrs = xcb_xinerama_query_screens_screen_info(ps->xinerama_scrs); + int length = xcb_xinerama_query_screens_screen_info_length(ps->xinerama_scrs); + for (int i = 0; i < length; i++) { + xcb_xinerama_screen_info_t *s = &scrs[i]; + if (s->x_org <= w->g.x && s->y_org <= w->g.y + && s->x_org + s->width >= w->g.x + w->widthb + && s->y_org + s->height >= w->g.y + w->heightb) { + w->xinerama_scr = i; + return; + } + } +#endif +} + +// TODO remove this +void configure_win(session_t *, xcb_configure_notify_event_t *); + +/// Map an already registered window +void +map_win(session_t *ps, xcb_window_t id) { + // Unmap overlay window if it got mapped but we are currently not + // in redirected state. + if (ps->overlay && id == ps->overlay && !ps->redirected) { + log_debug("Overlay is mapped while we are not redirected"); + auto e = xcb_request_check(ps->c, xcb_unmap_window(ps->c, ps->overlay)); + if (e) { + log_error("Failed to unmap the overlay window"); + free(e); + } + // We don't track the overlay window, so we can return + return; + } + + win *w = find_win(ps, id); + log_debug("Mapping (%#010x \"%s\")", id, (w ? w->name: NULL)); + + // Don't care about window mapping if it's an InputOnly window + // Also, try avoiding mapping a window twice + // TODO don't even add the input only windows + if (!w || w->a._class == XCB_WINDOW_CLASS_INPUT_ONLY) { + return; + } + + if (w->state != WSTATE_UNMAPPED && w->state != WSTATE_UNMAPPING) { + log_warn("Mapping an already mapped window"); + return; + } + + // XXX ??? + assert(!win_is_focused_real(ps, w)); + + // XXX Can we assume map_state is always viewable? + w->a.map_state = XCB_MAP_STATE_VIEWABLE; + + win_update_screen(ps, w); + + // Set window event mask before reading properties so that no property + // changes are lost + xcb_change_window_attributes(ps->c, id, XCB_CW_EVENT_MASK, + (const uint32_t[]) { determine_evmask(ps, id, WIN_EVMODE_FRAME) }); + + // Notify compton when the shape of a window changes + if (ps->shape_exists) { + xcb_shape_select_input(ps->c, id, 1); + } + + // Update window mode here to check for ARGB windows + win_determine_mode(ps, w); + + // Detect client window here instead of in add_win() as the client + // window should have been prepared at this point + if (!w->client_win) { + win_recheck_client(ps, w); + } else { + // Re-mark client window here + win_mark_client(ps, w, w->client_win); + } + assert(w->client_win); + + log_debug("Window (%#010x) has type %s", w->id, WINTYPES[w->window_type]); + + // Update window focus state + win_update_focused(ps, w); + + // Update opacity and dim state + win_update_opacity_prop(ps, w); + + // Check for _COMPTON_SHADOW + if (ps->o.respect_prop_shadow) { + win_update_prop_shadow_raw(ps, w); + } + + // Many things above could affect shadow + win_determine_shadow(ps, w); + + // XXX We need to make sure that win_data is available + // iff `state` is MAPPED + w->state = WSTATE_MAPPING; + w->opacity_tgt = win_get_opacity_target(ps, w); + log_debug("Window %#010x has opacity %f, opacity target is %f", w->id, w->opacity, w->opacity_tgt); + + win_determine_blur_background(ps, w); + + w->ever_damaged = false; + + /* if any configure events happened while + the window was unmapped, then configure + the window to its correct place */ + if (w->need_configure) { + configure_win(ps, &w->queue_configure); + } + + // We stopped listening on ShapeNotify events + // when the window is unmapped (XXX we shouldn't), + // so the shape of the window might have changed, + // update. (Issue #35) + win_update_bounding_shape(ps, w); + +#ifdef CONFIG_DBUS + // Send D-Bus signal + if (ps->o.dbus) { + cdbus_ev_win_mapped(ps, w); + } +#endif +} + + +// vim: set et sw=2 : diff --git a/src/win.h b/src/win.h index 4ed2491..70c5de5 100644 --- a/src/win.h +++ b/src/win.h @@ -64,6 +64,46 @@ typedef enum { WMODE_SOLID, // The window is opaque including the frame } winmode_t; +/// Transition table: +/// (DESTROYED is when the win struct is destroyed and freed) +/// ('o' means in all other cases) +/// (Window is created in the UNMAPPED state) +/// +-------------+---------+----------+-------+-------+--------+--------+---------+ +/// | |UNMAPPING|DESTROYING|MAPPING|FADING |UNMAPPED| MAPPED |DESTROYED| +/// +-------------+---------+----------+-------+-------+--------+--------+---------+ +/// | UNMAPPING | o | Window |Window | - | Fading | - | - | +/// | | |destroyed |mapped | |finished| | | +/// +-------------+---------+----------+-------+-------+--------+--------+---------+ +/// | DESTROYING | - | o | - | - | - | - | Fading | +/// | | | | | | | |finished | +/// +-------------+---------+----------+-------+-------+--------+--------+---------+ +/// | MAPPING | Window | Window | o | - | - | Fading | - | +/// | |unmapped |destroyed | | | |finished| | +/// +-------------+---------+----------+-------+-------+--------+--------+---------+ +/// | FADING | Window | Window | - | o | - | Fading | - | +/// | |unmapped |destroyed | | | |finished| | +/// +-------------+---------+----------+-------+-------+--------+--------+---------+ +/// | UNMAPPED | - | - |Window | - | o | - | Window | +/// | | | |mapped | | | |destroyed| +/// +-------------+---------+----------+-------+-------+--------+--------+---------+ +/// | MAPPED | Window | Window | - |Opacity| - | o | - | +/// | |unmapped |destroyed | |change | | | | +/// +-------------+---------+----------+-------+-------+--------+--------+---------+ +typedef enum { + // The window is being faded out because it's unmapped. + WSTATE_UNMAPPING, + // The window is being faded out because it's destroyed, + WSTATE_DESTROYING, + // The window is being faded in + WSTATE_MAPPING, + // Window opacity is not at the target level + WSTATE_FADING, + // The window is mapped, no fading is in progress. + WSTATE_MAPPED, + // The window is unmapped, no fading is in progress. + WSTATE_UNMAPPED, +} winstate_t; + /** * About coordinate systems * @@ -88,6 +128,9 @@ struct win { // Core members /// ID of the top-level frame window. xcb_window_t id; + /// The "mapped state" of this window, doesn't necessary + /// match X mapped state, because of fading. + winstate_t state; /// Window attributes. xcb_get_window_attributes_reply_t a; xcb_get_geometry_reply_t g; @@ -175,20 +218,12 @@ struct win { char *class_general; /// WM_WINDOW_ROLE value of the window. char *role; - const c2_lptr_t *cache_sblst; - const c2_lptr_t *cache_fblst; - const c2_lptr_t *cache_fcblst; - const c2_lptr_t *cache_ivclst; - const c2_lptr_t *cache_bbblst; - const c2_lptr_t *cache_oparule; - const c2_lptr_t *cache_pblst; - const c2_lptr_t *cache_uipblst; // Opacity-related members /// Current window opacity. - opacity_t opacity; + double opacity; /// Target window opacity. - opacity_t opacity_tgt; + double opacity_tgt; /// true if window (or client window, for broken window managers /// not transferring client window's _NET_WM_OPACITY value) has opacity prop bool has_opacity_prop; @@ -197,18 +232,11 @@ struct win { /// true if opacity is set by some rules bool opacity_is_set; /// Last window opacity value we set. - opacity_t opacity_set; + double opacity_set; // Fading-related members - /// Do not fade if it's false. Change on window type change. - /// Used by fading blacklist in the future. - bool fade; - /// Fade state on last paint. - bool fade_last; /// Override value of window fade state. Set by D-Bus method calls. switch_t fade_force; - /// Callback to be called after fading completed. - void (*fade_callback) (session_t *ps, win **w); // Frame-opacity-related members /// Current window frame opacity. Affected by window opacity. @@ -269,7 +297,7 @@ void win_determine_mode(session_t *ps, win *w); * Set real focused state of a window. */ void win_set_focused(session_t *ps, win *w, bool focused); -void win_determine_fade(session_t *ps, win *w); +bool attr_const win_should_fade(session_t *ps, const win *w); void win_update_prop_shadow_raw(session_t *ps, win *w); void win_update_prop_shadow(session_t *ps, win *w); void win_set_shadow(session_t *ps, win *w, bool shadow_new); @@ -288,8 +316,11 @@ void win_unmark_client(session_t *ps, win *w); void win_recheck_client(session_t *ps, win *w); xcb_window_t win_get_leader_raw(session_t *ps, win *w, int recursions); bool win_get_class(session_t *ps, win *w); -void win_calc_opacity(session_t *ps, win *w); -void win_calc_dim(session_t *ps, win *w); +double attr_const win_get_opacity_target(session_t *ps, const win *w); +bool attr_const win_should_dim(session_t *ps, const win *w); +void win_update_screen(session_t *, win *); +/// Prepare window for fading because opacity target changed +void win_start_fade(session_t *, win **); /** * Reread opacity property of a window. */ @@ -336,18 +367,8 @@ region_t win_get_region_noframe_local_by_val(win *w); void win_update_frame_extents(session_t *ps, win *w, xcb_window_t client); bool add_win(session_t *ps, xcb_window_t id, xcb_window_t prev); - -/** - * Set fade callback of a window, and possibly execute the previous - * callback. - * - * If a callback can cause rendering result to change, it should call - * `queue_redraw`. - * - * @param exec_callback whether the previous callback is to be executed - */ -void win_set_fade_callback(session_t *ps, win **_w, - void (*callback) (session_t *ps, win **w), bool exec_callback); +/// Unmap or destroy a window +void unmap_win(session_t *ps, win **, bool destroy); /** * Execute fade callback of a window if fading finished. @@ -369,11 +390,14 @@ win_get_leader(session_t *ps, win *w) { } /// check if window has ARGB visual -bool win_has_alpha(win *w); +bool attr_const win_has_alpha(const win *w); /// check if reg_ignore_valid is true for all windows above us bool win_is_region_ignore_valid(session_t *ps, win *w); +/// Free all resources in a struct win +void free_win_res(session_t *ps, win *w); + static inline region_t win_get_bounding_shape_global_by_val(win *w) { region_t ret;