diff --git a/compton-default-fshader-win.glsl b/compton-default-fshader-win.glsl index e3195238..4ab06848 100644 --- a/compton-default-fshader-win.glsl +++ b/compton-default-fshader-win.glsl @@ -1,11 +1,24 @@ uniform float opacity; uniform bool invert_color; uniform sampler2D tex; +uniform vec4 margin; + +float min3(vec3 v) { return min(min(v.x, v.y), v.z); } +float max3(vec3 v) { return max(max(v.x, v.y), v.z); } void main() { - vec4 c = texture2D(tex, gl_TexCoord[0]); - if (invert_color) - c = vec4(vec3(c.a, c.a, c.a) - vec3(c), c.a); - c *= opacity; - gl_FragColor = c; + vec2 coord = vec2(gl_TexCoord[0]); + vec4 c = texture2D(tex, coord); + + // If current window should be inverted and the pixel is inside client area + if (invert_color + && all(greaterThan(coord.xy, margin.st)) + && all(lessThan(coord.xy, margin.pq)) + ) { + // Fast luminance inversion, while preserving hue + c.rgb += 1.0 - max3(c.rgb) - min3(c.rgb); + } + + c *= opacity; + gl_FragColor = c; } diff --git a/man/compton.1.asciidoc b/man/compton.1.asciidoc index 95729499..f04cebf5 100644 --- a/man/compton.1.asciidoc +++ b/man/compton.1.asciidoc @@ -274,7 +274,10 @@ May also be one of the predefined kernels: `3x3box` (default), `5x5box`, `7x7box Additionally use X Sync fence to sync clients' draw calls. Needed on nvidia-drivers with GLX backend for some users. May be disabled at compile time with `NO_XSYNC=1`. *--glx-fshader-win* 'SHADER':: - GLX backend: Use specified GLSL fragment shader for rendering window contents. See `compton-default-fshader-win.glsl` and `compton-fake-transparency-fshader-win.glsl` in the source tree for examples. + GLX backend: Use specified GLSL fragment shader, supplied as a string, for rendering window contents. See `compton-default-fshader-win.glsl` and `compton-fake-transparency-fshader-win.glsl` in the source tree for examples. + +*--glx-fshader-win-file* 'PATH':: + GLX backend: Load GLSL fragment shader from the given path and use it for rendering window contents. See `compton-default-fshader-win.glsl` and `compton-fake-transparency-fshader-win.glsl` in the source tree for examples. *--force-win-blend*:: Force all windows to be painted with blending. Useful if you have a *--glx-fshader-win* that could turn opaque pixels transparent. diff --git a/src/common.h b/src/common.h index 2b75ff7a..b69c3ead 100644 --- a/src/common.h +++ b/src/common.h @@ -495,6 +495,8 @@ typedef struct { GLint unifm_invert_color; /// Location of uniform "tex" in window GLSL program. GLint unifm_tex; + /// Location of uniform "margin" in window GLSL program. + GLint unifm_margin; } glx_prog_main_t; #define GLX_PROG_MAIN_INIT { \ @@ -502,6 +504,7 @@ typedef struct { .unifm_opacity = -1, \ .unifm_invert_color = -1, \ .unifm_tex = -1, \ + .unifm_margin = -1, \ } #endif @@ -1528,6 +1531,26 @@ mstrcpy(const char *src) { return str; } +/** + * Allocate the space and read a text file into a string. + */ +static inline char * +mfread(const char *path) { + FILE *f = fopen(path, "r"); + if (!f) return NULL; + + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + char *str = cmalloc(fsize + 1, char); + fread(str, 1, fsize, f); + fclose(f); + str[fsize] = 0; + + return str; +} + /** * Allocate the space and copy a string. */ @@ -2209,7 +2232,7 @@ glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, 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, - double opacity, bool argb, bool neg, + double opacity, bool argb, bool neg, margin_t *margin, XserverRegion reg_tgt, const reg_data_t *pcache_reg #ifdef CONFIG_VSYNC_OPENGL_GLSL , const glx_prog_main_t *pprogram @@ -2218,12 +2241,12 @@ glx_render_(session_t *ps, const glx_texture_t *ptex, #ifdef CONFIG_VSYNC_OPENGL_GLSL #define \ - glx_render(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) \ - glx_render_(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) + glx_render(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, margin, reg_tgt, pcache_reg, pprogram) \ + glx_render_(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, margin, reg_tgt, pcache_reg, pprogram) #else #define \ - glx_render(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) \ - glx_render_(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg) + glx_render(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, margin, reg_tgt, pcache_reg, pprogram) \ + glx_render_(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, margin, reg_tgt, pcache_reg) #endif void diff --git a/src/compton.c b/src/compton.c index 92935a3d..740da660 100644 --- a/src/compton.c +++ b/src/compton.c @@ -1317,7 +1317,7 @@ win_paint_shadow(session_t *ps, win *w, } render(ps, 0, 0, w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, - w->shadow_width, w->shadow_height, w->shadow_opacity, true, false, + w->shadow_width, w->shadow_height, w->shadow_opacity, true, false, &w->frame_extents, w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, pcache_reg, NULL); } @@ -1513,7 +1513,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, static void render_(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, - double opacity, bool argb, bool neg, + double opacity, bool argb, bool neg, margin_t *margin, Picture pict, glx_texture_t *ptex, XserverRegion reg_paint, const reg_data_t *pcache_reg #ifdef CONFIG_VSYNC_OPENGL_GLSL @@ -1534,8 +1534,8 @@ render_(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, } #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: - glx_render(ps, ptex, x, y, dx, dy, wid, hei, - ps->psglx->z, opacity, argb, neg, reg_paint, pcache_reg, pprogram); + glx_render(ps, ptex, x, y, dx, dy, wid, hei, ps->psglx->z, opacity, + argb, neg, margin, reg_paint, pcache_reg, pprogram); ps->psglx->z += 1; break; #endif @@ -1598,37 +1598,42 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, const int y = w->a.y; const int wid = w->widthb; const int hei = w->heightb; + const bool invert_color = bkend_use_xrender(ps) && w->invert_color; Picture pict = w->paint.pict; + Picture invpict = NULL; // Invert window color, if required - if (bkend_use_xrender(ps) && w->invert_color) { - Picture newpict = xr_build_picture(ps, wid, hei, w->pictfmt); - if (newpict) { + if (invert_color) { + invpict = xr_build_picture(ps, wid, hei, w->pictfmt); + if (invpict) { // Apply clipping region to save some CPU if (reg_paint) { XserverRegion reg = copy_region(ps, reg_paint); XFixesTranslateRegion(ps->dpy, reg, -x, -y); - XFixesSetPictureClipRegion(ps->dpy, newpict, 0, 0, reg); + XFixesSetPictureClipRegion(ps->dpy, invpict, 0, 0, reg); free_region(ps, ®); } XRenderComposite(ps->dpy, PictOpSrc, pict, None, - newpict, 0, 0, 0, 0, 0, 0, wid, hei); + invpict, 0, 0, 0, 0, 0, 0, wid, hei); XRenderComposite(ps->dpy, PictOpDifference, ps->white_picture, None, - newpict, 0, 0, 0, 0, 0, 0, wid, hei); + invpict, 0, 0, 0, 0, 0, 0, wid, hei); + // Restore hue after inverting colors + XRenderComposite(ps->dpy, PictOpHSLHue, pict, None, + invpict, 0, 0, 0, 0, 0, 0, wid, hei); // We use an extra PictOpInReverse operation to get correct pixel // alpha. There could be a better solution. if (WMODE_ARGB == w->mode) XRenderComposite(ps->dpy, PictOpInReverse, pict, None, - newpict, 0, 0, 0, 0, 0, 0, wid, hei); - pict = newpict; + invpict, 0, 0, 0, 0, 0, 0, wid, hei); } } const double dopacity = get_opacity_percent(w); - if (!w->frame_opacity) { + if (!w->frame_opacity && !invert_color) { + // If neither frame opacity nor inverted color is required win_render(ps, w, 0, 0, wid, hei, dopacity, reg_paint, pcache_reg, pict); } else { @@ -1638,9 +1643,10 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, const int l = extents.left; const int b = extents.bottom; const int r = extents.right; + const double frame_opacity = w->frame_opacity ? w->frame_opacity : 1.0; #define COMP_BDR(cx, cy, cwid, chei) \ - win_render(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity, \ + win_render(ps, w, (cx), (cy), (cwid), (chei), frame_opacity, \ reg_paint, pcache_reg, pict) // The following complicated logic is required because some broken @@ -1676,7 +1682,8 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, pwid = wid - l - pwid; if (pwid > 0) { // body - win_render(ps, w, l, t, pwid, phei, dopacity, reg_paint, pcache_reg, pict); + win_render(ps, w, l, t, pwid, phei, dopacity, reg_paint, pcache_reg, + invpict ? invpict : pict); } } } @@ -1685,8 +1692,8 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, #undef COMP_BDR - if (pict != w->paint.pict) - free_picture(ps, &pict); + if (invpict) + free_picture(ps, &invpict); // Dimming the window if needed if (w->dim) { @@ -2013,7 +2020,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t glFlush(); glXWaitX(); glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, - ps->root_width, ps->root_height, 0, 1.0, false, false, + ps->root_width, ps->root_height, 0, 1.0, false, false, NULL, region_real, NULL, NULL); // No break here! case BKEND_GLX: @@ -2759,7 +2766,7 @@ win_mark_client(session_t *ps, win *w, Window client) { win_upd_wintype(ps, w); // Get frame widths. The window is in damaged area already. - if (ps->o.frame_opacity) + if (ps->o.frame_opacity || ps->o.invert_color_list) get_frame_extents(ps, w, client); // Get window group @@ -4814,6 +4821,10 @@ usage(int ret) { " GLX backend: Use specified GLSL fragment shader for rendering window\n" " contents.\n" "\n" + "--glx-fshader-win-file path\n" + " GLX backend: Load GLSL fragment shader from the given path and use it\n" + " for rendering window contents.\n" + "\n" "--force-win-blend\n" " Force all windows to be painted with blending. Useful if you have a\n" " --glx-fshader-win that could turn opaque pixels transparent.\n" @@ -5040,7 +5051,7 @@ parse_matrix(session_t *ps, const char *src, const char **endptr) { int wid = 0, hei = 0; const char *pc = NULL; XFixed *matrix = NULL; - + // Get matrix width and height { double val = 0.0; @@ -5638,6 +5649,17 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { exit(1); // --glx-use-gpushader4 lcfg_lookup_bool(&cfg, "glx-use-gpushader4", &ps->o.glx_use_gpushader4); + // --glx-fshader-win + if (config_lookup_string(&cfg, "glx-fshader-win", &sval)) + ps->o.glx_fshader_win_str = mstrcpy(sval); + // --glx-fshader-win-file + if (config_lookup_string(&cfg, "glx-fshader-win-file", &sval) + && !(ps->o.glx_fshader_win_str = mfread(sval))) { + printf("Cannot read file %s\n", sval); + exit(1); + } + // --glx-reinit-on-root-change + lcfg_lookup_bool(&cfg, "glx-reinit-on-root-change", &ps->o.glx_reinit_on_root_change); // --xrender-sync lcfg_lookup_bool(&cfg, "xrender-sync", &ps->o.xrender_sync); // --xrender-sync-fence @@ -5753,6 +5775,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "no-fading-destroyed-argb", no_argument, NULL, 315 }, { "force-win-blend", no_argument, NULL, 316 }, { "glx-fshader-win", required_argument, NULL, 317 }, + { "glx-fshader-win-file", required_argument, NULL, 321 }, { "version", no_argument, NULL, 318 }, { "no-x-selection", no_argument, NULL, 319 }, { "no-name-pixmap", no_argument, NULL, 320 }, @@ -6026,6 +6049,12 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { case 317: ps->o.glx_fshader_win_str = mstrcpy(optarg); break; + case 321: + if (!(ps->o.glx_fshader_win_str = mfread(optarg))) { + printf("Cannot read file %s\n", optarg); + exit(1); + } + break; P_CASEBOOL(319, no_x_selection); P_CASEBOOL(731, reredir_on_root_change); P_CASEBOOL(732, glx_reinit_on_root_change); diff --git a/src/compton.h b/src/compton.h index 0e27a759..2a749c1d 100644 --- a/src/compton.h +++ b/src/compton.h @@ -689,7 +689,7 @@ paint_preprocess(session_t *ps, win *list); static void render_(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, - double opacity, bool argb, bool neg, + double opacity, bool argb, bool neg, margin_t *margin, Picture pict, glx_texture_t *ptex, XserverRegion reg_paint, const reg_data_t *pcache_reg #ifdef CONFIG_VSYNC_OPENGL_GLSL @@ -699,12 +699,12 @@ render_(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, #ifdef CONFIG_VSYNC_OPENGL_GLSL #define \ - render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) \ - render_(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) + render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, margin, pict, ptex, reg_paint, pcache_reg, pprogram) \ + render_(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, margin, pict, ptex, reg_paint, pcache_reg, pprogram) #else #define \ - render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) \ - render_(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg) + render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, margin, pict, ptex, reg_paint, pcache_reg, pprogram) \ + render_(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, margin, pict, ptex, reg_paint, pcache_reg) #endif static inline void @@ -717,6 +717,7 @@ win_render(session_t *ps, win *w, int x, int y, int wid, int hei, const bool neg = (w && w->invert_color); render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, + (w ? &w->frame_extents : NULL), pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), reg_paint, pcache_reg, (w ? &ps->o.glx_prog_win: NULL)); } diff --git a/src/opengl.c b/src/opengl.c index 5a98f4e0..a6251779 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -290,6 +290,7 @@ glx_free_prog_main(session_t *ps, glx_prog_main_t *pprogram) { pprogram->unifm_opacity = -1; pprogram->unifm_invert_color = -1; pprogram->unifm_tex = -1; + pprogram->unifm_margin = -1; } #endif @@ -558,6 +559,7 @@ glx_load_prog_main(session_t *ps, P_GET_UNIFM_LOC("opacity", unifm_opacity); P_GET_UNIFM_LOC("invert_color", unifm_invert_color); P_GET_UNIFM_LOC("tex", unifm_tex); + P_GET_UNIFM_LOC("margin", unifm_margin); #undef P_GET_UNIFM_LOC glx_check_err(ps); @@ -1425,7 +1427,7 @@ glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, 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, - double opacity, bool argb, bool neg, + double opacity, bool argb, bool neg, margin_t *margin, XserverRegion reg_tgt, const reg_data_t *pcache_reg #ifdef CONFIG_VSYNC_OPENGL_GLSL , const glx_prog_main_t *pprogram @@ -1554,6 +1556,13 @@ glx_render_(session_t *ps, const glx_texture_t *ptex, glUniform1i(pprogram->unifm_invert_color, neg); if (pprogram->unifm_tex >= 0) glUniform1i(pprogram->unifm_tex, 0); + if (margin && pprogram->unifm_margin >= 0) + glUniform4f(pprogram->unifm_margin, + (float) margin->left / ptex->width, + (float) margin->top / ptex->height, + 1.0 - (float) margin->right / ptex->width, + 1.0 - (float) margin->bottom / ptex->height + ); } #endif