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 <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2019-02-09 15:50:40 +00:00
parent e109bee380
commit a826e0ce79
No known key found for this signature in database
GPG Key ID: 37C999F617EA1A47
16 changed files with 573 additions and 555 deletions

View File

@ -3,11 +3,11 @@
#include <xcb/xcb.h>
#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) {
&reg_noframe);
pixman_region32_fini(&reg_noframe);
}
bi->blur(ps->backend_data, ps,
(double)w->opacity / OPAQUE, &reg_blur);
bi->blur(ps->backend_data, ps, w->opacity, &reg_blur);
pixman_region32_fini(&reg_blur);
}

View File

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

View File

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

View File

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

View File

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

View File

@ -4,9 +4,11 @@
#include <stdc-predef.h>
#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

View File

@ -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 <code>struct _win</code>.
*/
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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,7 +9,7 @@
/// Enumeration type to represent switches.
typedef enum {
OFF, // false
OFF = 0, // false
ON, // true
UNSET
} switch_t;

480
src/win.c
View File

@ -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 <code>struct _win</code>.
*/
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 :

View File

@ -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;
/// <code>WM_WINDOW_ROLE</code> 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;