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..d6f0ab0 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 @@ -371,9 +370,6 @@ typedef struct session { bool tmout_unredir_hit; /// Whether we need to redraw the screen bool redraw_needed; - /// Whether the program is idling. I.e. no fading, no potential window - /// changes. - bool fade_running; /// Program start time. struct timeval time_start; /// The region needs to painted on next paint. @@ -687,11 +683,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 +783,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 +803,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..831ccc9 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) { @@ -334,32 +277,46 @@ fade_timeout(session_t *ps) { * Run fading on a window. * * @param steps steps of fading + * @return whether we are still in fading mode */ -static void -run_fade(session_t *ps, win *w, unsigned steps) { - // If we have reached target opacity, return - if (w->opacity == w->opacity_tgt) { - return; +static bool +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 false; } - 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) - w->opacity = normalize_d_range( - (double) w->opacity + (double) ps->o.fade_in_step * steps, - 0.0, w->opacity_tgt); - else - w->opacity = normalize_d_range( - (double) w->opacity - (double) ps->o.fade_out_step * steps, - w->opacity_tgt, OPAQUE); + } + if (w->opacity == w->opacity_tgt) { + // We have reached target opacity, wrapping up. + // Note, we reach here after we have rendered the window with the target + // opacity at least once. If the window is destroyed because it is faded out, + // there is no need to add damage here. + log_debug("Fading finished for window %#010x %s", w->id, w->name); + win_check_fade_finished(ps, _w); + return false; } - if (w->opacity != w->opacity_tgt) { - ps->fade_running = true; + if (steps) { + if (w->opacity < w->opacity_tgt) { + w->opacity = normalize_d_range( + w->opacity + ps->o.fade_in_step * steps, + 0.0, w->opacity_tgt); + } else { + w->opacity = normalize_d_range( + w->opacity - ps->o.fade_out_step * steps, + w->opacity_tgt, 1); + } } + + // Note even if opacity == opacity_tgt, we still want to run preprocess one last + // time to finish state transition. So return true in that case too. + return true; } // === Error handling === @@ -516,8 +473,11 @@ find_client_win(session_t *ps, xcb_window_t w) { } static win * -paint_preprocess(session_t *ps, win *list) { +paint_preprocess(session_t *ps, win *list, bool *fade_running) { + // XXX need better, more general name for `fade_running`. It really + // means if fade is still ongoing after the current frame is rendered win *t = NULL, *next = NULL; + *fade_running = false; // Fading step calculation unsigned long steps = 0L; @@ -539,40 +499,50 @@ 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); + if (run_fade(ps, &w, steps)) { + *fade_running = true; + } - if (win_has_frame(w)) + if (!w) { + // the window might have been destroyed because fading finished + continue; + } + + 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 +573,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 +591,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 +643,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 +652,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 +661,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 +759,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 +790,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 +841,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 +935,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 +962,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 +1027,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 +1084,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 +1256,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 +1286,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 +1438,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 +1985,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 +2019,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 @@ -2337,14 +2094,14 @@ _draw_callback(EV_P_ session_t *ps, int revents) { } } - ps->fade_running = false; - win *t = paint_preprocess(ps, ps->list); + bool fade_running = false; + win *t = paint_preprocess(ps, ps->list, &fade_running); ps->tmout_unredir_hit = false; // Start/stop fade timer depends on whether window are fading - if (!ps->fade_running && ev_is_active(&ps->fade_timer)) + if (!fade_running && ev_is_active(&ps->fade_timer)) ev_timer_stop(ps->loop, &ps->fade_timer); - else if (ps->fade_running && !ev_is_active(&ps->fade_timer)) { + else if (fade_running && !ev_is_active(&ps->fade_timer)) { ev_timer_set(&ps->fade_timer, fade_timeout(ps), 0); ev_timer_start(ps->loop, &ps->fade_timer); } @@ -2359,7 +2116,7 @@ _draw_callback(EV_P_ session_t *ps, int revents) { exit(0); } - if (!ps->fade_running) + if (!fade_running) ps->fade_time = 0L; ps->redraw_needed = false; @@ -2516,16 +2273,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, @@ -2552,7 +2309,6 @@ session_init(int argc, char **argv, Display *dpy, const char *config_file, .time_start = { 0, 0 }, .redirected = false, .alpha_picts = NULL, - .fade_running = false, .fade_time = 0L, .ignore_head = NULL, .ignore_tail = NULL, @@ -3167,20 +2923,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;