diff --git a/compton.sample.conf b/compton.sample.conf
index 599de2d5..0f2e4fbb 100644
--- a/compton.sample.conf
+++ b/compton.sample.conf
@@ -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;
diff --git a/src/compton.c b/src/compton.c
index a9053811..ceba178f 100644
--- a/src/compton.c
+++ b/src/compton.c
@@ -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
diff --git a/src/compton.h b/src/compton.h
index 16272e6f..8aecaa76 100644
--- a/src/compton.h
+++ b/src/compton.h
@@ -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 _NET_WM_OPACITY
.
@@ -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 Picture
.
+ */
+static inline void
+xrfilter_reset(session_t *ps, Picture p) {
+ XRenderSetPictureFilter(ps->dpy, p, "Nearest", NULL, 0);
+}
+
/**
* Subtract two unsigned long values.
*