Add optional global/per window greyscale transparency filter to compton-tde

pull/2/head
Timothy Pearson 10 years ago
parent c7056bbf7b
commit a582c6e22d

@ -489,6 +489,17 @@ typedef struct {
int height;
} glx_blur_cache_t;
typedef struct {
/// Framebuffer used for greyscale conversion.
GLuint fbo;
/// Textures used for greyscale conversion.
GLuint textures[2];
/// Width of the textures.
int width;
/// Height of the textures.
int height;
} glx_greyscale_cache_t;
typedef struct {
/// GLSL program.
GLuint prog;
@ -714,6 +725,10 @@ typedef struct _options_t {
c2_lptr_t *blur_background_blacklist;
/// Blur convolution kernel.
XFixed *blur_kerns[MAX_BLUR_PASS];
/// Whether to set background of semi-transparent / ARGB windows to greyscale.
bool greyscale_background;
/// Greyscale background blacklist. A linked list of conditions.
c2_lptr_t *greyscale_background_blacklist;
/// How much to dim an inactive window. 0.0 - 1.0, 0 to disable.
double inactive_dim;
/// Whether to use fixed inactive dim opacity, instead of deciding
@ -1047,10 +1062,12 @@ typedef struct _session_t {
Atom atom_compton_shadow;
/// Atom of property <code>_NET_WM_WINDOW_TYPE</code>.
Atom atom_win_type;
/// Atom of property <code>_KDE_TRANSPARENT_TO_BLACK</code>.
/// Atom of property <code>_TDE_TRANSPARENT_TO_BLACK</code>.
Atom atom_win_type_tde_transparent_to_black;
/// Atom of property <code>_KDE_TRANSPARENT_TO_DESKTOP</code>.
/// Atom of property <code>_TDE_TRANSPARENT_TO_DESKTOP</code>.
Atom atom_win_type_tde_transparent_to_desktop;
/// Atom of property <code>_TDE_TRANSPARENCY_FILTER_GREYSCALE</code>.
Atom atom_win_type_tde_transparency_filter_greyscale;
/// Array of atoms of all possible window types.
Atom atoms_wintypes[NUM_WINTYPES];
/// Linked list of additional atoms to track.
@ -1241,6 +1258,11 @@ typedef struct _win {
/// Background state on last paint.
bool blur_background_last;
/// Whether to set window background to greyscale.
bool greyscale_background;
/// Background state on last paint.
bool greyscale_background_last;
/// Whether to show black background
bool show_black_background;
@ -1250,6 +1272,9 @@ typedef struct _win {
#ifdef CONFIG_VSYNC_OPENGL_GLSL
/// Textures and FBO background blur use.
glx_blur_cache_t glx_blur_cache;
/// Textures and FBO greyscale background use.
glx_greyscale_cache_t glx_greyscale_cache;
#endif
} win;
@ -2218,6 +2243,10 @@ bool
glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z,
GLfloat factor, XserverRegion reg_tgt, const reg_data_t *pcache_reg);
bool
glx_greyscale_dst(session_t *ps, int dx, int dy, int width, int height, float z,
XserverRegion reg_tgt, const reg_data_t *pcache_reg, glx_greyscale_cache_t *pbc);
bool
glx_render_(session_t *ps, const glx_texture_t *ptex,
int x, int y, int dx, int dy, int width, int height, int z,
@ -2306,6 +2335,26 @@ free_glx_bc(session_t *ps, glx_blur_cache_t *pbc) {
free_glx_fbo(ps, &pbc->fbo);
free_glx_bc_resize(ps, pbc);
}
/**
* Free data in glx_greyscale_cache_t on resize.
*/
static inline void
free_glx_gc_resize(session_t *ps, glx_greyscale_cache_t *pbc) {
free_texture_r(ps, &pbc->textures[0]);
free_texture_r(ps, &pbc->textures[1]);
pbc->width = 0;
pbc->height = 0;
}
/**
* Free a glx_greyscale_cache_t
*/
static inline void
free_glx_gc(session_t *ps, glx_greyscale_cache_t *pbc) {
free_glx_fbo(ps, &pbc->fbo);
free_glx_gc_resize(ps, pbc);
}
#endif
#endif
@ -2349,6 +2398,7 @@ free_win_res_glx(session_t *ps, win *w) {
free_paint_glx(ps, &w->shadow_paint);
#ifdef CONFIG_VSYNC_OPENGL_GLSL
free_glx_bc(ps, &w->glx_blur_cache);
free_glx_gc(ps, &w->glx_greyscale_cache);
#endif
}

@ -1260,6 +1260,7 @@ paint_preprocess(session_t *ps, win *list) {
w->fade = w->fade_last;
win_set_invert_color(ps, w, w->invert_color_last);
win_set_blur_background(ps, w, w->blur_background_last);
win_set_greyscale_background(ps, w, w->greyscale_background_last);
}
// Update window opacity target and dim state if asked
@ -1407,6 +1408,7 @@ paint_preprocess(session_t *ps, win *list) {
w->fade_last = w->fade;
w->invert_color_last = w->invert_color;
w->blur_background_last = w->blur_background;
w->greyscale_background_last = w->greyscale_background;
}
}
}
@ -1546,6 +1548,53 @@ xr_blur_dst(session_t *ps, Picture tgt_buffer,
return true;
}
/**
* @brief Make an area on a buffer greyscale.
*
* @param ps current session
* @param tgt_buffer a buffer as both source and destination
* @param x x pos
* @param y y pos
* @param wid width
* @param hei height
* @param reg_clip a clipping region to be applied on intermediate buffers
*
* @return true if successful, false otherwise
*/
static bool
xr_greyscale_dst(session_t *ps, Picture tgt_buffer,
int x, int y, int wid, int hei, XserverRegion reg_clip) {
// Directly copying from tgt_buffer to it does not work, so we create a
// Picture in the middle.
Picture tmp_picture = xr_build_picture(ps, wid, hei, NULL);
if (!tmp_picture) {
printf_errf("(): Failed to build intermediate Picture.");
return false;
}
if (reg_clip && tmp_picture)
XFixesSetPictureClipRegion(ps->dpy, tmp_picture, reg_clip, 0, 0);
Picture src_pict = tgt_buffer, dst_pict = tmp_picture;
XRenderComposite(ps->dpy, PictOpHSLLuminosity, src_pict, None,
dst_pict, x, y, 0, 0, 0, 0, wid, hei);
XserverRegion tmp = src_pict;
src_pict = dst_pict;
dst_pict = tmp;
if (src_pict != tgt_buffer)
XRenderComposite(ps->dpy, PictOpSrc, src_pict, None, tgt_buffer,
0, 0, 0, 0, x, y, wid, hei);
free_picture(ps, &tmp_picture);
return true;
}
/*
* WORK-IN-PROGRESS!
static void
@ -1647,6 +1696,35 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer,
}
}
/**
* Set the background of a window to greyscale.
*/
static inline void
win_greyscale_background(session_t *ps, win *w, Picture tgt_buffer,
XserverRegion reg_paint, const reg_data_t *pcache_reg) {
const int x = w->a.x;
const int y = w->a.y;
const int wid = w->widthb;
const int hei = w->heightb;
switch (ps->o.backend) {
case BKEND_XRENDER:
case BKEND_XR_GLX_HYBRID:
{
xr_greyscale_dst(ps, tgt_buffer, x, y, wid, hei, reg_paint);
}
break;
#ifdef CONFIG_VSYNC_OPENGL_GLSL
case BKEND_GLX:
glx_greyscale_dst(ps, x, y, wid, hei, ps->psglx->z - 0.5,
reg_paint, pcache_reg, &w->glx_greyscale_cache);
break;
#endif
default:
assert(0);
}
}
static void
render_(session_t *ps, int x, int y, int dx, int dy, int wid, int hei,
double opacity, bool argb, bool neg,
@ -2111,6 +2189,11 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t
win_blur_background(ps, w, ps->tgt_buffer.pict, reg_paint, &cache_reg);
}
// Set window background to greyscale
if (w->greyscale_background && (!win_is_solid(ps, w))) {
win_greyscale_background(ps, w, ps->tgt_buffer.pict, reg_paint, &cache_reg);
}
// Painting the window
win_paint_win(ps, w, reg_paint, &cache_reg);
}
@ -2403,6 +2486,7 @@ map_win(session_t *ps, Window id) {
}
win_determine_blur_background(ps, w);
win_determine_greyscale_background(ps, w);
w->damaged = false;
@ -2519,6 +2603,28 @@ get_opacity_percent(win *w) {
return ((double) w->opacity) / OPAQUE;
}
static Bool
get_window_transparency_filter_greyscale(const session_t *ps, Window w)
{
Atom actual;
int format;
unsigned long n, left;
unsigned char *data;
int result = XGetWindowProperty (ps->dpy, w, ps->atom_win_type_tde_transparency_filter_greyscale, 0L, 1L, False,
XA_ATOM, &actual, &format,
&n, &left, &data);
if (result == Success && data != None && format == 32 )
{
Atom a;
a = *(long*)data;
XFree ( (void *) data);
return True;
}
return False;
}
static Bool
get_window_transparent_to_desktop(const session_t *ps, Window w)
{
@ -2950,6 +3056,32 @@ win_determine_blur_background(session_t *ps, win *w) {
win_set_blur_background(ps, w, blur_background_new);
}
static void
win_set_greyscale_background(session_t *ps, win *w, bool greyscale_background_new) {
if (w->greyscale_background == greyscale_background_new) return;
w->greyscale_background = greyscale_background_new;
// Only consider window damaged if it's previously painted with background
// set to greyscale
if (!win_is_solid(ps, w))
add_damage_win(ps, w);
}
/**
* Determine if a window should have a greyscale background.
*/
static void
win_determine_greyscale_background(session_t *ps, win *w) {
if (IsViewable != w->a.map_state)
return;
bool greyscale_background_new = (get_window_transparency_filter_greyscale(ps, w) ||
(ps->o.greyscale_background && !win_match(ps, w, ps->o.greyscale_background_blacklist, &w->cache_bbblst)));
win_set_greyscale_background(ps, w, greyscale_background_new);
}
/**
* Update window opacity according to opacity rules.
*/
@ -3006,6 +3138,8 @@ win_on_factor_change(session_t *ps, win *w) {
win_update_focused(ps, w);
if (ps->o.blur_background_blacklist)
win_determine_blur_background(ps, w);
if (ps->o.greyscale_background_blacklist)
win_determine_greyscale_background(ps, w);
if (ps->o.opacity_rules)
win_update_opacity_rule(ps, w);
if (IsViewable == w->a.map_state && ps->o.paint_blacklist)
@ -3273,6 +3407,7 @@ add_win(session_t *ps, Window id, Window prev) {
.invert_color_force = UNSET,
.blur_background = false,
.greyscale_background = false,
.show_black_background = false,
.show_root_tile = false,
@ -5037,6 +5172,11 @@ usage(int ret) {
" 11x11gaussian.\n"
"--blur-background-exclude condition\n"
" Exclude conditions for background blur.\n"
"--greyscale-background\n"
" Set background of semi-transparent / ARGB windows to greyscale.\n"
" The switch name may change without prior notifications.\n"
"--greyscale-background-exclude condition\n"
" Exclude conditions for greyscale background.\n"
"--resize-damage integer\n"
" Resize damaged region by a specific number of pixels. A positive\n"
" value enlarges it while a negative one shrinks it. Useful for\n"
@ -5909,12 +6049,16 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) {
parse_cfg_condlst(ps, &cfg, &ps->o.invert_color_list, "invert-color-include");
// --blur-background-exclude
parse_cfg_condlst(ps, &cfg, &ps->o.blur_background_blacklist, "blur-background-exclude");
// --greyscale-background-exclude
parse_cfg_condlst(ps, &cfg, &ps->o.greyscale_background_blacklist, "greyscale-background-exclude");
// --opacity-rule
parse_cfg_condlst_opct(ps, &cfg, "opacity-rule");
// --unredir-if-possible-exclude
parse_cfg_condlst(ps, &cfg, &ps->o.unredir_if_possible_blacklist, "unredir-if-possible-exclude");
// --blur-background
lcfg_lookup_bool(&cfg, "blur-background", &ps->o.blur_background);
// --greyscale-background
lcfg_lookup_bool(&cfg, "greyscale-background", &ps->o.greyscale_background);
// --blur-background-frame
lcfg_lookup_bool(&cfg, "blur-background-frame",
&ps->o.blur_background_frame);
@ -6066,6 +6210,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) {
{ "no-fading-opacitychange", no_argument, NULL, 321 },
{ "reredir-on-root-change", no_argument, NULL, 731 },
{ "glx-reinit-on-root-change", no_argument, NULL, 732 },
{ "greyscale-background", no_argument, NULL, 733 },
{ "greyscale-background-exclude", required_argument, NULL, 734 },
// Must terminate with a NULL entry
{ NULL, 0, NULL, 0 },
};
@ -6339,6 +6485,11 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) {
P_CASEBOOL(321, no_fading_opacitychange);
P_CASEBOOL(731, reredir_on_root_change);
P_CASEBOOL(732, glx_reinit_on_root_change);
P_CASEBOOL(733, greyscale_background);
case 734:
// --greyscale-background-exclude
condlst_add(ps, &ps->o.greyscale_background_blacklist, optarg);
break;
default:
usage(1);
break;
@ -6479,6 +6630,7 @@ init_atoms(session_t *ps) {
ps->atom_win_type_tde_transparent_to_black = get_atom(ps, "_TDE_TRANSPARENT_TO_BLACK");
ps->atom_win_type_tde_transparent_to_desktop = get_atom(ps, "_TDE_TRANSPARENT_TO_DESKTOP");
ps->atom_win_type_tde_transparency_filter_greyscale = get_atom(ps, "_TDE_TRANSPARENCY_FILTER_GREYSCALE");
}
#ifdef CONFIG_XRANDR
@ -7350,6 +7502,7 @@ session_init(session_t *ps_old, int argc, char **argv) {
.blur_background_fixed = false,
.blur_background_blacklist = NULL,
.blur_kerns = { NULL },
.greyscale_background = false,
.inactive_dim = 0.0,
.inactive_dim_fixed = false,
.invert_color_list = NULL,
@ -7447,6 +7600,7 @@ session_init(session_t *ps_old, int argc, char **argv) {
.atom_win_type = None,
.atom_win_type_tde_transparent_to_black = None,
.atom_win_type_tde_transparent_to_desktop = None,
.atom_win_type_tde_transparency_filter_greyscale = None,
.atoms_wintypes = { 0 },
.track_atom_lst = NULL,
@ -7851,6 +8005,7 @@ session_destroy(session_t *ps) {
free_wincondlst(&ps->o.focus_blacklist);
free_wincondlst(&ps->o.invert_color_list);
free_wincondlst(&ps->o.blur_background_blacklist);
free_wincondlst(&ps->o.greyscale_background_blacklist);
free_wincondlst(&ps->o.opacity_rules);
free_wincondlst(&ps->o.paint_blacklist);
free_wincondlst(&ps->o.unredir_if_possible_blacklist);

@ -870,6 +870,12 @@ win_set_blur_background(session_t *ps, win *w, bool blur_background_new);
static void
win_determine_blur_background(session_t *ps, win *w);
static void
win_set_greyscale_background(session_t *ps, win *w, bool greyscale_background_new);
static void
win_determine_greyscale_background(session_t *ps, win *w);
static void
win_on_wtype_change(session_t *ps, win *w);

@ -1385,6 +1385,175 @@ glx_blur_dst_end:
}
#endif
bool
glx_greyscale_dst(session_t *ps, int dx, int dy, int width, int height, float z,
XserverRegion reg_tgt, const reg_data_t *pcache_reg, glx_greyscale_cache_t *pbc) {
bool ret = false;
// Calculate copy region size
glx_greyscale_cache_t ibc = { .width = 0, .height = 0 };
if (!pbc)
pbc = &ibc;
#ifdef DEBUG_GLX
printf_dbgf("(): %d, %d, %d, %d\n", dx, dy, width, height);
#endif
// Free textures if size inconsistency discovered
if (width != pbc->width || height != pbc->height)
free_glx_gc_resize(ps, pbc);
// Generate FBO and textures if needed
if (!pbc->textures[0])
pbc->textures[0] = glx_gen_texture(ps, GL_TEXTURE_2D, width, height);
GLuint tex_scr1 = pbc->textures[0];
pbc->width = width;
pbc->height = height;
if (!tex_scr1) {
printf_errf("(): Failed to allocate texture.");
goto glx_greyscale_dst_end;
}
// Texture scaling factor
GLfloat texfac_x = 1.0f, texfac_y = 1.0f;
texfac_x /= width;
texfac_y /= height;
// Greyscale conversion in OpenGL ES taken nearly verbatim from this answer on Stack Overflow: http://stackoverflow.com/a/9690145
// Enable texture unit 0 to divide RGB values in our texture by 2
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex_scr1);
// Read destination pixels into the GL texture
glx_copy_region_to_tex(ps, GL_TEXTURE_2D, dx, dy, dx, dy, width, height);
// Finish setting up texture
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glClientActiveTexture(GL_TEXTURE0);
// GL_MODULATE is Arg0 * Arg1
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
// Configure Arg0
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
// Configure Arg1
float multipliers[4] = {.5, .5, .5, 0.0};
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, (GLfloat*)&multipliers);
// Enable texture unit 1 to increase RGB values by .5
glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex_scr1);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glClientActiveTexture(GL_TEXTURE1);
// GL_ADD is Arg0 + Arg1
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
// Configure Arg0
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
// Configure Arg1
GLfloat additions[4] = {.5, .5, .5, 0.0};
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, (GLfloat*)&additions);
// Enable texture combiner 2 to get a DOT3_RGB product of your RGB values
glActiveTexture(GL_TEXTURE2);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex_scr1);
glClientActiveTexture(GL_TEXTURE2);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
// GL_DOT3_RGB is 4*((Arg0r - 0.5) * (Arg1r - 0.5) + (Arg0g - 0.5) * (Arg1g - 0.5) + (Arg0b - 0.5) * (Arg1b - 0.5))
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
// Configure Arg0
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
// Configure Arg1
// We want this to adjust our DOT3 by R*0.3 + G*0.59 + B*0.11
// So, our actual adjustment will need to take into consideration
// the fact that OpenGL will subtract .5 from our Arg1
// and we need to also take into consideration that we have divided
// our RGB values by 2 and we are multiplying the entire
// DOT3 product by 4
// So, for Red adjustment you will get :
// .65 = (4*(0.3))/2 + 0.5 = (0.3/2) + 0.5
GLfloat weights[4] = {.65, .795, .555, 1.};
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, (GLfloat*)&weights);
// Render!
{
P_PAINTREG_START();
{
const GLfloat rx = (crect.x - dx) * texfac_x;
const GLfloat ry = (height - (crect.y - dy)) * texfac_y;
const GLfloat rxe = rx + crect.width * texfac_x;
const GLfloat rye = ry - crect.height * texfac_y;
GLfloat rdx = crect.x;
GLfloat rdy = ps->root_height - crect.y;
GLfloat rdxe = rdx + crect.width;
GLfloat rdye = rdy - crect.height;
#ifdef DEBUG_GLX
printf_dbgf("(): %f, %f, %f, %f -> %f, %f, %f, %f\n", rx, ry, rxe, rye, rdx, rdy, rdxe, rdye);
#endif
glTexCoord2f(rx, ry);
glVertex3f(rdx, rdy, z);
glTexCoord2f(rxe, ry);
glVertex3f(rdxe, rdy, z);
glTexCoord2f(rxe, rye);
glVertex3f(rdxe, rdye, z);
glTexCoord2f(rx, rye);
glVertex3f(rdx, rdye, z);
}
P_PAINTREG_END();
}
glEnd();
// Clean up by disabling your texture combiners or texture units.
glActiveTexture(GL_TEXTURE2);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glDisable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE1);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glDisable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glClientActiveTexture(GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
ret = true;
glx_greyscale_dst_end:
if (&ibc == pbc) {
free_glx_gc(ps, pbc);
}
glx_check_err(ps);
return ret;
}
bool
glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z,
GLfloat factor, XserverRegion reg_tgt, const reg_data_t *pcache_reg) {

Loading…
Cancel
Save