Feature #69: Blur window background

- Add window background blur support (--blur-background &
  --blur-background-frame), with X Render convolution filter.
  The performance sucks. The performance when the window is opaque but
  frame is transparent could be improved, but there are two possible
  ways and I'm hesitating.

- Known issue: The blurring effect looks very ungraceful during fading.
  I could partially fix the problem, but it probably isn't easy to fix
  it completely.
This commit is contained in:
Richard Grenville 2012-12-14 20:32:46 +08:00
parent a77aaf0718
commit 22cabf7c89
3 changed files with 155 additions and 2 deletions

View File

@ -22,6 +22,8 @@ inactive-opacity-override = false;
alpha-step = 0.06;
# inactive-dim = 0.2;
# inactive-dim-fixed = true;
# blur-background = true;
# blur-background-frame = true;
# Fading
fading = true;

View File

@ -1422,6 +1422,66 @@ win_paint_shadow(session_t *ps, win *w, Picture tgt_buffer) {
w->shadow_width, w->shadow_height);
}
/**
* Blur the background of a window.
*/
static inline void
win_blur_background(session_t *ps, win *w, Picture tgt_buffer,
XserverRegion reg_paint) {
// Convolution filter parameter (box blur)
// gaussian or binomial filters are definitely superior, yet looks
// like they aren't supported as of xorg-server-1.13.0
const static XFixed convolution_blur[] = {
// Must convert to XFixed with XDoubleToFixed()
// Matrix size
XDoubleToFixed(3), XDoubleToFixed(3),
// Matrix
XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1),
XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1),
XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1),
};
// Extra pixels we have to get for the blur to work correctly.
const static int expand = 1;
Pixmap tmp_pixmap = None;
Picture tmp_picture = None;
int x = w->a.x;
int y = w->a.y;
int wid = w->widthb;
int hei = w->heightb;
int xe = x - expand;
int ye = y - expand;
int wide = wid + expand * 2;
int heie = hei + expand * 2;
// Directly copying from tgt_buffer does not work, so we create a
// Picture in the middle. We expand the region slightly, to make sure
// the blur on border pixels work correctly.
tmp_pixmap = XCreatePixmap(ps->dpy, ps->root, wide, heie, ps->depth);
if (!tmp_pixmap)
goto win_blur_background_err;
tmp_picture = XRenderCreatePicture(ps->dpy, tmp_pixmap,
XRenderFindVisualFormat(ps->dpy, ps->vis), 0, 0);
if (!tmp_picture)
goto win_blur_background_err;
// Copy the content to tmp_picture, then copy back.
// We lift the PictureClipRegion here, to get the expanded pixels.
XFixesSetPictureClipRegion(ps->dpy, tgt_buffer, 0, 0, None);
XRenderComposite(ps->dpy, PictOpSrc, tgt_buffer, None, tmp_picture, xe, ye, 0, 0, 0, 0, wide, heie);
XFixesSetPictureClipRegion(ps->dpy, tgt_buffer, 0, 0, reg_paint);
XRenderSetPictureFilter(ps->dpy, tmp_picture, XRFILTER_CONVOLUTION, (XFixed *) convolution_blur, sizeof(convolution_blur) / sizeof(XFixed));
XRenderComposite(ps->dpy, PictOpSrc, tmp_picture, None, tgt_buffer, expand, expand, 0, 0, x, y, wid, hei);
xrfilter_reset(ps, tmp_picture);
win_blur_background_err:
free_pixmap(ps, &tmp_pixmap);
free_picture(ps, &tmp_picture);
}
/**
* Paint a window itself and dim it if asked.
*/
@ -1610,7 +1670,8 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
// Detect if the region is empty before painting
if (region == reg_paint || !is_region_empty(ps, reg_paint)) {
XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg_paint);
XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0,
reg_paint);
win_paint_shadow(ps, w, ps->tgt_buffer);
}
@ -1638,6 +1699,11 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
if (!is_region_empty(ps, reg_paint)) {
XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg_paint);
// Blur window background
if ((ps->o.blur_background && WINDOW_SOLID != w->mode)
|| (ps->o.blur_background_frame && w->frame_opacity)) {
win_blur_background(ps, w, ps->tgt_buffer, reg_paint);
}
// Painting the window
win_paint_win(ps, w, ps->tgt_buffer);
@ -1670,7 +1736,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
// DBE painting mode, only need to swap the buffer
if (ps->o.dbe) {
XdbeSwapInfo swap_info = {
.swap_window = (ps->o.paint_on_overlay ? ps->overlay: ps->root),
.swap_window = get_tgt_window(ps),
// Is it safe to use XdbeUndefined?
.swap_action = XdbeCopied
};
@ -3689,6 +3755,14 @@ usage(void) {
" Use WM_CLIENT_LEADER to group windows, and consider windows in\n"
" the same group focused at the same time. WM_TRANSIENT_FOR has\n"
" higher priority if --detect-transient is enabled, too.\n"
"--blur-background\n"
" Blur background of semi-transparent / ARGB windows. Bad in\n"
" performance. The switch name may change without prior\n"
" notifications.\n"
"--blur-background-frame\n"
" Blur background of windows when the window frame is not opaque.\n"
" Implies --blur-background. Bad in performance. The switch name\n"
" may change.\n"
"\n"
"Format of a condition:\n"
"\n"
@ -4095,6 +4169,11 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) {
parse_cfg_condlst(&cfg, &ps->o.shadow_blacklist, "shadow-exclude");
// --focus-exclude
parse_cfg_condlst(&cfg, &ps->o.focus_blacklist, "focus-exclude");
// --blur-background
lcfg_lookup_bool(&cfg, "blur-background", &ps->o.blur_background);
// --blur-background-frame
lcfg_lookup_bool(&cfg, "blur-background-frame",
&ps->o.blur_background_frame);
// Wintype settings
{
wintype_t i;
@ -4154,6 +4233,8 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
{ "inactive-dim-fixed", no_argument, NULL, 280 },
{ "detect-transient", no_argument, NULL, 281 },
{ "detect-client-leader", no_argument, NULL, 282 },
{ "blur-background", no_argument, NULL, 283 },
{ "blur-background-frame", no_argument, NULL, 284 },
// Must terminate with a NULL entry
{ NULL, 0, NULL, 0 },
};
@ -4371,6 +4452,14 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
// --detect-client-leader
ps->o.detect_client_leader = true;
break;
case 283:
// --blur-background
ps->o.blur_background = true;
break;
case 284:
// --blur-background-frame
ps->o.blur_background_frame = true;
break;
default:
usage();
}
@ -4409,6 +4498,10 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
ps->o.wintype_opacity[WINTYPE_POPUP_MENU] = cfgtmp.menu_opacity;
}
// --blur-background-frame implies --blur-background
if (ps->o.blur_background_frame)
ps->o.blur_background = true;
// Other variables determined by options
// Determine whether we need to track focus changes
@ -4785,6 +4878,32 @@ init_overlay(session_t *ps) {
}
}
/**
* Query needed X Render filters to check for their existence.
*/
static void
init_filters(session_t *ps) {
if (ps->o.blur_background || ps->o.blur_background_frame) {
// Query filters
XFilters *pf = XRenderQueryFilters(ps->dpy, get_tgt_window(ps));
if (pf) {
for (int i = 0; i < pf->nfilter; ++i) {
// Convolution filter
if (!strcmp(pf->filter[i], XRFILTER_CONVOLUTION))
ps->xrfilter_convolution_exists = true;
}
}
XFree(pf);
// Turn features off if any required filter is not present
if (!ps->xrfilter_convolution_exists) {
fprintf(stderr, "X Render convolution filter unsupported by your X server. Background blur disabled.\n");
ps->o.blur_background = false;
ps->o.blur_background_frame = false;
}
}
}
/**
* Redirect all windows.
*/
@ -4908,6 +5027,8 @@ session_init(session_t *ps_old, int argc, char **argv) {
.inactive_dim = 0.0,
.inactive_dim_fixed = false,
.alpha_step = 0.03,
.blur_background = false,
.blur_background_frame = false,
.wintype_focus = { false },
.use_ewmh_active_win = false,
@ -4977,6 +5098,7 @@ session_init(session_t *ps_old, int argc, char **argv) {
.glx_event = 0,
.glx_error = 0,
.dbe_exists = false,
.xrfilter_convolution_exists = false,
.atom_opacity = None,
.atom_frame_extents = None,
@ -5152,6 +5274,8 @@ session_init(session_t *ps_old, int argc, char **argv) {
}
}
init_filters(ps);
ps->black_picture = solid_picture(ps, true, 1, 0, 0, 0);
// Generates another Picture for shadows if the color is modified by

View File

@ -116,6 +116,10 @@
#define US_PER_SEC 1000000L
#define MS_PER_SEC 1000
#define XRFILTER_CONVOLUTION "convolution"
#define XRFILTER_GUASSIAN "gaussian"
#define XRFILTER_BINOMIAL "binomial"
// Window flags
// Window size is changed
@ -326,6 +330,11 @@ typedef struct {
bool inactive_dim_fixed;
/// Step for pregenerating alpha pictures. 0.01 - 1.0.
double alpha_step;
/// Whether to blur background of semi-transparent / ARGB windows.
bool blur_background;
/// Whether to blur background when the window frame is not opaque.
/// Implies blur_background.
bool blur_background_frame;
// === Focus related ===
/// Consider windows of specific types to be always focused.
@ -519,6 +528,8 @@ typedef struct {
#endif
/// Whether X DBE extension exists.
bool dbe_exists;
/// Whether X Render convolution filter exists.
bool xrfilter_convolution_exists;
// === Atoms ===
/// Atom of property <code>_NET_WM_OPACITY</code>.
@ -778,6 +789,22 @@ set_ignore_next(session_t *ps) {
static int
should_ignore(session_t *ps, unsigned long sequence);
/**
* Return the painting target window.
*/
static inline Window
get_tgt_window(session_t *ps) {
return ps->o.paint_on_overlay ? ps->overlay: ps->root;
}
/**
* Reset filter on a <code>Picture</code>.
*/
static inline void
xrfilter_reset(session_t *ps, Picture p) {
XRenderSetPictureFilter(ps->dpy, p, "Nearest", NULL, 0);
}
/**
* Subtract two unsigned long values.
*