Merge pull request #111 from yshui/winstate-refactor
Window state tracking refactor
This commit is contained in:
commit
ecb3050eaa
|
@ -3,11 +3,11 @@
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
#include "backend.h"
|
#include "backend.h"
|
||||||
#include "config.h"
|
|
||||||
#include "win.h"
|
|
||||||
#include "region.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "compiler.h"
|
#include "compiler.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "region.h"
|
||||||
|
#include "win.h"
|
||||||
|
|
||||||
backend_info_t *backend_list[NUM_BKEND] = {
|
backend_info_t *backend_list[NUM_BKEND] = {
|
||||||
[BKEND_XRENDER] = &xrender_backend,
|
[BKEND_XRENDER] = &xrender_backend,
|
||||||
|
@ -107,8 +107,7 @@ void paint_all_new(session_t *ps, win *const t, bool ignore_damage) {
|
||||||
®_noframe);
|
®_noframe);
|
||||||
pixman_region32_fini(®_noframe);
|
pixman_region32_fini(®_noframe);
|
||||||
}
|
}
|
||||||
bi->blur(ps->backend_data, ps,
|
bi->blur(ps->backend_data, ps, w->opacity, ®_blur);
|
||||||
(double)w->opacity / OPAQUE, ®_blur);
|
|
||||||
pixman_region32_fini(®_blur);
|
pixman_region32_fini(®_blur);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) ||
|
bool blend = default_is_frame_transparent(NULL, w, win_data) ||
|
||||||
default_is_win_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);
|
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,
|
// XXX Move shadow drawing into a separate function,
|
||||||
// also do shadow excluding outside of backend
|
// 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);
|
0, 0, 0, 0, w->widthb, w->heightb);
|
||||||
}
|
}
|
||||||
|
|
||||||
const double dopacity = get_opacity_percent(w);
|
|
||||||
if (w->frame_opacity != 1) {
|
if (w->frame_opacity != 1) {
|
||||||
// Handle transparent frame
|
// Handle transparent frame
|
||||||
// Step 1: clip paint area to 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
|
// Draw the frame with frame opacity
|
||||||
xcb_render_picture_t alpha_pict =
|
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);
|
x_set_picture_clip_region(ps->c, wd->rendered_pict, 0, 0, &frame_reg);
|
||||||
|
|
||||||
// Step 2: multiply alpha value
|
// 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;
|
double dim_opacity = ps->o.inactive_dim;
|
||||||
if (!ps->o.inactive_dim_fixed)
|
if (!ps->o.inactive_dim_fixed)
|
||||||
dim_opacity *= get_opacity_percent(w);
|
dim_opacity *= w->opacity;
|
||||||
|
|
||||||
xcb_render_color_t color = {
|
xcb_render_color_t color = {
|
||||||
.red = 0, .green = 0, .blue = 0, .alpha = 0xffff * dim_opacity};
|
.red = 0, .green = 0, .blue = 0, .alpha = 0xffff * dim_opacity};
|
||||||
|
|
20
src/c2.c
20
src/c2.c
|
@ -353,7 +353,7 @@ static xcb_atom_t
|
||||||
c2_get_atom_type(const c2_l_t *pleaf);
|
c2_get_atom_type(const c2_l_t *pleaf);
|
||||||
|
|
||||||
static bool
|
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.
|
* Parse a condition string.
|
||||||
|
@ -1365,7 +1365,7 @@ c2_get_atom_type(const c2_l_t *pleaf) {
|
||||||
* For internal use.
|
* For internal use.
|
||||||
*/
|
*/
|
||||||
static inline void
|
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) {
|
bool *pres, bool *perr) {
|
||||||
assert(pleaf);
|
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.
|
* @return true if matched, false otherwise.
|
||||||
*/
|
*/
|
||||||
static bool
|
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 result = false;
|
||||||
bool error = true;
|
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.
|
* @return true if matched, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
c2_match(session_t *ps, win *w, const c2_lptr_t *condlst,
|
c2_match(session_t *ps, const win *w, const c2_lptr_t *condlst, void **pdata) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then go through the whole linked list
|
// Then go through the whole linked list
|
||||||
for (; condlst; condlst = condlst->next) {
|
for (; condlst; condlst = condlst->next) {
|
||||||
if (c2_match_once(ps, w, condlst->ptr)) {
|
if (c2_match_once(ps, w, condlst->ptr)) {
|
||||||
if (cache)
|
|
||||||
*cache = condlst;
|
|
||||||
if (pdata)
|
if (pdata)
|
||||||
*pdata = condlst->data;
|
*pdata = condlst->data;
|
||||||
return true;
|
return true;
|
||||||
|
|
3
src/c2.h
3
src/c2.h
|
@ -21,7 +21,6 @@ c2_lptr_t *c2_parse(c2_lptr_t **pcondlst, const char *pattern, void *data);
|
||||||
|
|
||||||
c2_lptr_t *c2_free_lptr(c2_lptr_t *lp);
|
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,
|
bool c2_match(session_t *ps, const win *w, const c2_lptr_t *condlst, void **pdata);
|
||||||
void **pdata);
|
|
||||||
|
|
||||||
bool c2_list_postprocess(session_t *ps, c2_lptr_t *list);
|
bool c2_list_postprocess(session_t *ps, c2_lptr_t *list);
|
||||||
|
|
15
src/common.h
15
src/common.h
|
@ -98,7 +98,6 @@
|
||||||
#define ROUNDED_PERCENT 0.05
|
#define ROUNDED_PERCENT 0.05
|
||||||
#define ROUNDED_PIXELS 10
|
#define ROUNDED_PIXELS 10
|
||||||
|
|
||||||
#define OPAQUE 0xffffffff
|
|
||||||
#define REGISTER_PROP "_NET_WM_CM_S"
|
#define REGISTER_PROP "_NET_WM_CM_S"
|
||||||
|
|
||||||
#define TIME_MS_MAX LONG_MAX
|
#define TIME_MS_MAX LONG_MAX
|
||||||
|
@ -371,9 +370,6 @@ typedef struct session {
|
||||||
bool tmout_unredir_hit;
|
bool tmout_unredir_hit;
|
||||||
/// Whether we need to redraw the screen
|
/// Whether we need to redraw the screen
|
||||||
bool redraw_needed;
|
bool redraw_needed;
|
||||||
/// Whether the program is idling. I.e. no fading, no potential window
|
|
||||||
/// changes.
|
|
||||||
bool fade_running;
|
|
||||||
/// Program start time.
|
/// Program start time.
|
||||||
struct timeval time_start;
|
struct timeval time_start;
|
||||||
/// The region needs to painted on next paint.
|
/// The region needs to painted on next paint.
|
||||||
|
@ -687,11 +683,6 @@ timespec_subtract(struct timespec *result,
|
||||||
return x->tv_sec < y->tv_sec;
|
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.
|
* Get current time in struct timeval.
|
||||||
*/
|
*/
|
||||||
|
@ -792,8 +783,9 @@ find_win(session_t *ps, xcb_window_t id) {
|
||||||
win *w;
|
win *w;
|
||||||
|
|
||||||
for (w = ps->list; w; w = w->next) {
|
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 w;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -811,8 +803,9 @@ find_toplevel(session_t *ps, xcb_window_t id) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (win *w = ps->list; w; w = w->next) {
|
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 w;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -4,9 +4,11 @@
|
||||||
|
|
||||||
#include <stdc-predef.h>
|
#include <stdc-predef.h>
|
||||||
|
|
||||||
#define auto __auto_type
|
#define auto __auto_type
|
||||||
#define likely(x) __builtin_expect(!!(x), 1)
|
#define likely(x) __builtin_expect(!!(x), 1)
|
||||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||||
|
#define likely_if(x) if (likely(x))
|
||||||
|
#define unlikely_if(x) if (unlikely(x))
|
||||||
|
|
||||||
#ifndef __has_attribute
|
#ifndef __has_attribute
|
||||||
# if __GNUC__ >= 4
|
# if __GNUC__ >= 4
|
||||||
|
|
472
src/compton.c
472
src/compton.c
|
@ -59,12 +59,6 @@
|
||||||
(session_t *)((char *)__mptr - offsetof(session_t, member)); \
|
(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
|
static void
|
||||||
update_refresh_rate(session_t *ps);
|
update_refresh_rate(session_t *ps);
|
||||||
|
|
||||||
|
@ -91,9 +85,6 @@ redir_stop(session_t *ps);
|
||||||
static win *
|
static win *
|
||||||
recheck_focus(session_t *ps);
|
recheck_focus(session_t *ps);
|
||||||
|
|
||||||
static double
|
|
||||||
get_opacity_percent(win *w);
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
restack_win(session_t *ps, win *w, xcb_window_t new_above);
|
restack_win(session_t *ps, win *w, xcb_window_t new_above);
|
||||||
|
|
||||||
|
@ -169,25 +160,6 @@ free_xinerama_info(session_t *ps) {
|
||||||
#endif
|
#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.
|
* 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;
|
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
|
// XXX Move to x.c
|
||||||
static void
|
static void
|
||||||
cxinerama_upd_scrs(session_t *ps) {
|
cxinerama_upd_scrs(session_t *ps) {
|
||||||
|
@ -334,32 +277,46 @@ fade_timeout(session_t *ps) {
|
||||||
* Run fading on a window.
|
* Run fading on a window.
|
||||||
*
|
*
|
||||||
* @param steps steps of fading
|
* @param steps steps of fading
|
||||||
|
* @return whether we are still in fading mode
|
||||||
*/
|
*/
|
||||||
static void
|
static bool
|
||||||
run_fade(session_t *ps, win *w, unsigned steps) {
|
run_fade(session_t *ps, win **_w, unsigned steps) {
|
||||||
// If we have reached target opacity, return
|
win *w = *_w;
|
||||||
if (w->opacity == w->opacity_tgt) {
|
if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) {
|
||||||
return;
|
// 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;
|
w->opacity = w->opacity_tgt;
|
||||||
else if (steps) {
|
}
|
||||||
// Use double below because opacity_t will probably overflow during
|
if (w->opacity == w->opacity_tgt) {
|
||||||
// calculations
|
// We have reached target opacity, wrapping up.
|
||||||
if (w->opacity < w->opacity_tgt)
|
// Note, we reach here after we have rendered the window with the target
|
||||||
w->opacity = normalize_d_range(
|
// opacity at least once. If the window is destroyed because it is faded out,
|
||||||
(double) w->opacity + (double) ps->o.fade_in_step * steps,
|
// there is no need to add damage here.
|
||||||
0.0, w->opacity_tgt);
|
log_debug("Fading finished for window %#010x %s", w->id, w->name);
|
||||||
else
|
win_check_fade_finished(ps, _w);
|
||||||
w->opacity = normalize_d_range(
|
return false;
|
||||||
(double) w->opacity - (double) ps->o.fade_out_step * steps,
|
|
||||||
w->opacity_tgt, OPAQUE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (w->opacity != w->opacity_tgt) {
|
if (steps) {
|
||||||
ps->fade_running = true;
|
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 ===
|
// === Error handling ===
|
||||||
|
@ -516,8 +473,11 @@ find_client_win(session_t *ps, xcb_window_t w) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static win *
|
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;
|
win *t = NULL, *next = NULL;
|
||||||
|
*fade_running = false;
|
||||||
|
|
||||||
// Fading step calculation
|
// Fading step calculation
|
||||||
unsigned long steps = 0L;
|
unsigned long steps = 0L;
|
||||||
|
@ -539,40 +499,50 @@ paint_preprocess(session_t *ps, win *list) {
|
||||||
const bool was_painted = w->to_paint;
|
const bool was_painted = w->to_paint;
|
||||||
const opacity_t opacity_old = w->opacity;
|
const opacity_t opacity_old = w->opacity;
|
||||||
// Restore flags from last paint if the window is being faded out
|
// 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);
|
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_invert_color(ps, w, w->invert_color_last);
|
||||||
win_set_blur_background(ps, w, w->blur_background_last);
|
win_set_blur_background(ps, w, w->blur_background_last);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update window opacity target and dim state if asked
|
if (win_should_dim(ps, w) != w->dim) {
|
||||||
if (WFLAG_OPCT_CHANGE & w->flags) {
|
w->dim = win_should_dim(ps, w);
|
||||||
win_calc_opacity(ps, w);
|
add_damage_from_win(ps, w);
|
||||||
win_calc_dim(ps, w);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run fading
|
// 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;
|
w->frame_opacity = ps->o.frame_opacity;
|
||||||
else
|
} else {
|
||||||
w->frame_opacity = 1.0;
|
w->frame_opacity = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
// Update window mode
|
// Update window mode
|
||||||
win_determine_mode(ps, w);
|
win_determine_mode(ps, w);
|
||||||
|
|
||||||
// Destroy all reg_ignore above when frame opaque state changes on
|
// Destroy all reg_ignore above when frame opaque state changes on
|
||||||
// SOLID mode
|
// SOLID mode
|
||||||
if (was_painted && w->mode != mode_old)
|
if (was_painted && w->mode != mode_old) {
|
||||||
w->reg_ignore_valid = false;
|
w->reg_ignore_valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Add window to damaged area if its opacity changes
|
// 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, 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 == 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);
|
add_damage_from_win(ps, w);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opacity will not change, from now on.
|
// Opacity will not change, from now on.
|
||||||
|
@ -603,8 +573,8 @@ paint_preprocess(session_t *ps, win *list) {
|
||||||
if (!w->ever_damaged
|
if (!w->ever_damaged
|
||||||
|| w->g.x + w->g.width < 1 || w->g.y + w->g.height < 1
|
|| 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->g.x >= ps->root_width || w->g.y >= ps->root_height
|
||||||
|| ((w->a.map_state == XCB_MAP_STATE_UNMAPPED || w->destroying) && !w->paint.pixmap)
|
|| w->state == WSTATE_UNMAPPED
|
||||||
|| (double) w->opacity / OPAQUE * MAX_ALPHA < 1
|
|| (double) w->opacity * MAX_ALPHA < 1
|
||||||
|| w->paint_excluded)
|
|| w->paint_excluded)
|
||||||
to_paint = false;
|
to_paint = false;
|
||||||
//log_trace("%s %d %d %d", w->name, to_paint, w->opacity, w->paint_excluded);
|
//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;
|
goto skip_window;
|
||||||
|
|
||||||
// Calculate shadow opacity
|
// 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
|
// Generate ignore region for painting to reduce GPU load
|
||||||
if (!w->reg_ignore)
|
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;
|
reg_ignore_valid = reg_ignore_valid && w->reg_ignore_valid;
|
||||||
w->reg_ignore_valid = true;
|
w->reg_ignore_valid = true;
|
||||||
|
|
||||||
assert(w->destroying == (w->fade_callback == finish_destroy_win));
|
|
||||||
win_check_fade_finished(ps, &w);
|
win_check_fade_finished(ps, &w);
|
||||||
|
|
||||||
// Avoid setting w->to_paint if w is freed
|
// Avoid setting w->to_paint if w is freed
|
||||||
|
@ -683,7 +652,6 @@ paint_preprocess(session_t *ps, win *list) {
|
||||||
if (w->to_paint) {
|
if (w->to_paint) {
|
||||||
// Save flags
|
// Save flags
|
||||||
w->shadow_last = w->shadow;
|
w->shadow_last = w->shadow;
|
||||||
w->fade_last = w->fade;
|
|
||||||
w->invert_color_last = w->invert_color;
|
w->invert_color_last = w->invert_color;
|
||||||
w->blur_background_last = w->blur_background;
|
w->blur_background_last = w->blur_background;
|
||||||
}
|
}
|
||||||
|
@ -693,7 +661,7 @@ paint_preprocess(session_t *ps, win *list) {
|
||||||
rc_region_unref(&last_reg_ignore);
|
rc_region_unref(&last_reg_ignore);
|
||||||
|
|
||||||
// If possible, unredirect all windows and stop painting
|
// 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;
|
unredir_possible = !ps->o.redirected_force;
|
||||||
else if (ps->o.unredir_if_possible && is_highest && !ps->redirected)
|
else if (ps->o.unredir_if_possible && is_highest && !ps->redirected)
|
||||||
// If there's no window to paint, and the screen isn't 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);
|
add_damage(ps, &parts);
|
||||||
pixman_region32_fini(&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
|
static void
|
||||||
restack_win(session_t *ps, win *w, xcb_window_t new_above) {
|
restack_win(session_t *ps, win *w, xcb_window_t new_above) {
|
||||||
xcb_window_t old_above;
|
xcb_window_t old_above;
|
||||||
|
@ -986,7 +790,7 @@ restack_win(session_t *ps, win *w, xcb_window_t new_above) {
|
||||||
|
|
||||||
// rehook
|
// rehook
|
||||||
for (prev = &ps->list; *prev; prev = &(*prev)->next) {
|
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;
|
found = true;
|
||||||
break;
|
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) {
|
configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
|
||||||
// On root window changes
|
// On root window changes
|
||||||
if (ce->window == ps->root) {
|
if (ce->window == ps->root) {
|
||||||
|
@ -1131,7 +935,7 @@ configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
|
||||||
if (factor_change) {
|
if (factor_change) {
|
||||||
win_on_factor_change(ps, w);
|
win_on_factor_change(ps, w);
|
||||||
add_damage(ps, &damage);
|
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);
|
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
|
static inline void
|
||||||
root_damaged(session_t *ps) {
|
root_damaged(session_t *ps) {
|
||||||
if (ps->root_tile_paint.pixmap) {
|
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.
|
* Set w->fade_force of a window.
|
||||||
|
*
|
||||||
|
* Doesn't affect fading already in progress
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
win_set_fade_force(session_t *ps, win *w, switch_t val) {
|
win_set_fade_force(session_t *ps, win *w, switch_t val) {
|
||||||
if (val != w->fade_force) {
|
w->fade_force = val;
|
||||||
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.
|
* Set no_fading_openclose option.
|
||||||
|
*
|
||||||
|
* Don't affect fading already in progress
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
opts_set_no_fading_openclose(session_t *ps, bool newval) {
|
opts_set_no_fading_openclose(session_t *ps, bool newval) {
|
||||||
if (newval != ps->o.no_fading_openclose) {
|
ps->o.no_fading_openclose = newval;
|
||||||
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
|
inline static void
|
||||||
ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *ev) {
|
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
|
inline static void
|
||||||
ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) {
|
ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) {
|
||||||
map_win(ps, ev->window);
|
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
|
inline static void
|
||||||
ev_unmap_notify(session_t *ps, xcb_unmap_notify_event_t *ev) {
|
ev_unmap_notify(session_t *ps, xcb_unmap_notify_event_t *ev) {
|
||||||
win *w = find_win(ps, ev->window);
|
win *w = find_win(ps, ev->window);
|
||||||
|
if (w) {
|
||||||
if (w)
|
unmap_win(ps, &w, false);
|
||||||
unmap_win(ps, &w);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static void
|
inline static void
|
||||||
|
@ -1540,9 +1286,14 @@ ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t *ev) {
|
||||||
ev->parent, ev->override_redirect);
|
ev->parent, ev->override_redirect);
|
||||||
|
|
||||||
if (ev->parent == ps->root) {
|
if (ev->parent == ps->root) {
|
||||||
|
// new window
|
||||||
add_win(ps, ev->window, 0);
|
add_win(ps, ev->window, 0);
|
||||||
} else {
|
} 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
|
// Reset event mask in case something wrong happens
|
||||||
xcb_change_window_attributes(ps->c, ev->window, XCB_CW_EVENT_MASK,
|
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);
|
win *w = find_win(ps, ev->window) ?: find_toplevel(ps, ev->window);
|
||||||
if (w) {
|
if (w) {
|
||||||
win_update_opacity_prop(ps, 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
|
static void
|
||||||
redir_start(session_t *ps) {
|
redir_start(session_t *ps) {
|
||||||
if (!ps->redirected) {
|
if (!ps->redirected) {
|
||||||
log_trace("Screen redirected.");
|
log_debug("Screen redirected.");
|
||||||
|
|
||||||
// Map overlay window. Done firstly according to this:
|
// Map overlay window. Done firstly according to this:
|
||||||
// https://bugzilla.gnome.org/show_bug.cgi?id=597014
|
// https://bugzilla.gnome.org/show_bug.cgi?id=597014
|
||||||
|
@ -2262,7 +2019,7 @@ redir_start(session_t *ps) {
|
||||||
static void
|
static void
|
||||||
redir_stop(session_t *ps) {
|
redir_stop(session_t *ps) {
|
||||||
if (ps->redirected) {
|
if (ps->redirected) {
|
||||||
log_trace("Screen unredirected.");
|
log_debug("Screen unredirected.");
|
||||||
// Destroy all Pictures as they expire once windows are unredirected
|
// Destroy all Pictures as they expire once windows are unredirected
|
||||||
// If we don't destroy them here, looks like the resources are just
|
// If we don't destroy them here, looks like the resources are just
|
||||||
// kept inaccessible somehow
|
// kept inaccessible somehow
|
||||||
|
@ -2337,14 +2094,14 @@ _draw_callback(EV_P_ session_t *ps, int revents) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ps->fade_running = false;
|
bool fade_running = false;
|
||||||
win *t = paint_preprocess(ps, ps->list);
|
win *t = paint_preprocess(ps, ps->list, &fade_running);
|
||||||
ps->tmout_unredir_hit = false;
|
ps->tmout_unredir_hit = false;
|
||||||
|
|
||||||
// Start/stop fade timer depends on whether window are fading
|
// 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);
|
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_set(&ps->fade_timer, fade_timeout(ps), 0);
|
||||||
ev_timer_start(ps->loop, &ps->fade_timer);
|
ev_timer_start(ps->loop, &ps->fade_timer);
|
||||||
}
|
}
|
||||||
|
@ -2359,7 +2116,7 @@ _draw_callback(EV_P_ session_t *ps, int revents) {
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ps->fade_running)
|
if (!fade_running)
|
||||||
ps->fade_time = 0L;
|
ps->fade_time = 0L;
|
||||||
|
|
||||||
ps->redraw_needed = false;
|
ps->redraw_needed = false;
|
||||||
|
@ -2516,16 +2273,16 @@ session_init(int argc, char **argv, Display *dpy, const char *config_file,
|
||||||
.respect_prop_shadow = false,
|
.respect_prop_shadow = false,
|
||||||
.xinerama_shadow_crop = false,
|
.xinerama_shadow_crop = false,
|
||||||
|
|
||||||
.fade_in_step = 0.028 * OPAQUE,
|
.fade_in_step = 0.028,
|
||||||
.fade_out_step = 0.03 * OPAQUE,
|
.fade_out_step = 0.03,
|
||||||
.fade_delta = 10,
|
.fade_delta = 10,
|
||||||
.no_fading_openclose = false,
|
.no_fading_openclose = false,
|
||||||
.no_fading_destroyed_argb = false,
|
.no_fading_destroyed_argb = false,
|
||||||
.fade_blacklist = NULL,
|
.fade_blacklist = NULL,
|
||||||
|
|
||||||
.inactive_opacity = OPAQUE,
|
.inactive_opacity = 1.0,
|
||||||
.inactive_opacity_override = false,
|
.inactive_opacity_override = false,
|
||||||
.active_opacity = OPAQUE,
|
.active_opacity = 1.0,
|
||||||
.frame_opacity = 1.0,
|
.frame_opacity = 1.0,
|
||||||
.detect_client_opacity = false,
|
.detect_client_opacity = false,
|
||||||
|
|
||||||
|
@ -2552,7 +2309,6 @@ session_init(int argc, char **argv, Display *dpy, const char *config_file,
|
||||||
.time_start = { 0, 0 },
|
.time_start = { 0, 0 },
|
||||||
.redirected = false,
|
.redirected = false,
|
||||||
.alpha_picts = NULL,
|
.alpha_picts = NULL,
|
||||||
.fade_running = false,
|
|
||||||
.fade_time = 0L,
|
.fade_time = 0L,
|
||||||
.ignore_head = NULL,
|
.ignore_head = NULL,
|
||||||
.ignore_tail = NULL,
|
.ignore_tail = NULL,
|
||||||
|
@ -3167,20 +2923,16 @@ session_destroy(session_t *ps) {
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
session_run(session_t *ps) {
|
session_run(session_t *ps) {
|
||||||
win *t;
|
|
||||||
|
|
||||||
if (ps->o.sw_opti)
|
if (ps->o.sw_opti)
|
||||||
ps->paint_tm_offset = get_time_timeval().tv_usec;
|
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
|
// 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);
|
ev_idle_start(ps->loop, &ps->draw_idle);
|
||||||
|
} else {
|
||||||
|
// Let's draw our first frame!
|
||||||
|
queue_redraw(ps);
|
||||||
|
}
|
||||||
ev_run(ps->loop, 0);
|
ev_run(ps->loop, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,14 +148,4 @@ dump_drawable(session_t *ps, xcb_drawable_t drawable) {
|
||||||
free(r);
|
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 :
|
// vim: set et sw=2 :
|
||||||
|
|
11
src/config.h
11
src/config.h
|
@ -167,9 +167,9 @@ typedef struct options_t {
|
||||||
|
|
||||||
// === Fading ===
|
// === Fading ===
|
||||||
/// How much to fade in in a single fading step.
|
/// 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.
|
/// How much to fade out in a single fading step.
|
||||||
opacity_t fade_out_step;
|
double fade_out_step;
|
||||||
/// Fading time delta. In milliseconds.
|
/// Fading time delta. In milliseconds.
|
||||||
unsigned long fade_delta;
|
unsigned long fade_delta;
|
||||||
/// Whether to disable fading on window open/close.
|
/// Whether to disable fading on window open/close.
|
||||||
|
@ -181,11 +181,10 @@ typedef struct options_t {
|
||||||
|
|
||||||
// === Opacity ===
|
// === Opacity ===
|
||||||
/// Default opacity for inactive windows.
|
/// Default opacity for inactive windows.
|
||||||
/// 32-bit integer with the format of _NET_WM_OPACITY. 0 stands for
|
/// 32-bit integer with the format of _NET_WM_OPACITY.
|
||||||
/// not enabled, default.
|
double inactive_opacity;
|
||||||
opacity_t inactive_opacity;
|
|
||||||
/// Default opacity for inactive windows.
|
/// Default opacity for inactive windows.
|
||||||
opacity_t active_opacity;
|
double active_opacity;
|
||||||
/// Whether inactive_opacity overrides the opacity set by window
|
/// Whether inactive_opacity overrides the opacity set by window
|
||||||
/// attributes.
|
/// attributes.
|
||||||
bool inactive_opacity_override;
|
bool inactive_opacity_override;
|
||||||
|
|
|
@ -218,10 +218,10 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
|
||||||
opt->fade_delta = ival;
|
opt->fade_delta = ival;
|
||||||
// -I (fade_in_step)
|
// -I (fade_in_step)
|
||||||
if (config_lookup_float(&cfg, "fade-in-step", &dval))
|
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)
|
// -O (fade_out_step)
|
||||||
if (config_lookup_float(&cfg, "fade-out-step", &dval))
|
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)
|
// -r (shadow_radius)
|
||||||
config_lookup_int(&cfg, "shadow-radius", &opt->shadow_radius);
|
config_lookup_int(&cfg, "shadow-radius", &opt->shadow_radius);
|
||||||
// -o (shadow_opacity)
|
// -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);
|
config_lookup_int(&cfg, "shadow-offset-y", &opt->shadow_offset_y);
|
||||||
// -i (inactive_opacity)
|
// -i (inactive_opacity)
|
||||||
if (config_lookup_float(&cfg, "inactive-opacity", &dval))
|
if (config_lookup_float(&cfg, "inactive-opacity", &dval))
|
||||||
opt->inactive_opacity = normalize_d(dval) * OPAQUE;
|
opt->inactive_opacity = normalize_d(dval);
|
||||||
// --active_opacity
|
// --active_opacity
|
||||||
if (config_lookup_float(&cfg, "active-opacity", &dval))
|
if (config_lookup_float(&cfg, "active-opacity", &dval))
|
||||||
opt->active_opacity = normalize_d(dval) * OPAQUE;
|
opt->active_opacity = normalize_d(dval);
|
||||||
// -e (frame_opacity)
|
// -e (frame_opacity)
|
||||||
config_lookup_float(&cfg, "frame-opacity", &opt->frame_opacity);
|
config_lookup_float(&cfg, "frame-opacity", &opt->frame_opacity);
|
||||||
// -c (shadow_enable)
|
// -c (shadow_enable)
|
||||||
|
|
11
src/dbus.c
11
src/dbus.c
|
@ -493,8 +493,9 @@ cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data) {
|
||||||
// Get the number of wids we are to include
|
// Get the number of wids we are to include
|
||||||
unsigned count = 0;
|
unsigned count = 0;
|
||||||
for (win *w = ps->list; w; w = w->next) {
|
for (win *w = ps->list; w; w = w->next) {
|
||||||
if (!w->destroying)
|
if (w->state != WSTATE_DESTROYING) {
|
||||||
++count;
|
++count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!count) {
|
if (!count) {
|
||||||
|
@ -509,7 +510,7 @@ cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data) {
|
||||||
{
|
{
|
||||||
cdbus_window_t *pcur = arr;
|
cdbus_window_t *pcur = arr;
|
||||||
for (win *w = ps->list; w; w = w->next) {
|
for (win *w = ps->list; w; w = w->next) {
|
||||||
if (!w->destroying) {
|
if (w->state != WSTATE_DESTROYING) {
|
||||||
*pcur = w->id;
|
*pcur = w->id;
|
||||||
++pcur;
|
++pcur;
|
||||||
assert(pcur <= arr + count);
|
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(mode, cdbus_reply_enum);
|
||||||
cdbus_m_win_get_do(client_win, cdbus_reply_wid);
|
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(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(window_type, cdbus_reply_enum);
|
||||||
cdbus_m_win_get_do(wmwin, cdbus_reply_bool);
|
cdbus_m_win_get_do(wmwin, cdbus_reply_bool);
|
||||||
cdbus_m_win_get_do(leader, cdbus_reply_wid);
|
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(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(invert_color, cdbus_reply_bool);
|
||||||
cdbus_m_win_get_do(blur_background, cdbus_reply_bool);
|
cdbus_m_win_get_do(blur_background, cdbus_reply_bool);
|
||||||
#undef cdbus_m_win_get_do
|
#undef cdbus_m_win_get_do
|
||||||
|
@ -1140,7 +1139,7 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) {
|
||||||
double val = 0.0;
|
double val = 0.0;
|
||||||
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val))
|
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val))
|
||||||
return false;
|
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;
|
goto cdbus_process_opts_set_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1149,7 +1148,7 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) {
|
||||||
double val = 0.0;
|
double val = 0.0;
|
||||||
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val))
|
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val))
|
||||||
return false;
|
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;
|
goto cdbus_process_opts_set_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
// These options are handled by get_early_config()
|
||||||
break;
|
break;
|
||||||
P_CASELONG('D', fade_delta);
|
P_CASELONG('D', fade_delta);
|
||||||
case 'I': opt->fade_in_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)) * OPAQUE; break;
|
case 'O': opt->fade_out_step = normalize_d(atof(optarg)); break;
|
||||||
case 'c': shadow_enable = true; break;
|
case 'c': shadow_enable = true; break;
|
||||||
case 'C':
|
case 'C':
|
||||||
winopt_mask[WINTYPE_DOCK].shadow = true;
|
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('l', shadow_offset_x);
|
||||||
P_CASELONG('t', shadow_offset_y);
|
P_CASELONG('t', shadow_offset_y);
|
||||||
case 'i':
|
case 'i':
|
||||||
opt->inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE);
|
opt->inactive_opacity = normalize_d(atof(optarg));
|
||||||
break;
|
break;
|
||||||
case 'e': opt->frame_opacity = atof(optarg); break;
|
case 'e': opt->frame_opacity = atof(optarg); break;
|
||||||
case 'z':
|
case 'z':
|
||||||
|
@ -718,7 +718,7 @@ void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
||||||
break;
|
break;
|
||||||
case 297:
|
case 297:
|
||||||
// --active-opacity
|
// --active-opacity
|
||||||
opt->active_opacity = (normalize_d(atof(optarg)) * OPAQUE);
|
opt->active_opacity = normalize_d(atof(optarg));
|
||||||
break;
|
break;
|
||||||
P_CASEBOOL(298, glx_no_rebind_pixmap);
|
P_CASEBOOL(298, glx_no_rebind_pixmap);
|
||||||
case 299:
|
case 299:
|
||||||
|
|
12
src/render.c
12
src/render.c
|
@ -307,10 +307,8 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const double dopacity = get_opacity_percent(w);
|
|
||||||
|
|
||||||
if (w->frame_opacity == 1) {
|
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 {
|
} else {
|
||||||
// Painting parameters
|
// Painting parameters
|
||||||
const margin_t extents = win_calc_frame_extents(w);
|
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;
|
const int r = extents.right;
|
||||||
|
|
||||||
#define COMP_BDR(cx, cy, cwid, chei) \
|
#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)
|
reg_paint, pict)
|
||||||
|
|
||||||
// Sanitize the margins, in case some broken WM makes
|
// 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
|
// body
|
||||||
paint_region(ps, w, cleft, ctop, body_width, body_height,
|
paint_region(ps, w, cleft, ctop, body_width, body_height,
|
||||||
dopacity, reg_paint, pict);
|
w->opacity, reg_paint, pict);
|
||||||
} while (0);
|
} while (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,7 +383,7 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
|
||||||
if (w->dim) {
|
if (w->dim) {
|
||||||
double dim_opacity = ps->o.inactive_dim;
|
double dim_opacity = ps->o.inactive_dim;
|
||||||
if (!ps->o.inactive_dim_fixed)
|
if (!ps->o.inactive_dim_fixed)
|
||||||
dim_opacity *= get_opacity_percent(w);
|
dim_opacity *= w->opacity;
|
||||||
|
|
||||||
switch (ps->o.backend) {
|
switch (ps->o.backend) {
|
||||||
case BKEND_XRENDER:
|
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
|
// Adjust blur strength according to window opacity, to make it appear
|
||||||
// better during fading
|
// better during fading
|
||||||
if (!ps->o.blur_background_fixed) {
|
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);
|
factor_center = pct * 8.0 / (1.1 - pct);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
/// Enumeration type to represent switches.
|
/// Enumeration type to represent switches.
|
||||||
typedef enum {
|
typedef enum {
|
||||||
OFF, // false
|
OFF = 0, // false
|
||||||
ON, // true
|
ON, // true
|
||||||
UNSET
|
UNSET
|
||||||
} switch_t;
|
} switch_t;
|
||||||
|
|
480
src/win.c
480
src/win.c
|
@ -30,8 +30,15 @@
|
||||||
#include "dbus.h"
|
#include "dbus.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_OPENGL
|
||||||
|
// TODO remove this include
|
||||||
|
#include "opengl.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "win.h"
|
#include "win.h"
|
||||||
|
|
||||||
|
#define OPAQUE 0xffffffff
|
||||||
|
|
||||||
/// Generate a "return by value" function, from a function that returns the
|
/// Generate a "return by value" function, from a function that returns the
|
||||||
/// region via a region_t pointer argument.
|
/// region via a region_t pointer argument.
|
||||||
/// Function signature has to be (win *, region_t *)
|
/// Function signature has to be (win *, region_t *)
|
||||||
|
@ -74,7 +81,7 @@ group_update_focused(session_t *ps, xcb_window_t leader) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (win *w = ps->list; w; w = w->next) {
|
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);
|
win_update_focused(ps, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +100,7 @@ group_is_focused(session_t *ps, xcb_window_t leader) {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (win *w = ps->list; w; w = w->next) {
|
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))
|
&& win_is_focused_real(ps, w))
|
||||||
return true;
|
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
|
// 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 &&
|
return w->pictfmt &&
|
||||||
w->pictfmt->type == XCB_RENDER_PICT_TYPE_DIRECT &&
|
w->pictfmt->type == XCB_RENDER_PICT_TYPE_DIRECT &&
|
||||||
w->pictfmt->direct.alpha_mask;
|
w->pictfmt->direct.alpha_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
void win_determine_mode(session_t *ps, win *w) {
|
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;
|
w->mode = WMODE_TRANS;
|
||||||
} else if (w->frame_opacity != 1.0) {
|
} else if (w->frame_opacity != 1.0) {
|
||||||
w->mode = WMODE_FRAME_TRANS;
|
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
|
* If window is inactive and inactive_opacity_override is set, the
|
||||||
* priority is: (Simulates the old behavior)
|
* priority is: (Simulates the old behavior)
|
||||||
|
@ -325,78 +332,81 @@ void win_determine_mode(session_t *ps, win *w) {
|
||||||
*
|
*
|
||||||
* @param ps current session
|
* @param ps current session
|
||||||
* @param w struct _win object representing the window
|
* @param w struct _win object representing the window
|
||||||
|
*
|
||||||
|
* @return target opacity
|
||||||
*/
|
*/
|
||||||
void win_calc_opacity(session_t *ps, win *w) {
|
double win_get_opacity_target(session_t *ps, const win *w) {
|
||||||
opacity_t opacity = OPAQUE;
|
double opacity = 1;
|
||||||
|
|
||||||
if (w->destroying || w->a.map_state != XCB_MAP_STATE_VIEWABLE)
|
if (w->state == WSTATE_UNMAPPED) {
|
||||||
opacity = 0;
|
// be consistent
|
||||||
else {
|
return 0;
|
||||||
// Try obeying opacity property and window type opacity firstly
|
}
|
||||||
if (w->has_opacity_prop)
|
if (w->state == WSTATE_UNMAPPING || w->state == WSTATE_DESTROYING) {
|
||||||
opacity = w->opacity_prop;
|
return 0;
|
||||||
else if (!safe_isnan(ps->o.wintype_option[w->window_type].opacity))
|
}
|
||||||
opacity = ps->o.wintype_option[w->window_type].opacity * OPAQUE;
|
// Try obeying opacity property and window type opacity firstly
|
||||||
else {
|
if (w->has_opacity_prop) {
|
||||||
// Respect active_opacity only when the window is physically focused
|
opacity = ((double)w->opacity_prop) / OPAQUE;
|
||||||
if (win_is_focused_real(ps, w))
|
} else if (!safe_isnan(ps->o.wintype_option[w->window_type].opacity)) {
|
||||||
opacity = ps->o.active_opacity;
|
opacity = ps->o.wintype_option[w->window_type].opacity;
|
||||||
else if (false == w->focused)
|
} else {
|
||||||
// Respect inactive_opacity in some cases
|
// Respect active_opacity only when the window is physically focused
|
||||||
opacity = ps->o.inactive_opacity;
|
if (win_is_focused_real(ps, w))
|
||||||
}
|
opacity = ps->o.active_opacity;
|
||||||
|
else if (!w->focused)
|
||||||
// respect inactive override
|
// Respect inactive_opacity in some cases
|
||||||
if (ps->o.inactive_opacity_override && false == w->focused)
|
|
||||||
opacity = ps->o.inactive_opacity;
|
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.
|
* Determine whether a window is to be dimmed.
|
||||||
*/
|
*/
|
||||||
void win_calc_dim(session_t *ps, win *w) {
|
bool win_should_dim(session_t *ps, const win *w) {
|
||||||
bool dim;
|
|
||||||
|
|
||||||
// Make sure we do nothing if the window is unmapped / being destroyed
|
// Make sure we do nothing if the window is unmapped / being destroyed
|
||||||
if (w->destroying || w->a.map_state != XCB_MAP_STATE_VIEWABLE)
|
if (w->state == WSTATE_UNMAPPED) {
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
if (ps->o.inactive_dim && !(w->focused)) {
|
|
||||||
dim = true;
|
|
||||||
} else {
|
|
||||||
dim = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dim != w->dim) {
|
if (ps->o.inactive_dim && !(w->focused)) {
|
||||||
w->dim = dim;
|
return true;
|
||||||
add_damage_from_win(ps, w);
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if a window should fade on opacity change.
|
* 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
|
// To prevent it from being overwritten by last-paint value if the window is
|
||||||
// unmapped on next frame, write w->fade_last as well
|
// unmapped on next frame, write w->fade_last as well
|
||||||
if (UNSET != w->fade_force)
|
if (w->fade_force != UNSET) {
|
||||||
w->fade_last = w->fade = w->fade_force;
|
return w->fade_force;
|
||||||
else if (ps->o.no_fading_openclose && w->in_openclose)
|
}
|
||||||
w->fade_last = w->fade = false;
|
if (ps->o.no_fading_openclose && w->in_openclose) {
|
||||||
else if (ps->o.no_fading_destroyed_argb && w->destroying &&
|
return false;
|
||||||
win_has_alpha(w) && w->client_win && w->client_win != w->id) {
|
}
|
||||||
w->fade_last = w->fade = 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
|
// Ignore other possible causes of fading state changes after window
|
||||||
// gets unmapped
|
// gets unmapped
|
||||||
else if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
|
//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;
|
if (c2_match(ps, w, ps->o.fade_blacklist, NULL)) {
|
||||||
else
|
return false;
|
||||||
w->fade = ps->o.wintype_option[w->window_type].fade;
|
}
|
||||||
|
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;
|
shadow_new = w->shadow_force;
|
||||||
else if (w->a.map_state == XCB_MAP_STATE_VIEWABLE)
|
else if (w->a.map_state == XCB_MAP_STATE_VIEWABLE)
|
||||||
shadow_new = (ps->o.wintype_option[w->window_type].shadow &&
|
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 &&
|
!(ps->o.shadow_ignore_shaped && w->bounding_shaped &&
|
||||||
!w->rounded_corners) &&
|
!w->rounded_corners) &&
|
||||||
!(ps->o.respect_prop_shadow && 0 == w->prop_shadow));
|
!(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;
|
invert_color_new = w->invert_color_force;
|
||||||
else if (w->a.map_state == XCB_MAP_STATE_VIEWABLE)
|
else if (w->a.map_state == XCB_MAP_STATE_VIEWABLE)
|
||||||
invert_color_new =
|
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);
|
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 =
|
bool blur_background_new =
|
||||||
ps->o.blur_background &&
|
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);
|
win_set_blur_background(ps, w, blur_background_new);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update window opacity according to opacity rules.
|
* 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) {
|
void win_update_opacity_rule(session_t *ps, win *w) {
|
||||||
if (w->a.map_state != XCB_MAP_STATE_VIEWABLE)
|
if (w->a.map_state != XCB_MAP_STATE_VIEWABLE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
opacity_t opacity = OPAQUE;
|
double opacity = 1.0;
|
||||||
bool is_set = false;
|
bool is_set = false;
|
||||||
void *val = NULL;
|
void *val = NULL;
|
||||||
if (c2_match(ps, w, ps->o.opacity_rules, &w->cache_oparule, &val)) {
|
if (c2_match(ps, w, ps->o.opacity_rules, &val)) {
|
||||||
opacity = ((double)(long)val) / 100.0 * OPAQUE;
|
opacity = ((double)(long)val) / 100.0;
|
||||||
is_set = true;
|
is_set = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,7 +561,7 @@ void win_update_opacity_rule(session_t *ps, win *w) {
|
||||||
if (!is_set)
|
if (!is_set)
|
||||||
wid_rm_opacity_prop(ps, w->id);
|
wid_rm_opacity_prop(ps, w->id);
|
||||||
else
|
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) {
|
void win_on_wtype_change(session_t *ps, win *w) {
|
||||||
win_determine_shadow(ps, w);
|
win_determine_shadow(ps, w);
|
||||||
win_determine_fade(ps, w);
|
|
||||||
win_update_focused(ps, w);
|
win_update_focused(ps, w);
|
||||||
if (ps->o.invert_color_list)
|
if (ps->o.invert_color_list)
|
||||||
win_determine_invert_color(ps, w);
|
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.
|
* Function to be called on window data changes.
|
||||||
|
*
|
||||||
|
* TODO need better name
|
||||||
*/
|
*/
|
||||||
void win_on_factor_change(session_t *ps, win *w) {
|
void win_on_factor_change(session_t *ps, win *w) {
|
||||||
if (ps->o.shadow_blacklist)
|
if (ps->o.shadow_blacklist)
|
||||||
win_determine_shadow(ps, w);
|
win_determine_shadow(ps, w);
|
||||||
if (ps->o.fade_blacklist)
|
|
||||||
win_determine_fade(ps, w);
|
|
||||||
if (ps->o.invert_color_list)
|
if (ps->o.invert_color_list)
|
||||||
win_determine_invert_color(ps, w);
|
win_determine_invert_color(ps, w);
|
||||||
if (ps->o.focus_blacklist)
|
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);
|
win_update_opacity_rule(ps, w);
|
||||||
if (w->a.map_state == XCB_MAP_STATE_VIEWABLE && ps->o.paint_blacklist)
|
if (w->a.map_state == XCB_MAP_STATE_VIEWABLE && ps->o.paint_blacklist)
|
||||||
w->paint_excluded =
|
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)
|
if (w->a.map_state == XCB_MAP_STATE_VIEWABLE && ps->o.unredir_if_possible_blacklist)
|
||||||
w->unredir_if_possible_excluded = c2_match(
|
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;
|
w->reg_ignore_valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -733,6 +745,31 @@ void win_recheck_client(session_t *ps, win *w) {
|
||||||
win_mark_client(ps, w, cw);
|
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)
|
// 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) {
|
bool add_win(session_t *ps, xcb_window_t id, xcb_window_t prev) {
|
||||||
static const win win_def = {
|
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,
|
.widthb = 0,
|
||||||
.heightb = 0,
|
.heightb = 0,
|
||||||
.destroying = false,
|
.state = WSTATE_UNMAPPED,
|
||||||
.bounding_shaped = false,
|
.bounding_shaped = false,
|
||||||
.rounded_corners = false,
|
.rounded_corners = false,
|
||||||
.to_paint = 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,
|
.client_win = XCB_NONE,
|
||||||
.window_type = WINTYPE_UNKNOWN,
|
.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_instance = NULL,
|
||||||
.class_general = NULL,
|
.class_general = NULL,
|
||||||
.role = NULL,
|
.role = NULL,
|
||||||
.cache_sblst = NULL,
|
|
||||||
.cache_fblst = NULL,
|
|
||||||
.cache_fcblst = NULL,
|
|
||||||
.cache_ivclst = NULL,
|
|
||||||
.cache_bbblst = NULL,
|
|
||||||
.cache_oparule = NULL,
|
|
||||||
|
|
||||||
.opacity = 0,
|
.opacity = 0,
|
||||||
.opacity_tgt = 0,
|
.opacity_tgt = 0,
|
||||||
.has_opacity_prop = false,
|
.has_opacity_prop = false,
|
||||||
.opacity_prop = OPAQUE,
|
.opacity_prop = OPAQUE,
|
||||||
.opacity_is_set = false,
|
.opacity_is_set = false,
|
||||||
.opacity_set = OPAQUE,
|
.opacity_set = 1,
|
||||||
|
|
||||||
.fade = false,
|
|
||||||
.fade_force = UNSET,
|
.fade_force = UNSET,
|
||||||
.fade_callback = NULL,
|
|
||||||
|
|
||||||
.frame_opacity = 1.0,
|
.frame_opacity = 1.0,
|
||||||
.frame_extents = MARGIN_INIT,
|
.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;
|
win **p = NULL;
|
||||||
if (prev) {
|
if (prev) {
|
||||||
for (p = &ps->list; *p; p = &(*p)->next) {
|
for (p = &ps->list; *p; p = &(*p)->next) {
|
||||||
if ((*p)->id == prev && !(*p)->destroying)
|
if ((*p)->id == prev && (*p)->state != WSTATE_DESTROYING)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
assert(map_state == XCB_MAP_STATE_VIEWABLE || map_state == XCB_MAP_STATE_UNMAPPED);
|
||||||
new->a.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
|
// Create Damage for window
|
||||||
new->damage = xcb_generate_id(ps->c);
|
new->damage = xcb_generate_id(ps->c);
|
||||||
xcb_generic_error_t *e = xcb_request_check(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 &&
|
|| (ps->o.mark_ovredir_focused &&
|
||||||
w->id == w->client_win && !w->wmwin)
|
w->id == w->client_win && !w->wmwin)
|
||||||
|| (w->a.map_state == XCB_MAP_STATE_VIEWABLE &&
|
|| (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;
|
w->focused = true;
|
||||||
|
|
||||||
// If window grouping detection is enabled, mark the window active if
|
// 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
|
// Always recalculate the window target opacity, since some opacity-related
|
||||||
// options depend on the output value of win_is_focused_real() instead of
|
// options depend on the output value of win_is_focused_real() instead of
|
||||||
// w->focused
|
// 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) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static void
|
||||||
* Set fade callback of a window, and possibly execute the previous
|
finish_unmap_win(session_t *ps, win **_w) {
|
||||||
* 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) {
|
|
||||||
win *w = *_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;
|
free_paint(ps, &w->paint);
|
||||||
// Must be the last line as the callback could destroy w!
|
free_paint(ps, &w->shadow_paint);
|
||||||
if (exec_callback && old_callback)
|
}
|
||||||
old_callback(ps, _w);
|
|
||||||
|
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
|
void
|
||||||
win_check_fade_finished(session_t *ps, win **_w) {
|
win_check_fade_finished(session_t *ps, win **_w) {
|
||||||
win *w = *_w;
|
win *w = *_w;
|
||||||
if (w->fade_callback && w->opacity == w->opacity_tgt) {
|
if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) {
|
||||||
// Must be the last line as the callback could destroy w!
|
// No fading in progress
|
||||||
win_set_fade_callback(ps, _w, NULL, true);
|
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 :
|
||||||
|
|
92
src/win.h
92
src/win.h
|
@ -64,6 +64,46 @@ typedef enum {
|
||||||
WMODE_SOLID, // The window is opaque including the frame
|
WMODE_SOLID, // The window is opaque including the frame
|
||||||
} winmode_t;
|
} 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
|
* About coordinate systems
|
||||||
*
|
*
|
||||||
|
@ -88,6 +128,9 @@ struct win {
|
||||||
// Core members
|
// Core members
|
||||||
/// ID of the top-level frame window.
|
/// ID of the top-level frame window.
|
||||||
xcb_window_t id;
|
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.
|
/// Window attributes.
|
||||||
xcb_get_window_attributes_reply_t a;
|
xcb_get_window_attributes_reply_t a;
|
||||||
xcb_get_geometry_reply_t g;
|
xcb_get_geometry_reply_t g;
|
||||||
|
@ -175,20 +218,12 @@ struct win {
|
||||||
char *class_general;
|
char *class_general;
|
||||||
/// <code>WM_WINDOW_ROLE</code> value of the window.
|
/// <code>WM_WINDOW_ROLE</code> value of the window.
|
||||||
char *role;
|
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
|
// Opacity-related members
|
||||||
/// Current window opacity.
|
/// Current window opacity.
|
||||||
opacity_t opacity;
|
double opacity;
|
||||||
/// Target window opacity.
|
/// Target window opacity.
|
||||||
opacity_t opacity_tgt;
|
double opacity_tgt;
|
||||||
/// true if window (or client window, for broken window managers
|
/// true if window (or client window, for broken window managers
|
||||||
/// not transferring client window's _NET_WM_OPACITY value) has opacity prop
|
/// not transferring client window's _NET_WM_OPACITY value) has opacity prop
|
||||||
bool has_opacity_prop;
|
bool has_opacity_prop;
|
||||||
|
@ -197,18 +232,11 @@ struct win {
|
||||||
/// true if opacity is set by some rules
|
/// true if opacity is set by some rules
|
||||||
bool opacity_is_set;
|
bool opacity_is_set;
|
||||||
/// Last window opacity value we set.
|
/// Last window opacity value we set.
|
||||||
opacity_t opacity_set;
|
double opacity_set;
|
||||||
|
|
||||||
// Fading-related members
|
// 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.
|
/// Override value of window fade state. Set by D-Bus method calls.
|
||||||
switch_t fade_force;
|
switch_t fade_force;
|
||||||
/// Callback to be called after fading completed.
|
|
||||||
void (*fade_callback) (session_t *ps, win **w);
|
|
||||||
|
|
||||||
// Frame-opacity-related members
|
// Frame-opacity-related members
|
||||||
/// Current window frame opacity. Affected by window opacity.
|
/// 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.
|
* Set real focused state of a window.
|
||||||
*/
|
*/
|
||||||
void win_set_focused(session_t *ps, win *w, bool focused);
|
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_raw(session_t *ps, win *w);
|
||||||
void win_update_prop_shadow(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);
|
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);
|
void win_recheck_client(session_t *ps, win *w);
|
||||||
xcb_window_t win_get_leader_raw(session_t *ps, win *w, int recursions);
|
xcb_window_t win_get_leader_raw(session_t *ps, win *w, int recursions);
|
||||||
bool win_get_class(session_t *ps, win *w);
|
bool win_get_class(session_t *ps, win *w);
|
||||||
void win_calc_opacity(session_t *ps, win *w);
|
double attr_const win_get_opacity_target(session_t *ps, const win *w);
|
||||||
void win_calc_dim(session_t *ps, 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.
|
* Reread opacity property of a window.
|
||||||
*/
|
*/
|
||||||
|
@ -336,18 +367,8 @@ region_t win_get_region_noframe_local_by_val(win *w);
|
||||||
void
|
void
|
||||||
win_update_frame_extents(session_t *ps, win *w, xcb_window_t client);
|
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);
|
bool add_win(session_t *ps, xcb_window_t id, xcb_window_t prev);
|
||||||
|
/// Unmap or destroy a window
|
||||||
/**
|
void unmap_win(session_t *ps, win **, bool destroy);
|
||||||
* 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);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute fade callback of a window if fading finished.
|
* 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
|
/// 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
|
/// check if reg_ignore_valid is true for all windows above us
|
||||||
bool win_is_region_ignore_valid(session_t *ps, win *w);
|
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
|
static inline region_t
|
||||||
win_get_bounding_shape_global_by_val(win *w) {
|
win_get_bounding_shape_global_by_val(win *w) {
|
||||||
region_t ret;
|
region_t ret;
|
||||||
|
|
Loading…
Reference in New Issue