Merge pull request #111 from yshui/winstate-refactor

Window state tracking refactor
This commit is contained in:
yshui 2019-02-17 16:56:27 +00:00 committed by GitHub
commit ecb3050eaa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 596 additions and 572 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
@ -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,9 +783,10 @@ 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,9 +803,10 @@ 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

@ -7,6 +7,8 @@
#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) {
@ -334,34 +277,48 @@ 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 ===
static void
@ -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,41 +499,51 @@ 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.
rc_region_t *last_reg_ignore = rc_region_new();
@ -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);
}
}
/**
@ -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);
}
}
//!@}
@ -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);
}

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,9 +493,10 @@ 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) {
// Nothing to append
@ -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;

462
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 {
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 = 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 {
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 (false == w->focused)
else if (!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 (ps->o.inactive_opacity_override && !w->focused)
opacity = ps->o.inactive_opacity;
}
w->opacity_tgt = 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 &&
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) {
w->fade_last = w->fade = false;
// 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;