Skip to content

Instantly share code, notes, and snippets.

@richardgv
Created March 8, 2015 13:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save richardgv/af42ea1fc377f447a38e to your computer and use it in GitHub Desktop.
Save richardgv/af42ea1fc377f447a38e to your computer and use it in GitHub Desktop.
chjj/compton #266: Add --glx-prog-win-rule
diff --git a/compton-chg-saturate-brightness-contrast.glsl b/compton-chg-saturate-brightness-contrast.glsl
new file mode 100644
index 0000000..7bb6811
--- /dev/null
+++ b/compton-chg-saturate-brightness-contrast.glsl
@@ -0,0 +1,55 @@
+uniform float opacity;
+uniform bool invert_color;
+uniform sampler2D tex;
+
+/**
+ * Adjusts the saturation of a color.
+ *
+ * From: https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Shaders/Builtin/Functions/saturation.glsl
+ *
+ * @param {vec3} rgb The color.
+ * @param {float} adjustment The amount to adjust the saturation of the color.
+ *
+ * @returns {float} The color with the saturation adjusted.
+ *
+ * @example
+ * vec3 greyScale = chg_saturation(color, 0.0);
+ * vec3 doubleSaturation = chg_saturation(color, 2.0);
+ */
+vec3 chg_saturation(vec3 rgb, float adjustment) {
+ // Algorithm from Chapter 16 of OpenGL Shading Language
+ const vec3 W = vec3(0.2125, 0.7154, 0.0721);
+ vec3 intensity = vec3(dot(rgb, W));
+ return mix(intensity, rgb, adjustment);
+}
+
+/**
+ * Adjusts the contrast of a color.
+ *
+ * @param adjustment the adjustment value, 0.0 - 1.0 reduces the contrast, >
+ * 1.0 increases it
+ */
+vec3 chg_contrast(vec3 rgb, float adjustment) {
+ return (rgb - 0.5) * adjustment + 0.5;
+}
+
+/**
+ * Adjusts the brightness of a color.
+ *
+ * @param adjustment the adjustment value, 0.0 - 1.0 reduces the brightness,
+ * > 1.0 increases it
+ */
+vec3 chg_brightness(vec3 rgb, float adjustment) {
+ return rgb * adjustment;
+}
+
+void main() {
+ vec4 c = texture2D(tex, gl_TexCoord[0]);
+ // c = vec4(chg_saturation(vec3(c), 0.5), c.a);
+ // c = vec4(chg_contrast(vec3(c), 0.5), c.a);
+ c = vec4(chg_brightness(vec3(c), 0.5), c.a);
+ if (invert_color)
+ c = vec4(vec3(c.a, c.a, c.a) - vec3(c), c.a);
+ c *= opacity;
+ gl_FragColor = c;
+}
diff --git a/src/c2.c b/src/c2.c
index 6baf133..084c353 100644
--- a/src/c2.c
+++ b/src/c2.c
@@ -1318,3 +1318,25 @@ c2_matchd(session_t *ps, win *w, const c2_lptr_t *condlst,
return false;
}
+/**
+ * @brief Call a handler for each condition in the given condition list.
+ *
+ * @param ps the session object
+ * @param pcondlst the condition list to iterate over
+ * @param handler the handler to call, with the condition data pointer passed
+ * to it, when it returns <code>false</code> the loop terminates
+ * and returns <code>false</code>
+ * @param extra_data the extra data pointer to pass to the handler
+ *
+ * @return <code>true</code> if all handler calls succeeded (returns
+ * <code>true</code>, <code>false</code> otherwise
+ */
+bool
+c2_foreach_condition(session_t *ps, const c2_lptr_t *pcondlst,
+ bool (*handler)(session_t *ps, void *data, void *extra_data),
+ void *extra_data) {
+ for (const c2_lptr_t *p = pcondlst; p; p = p->next)
+ if (!handler(ps, p->data, extra_data))
+ return false;
+ return true;
+}
diff --git a/src/common.h b/src/common.h
index 3be2665..383064f 100644
--- a/src/common.h
+++ b/src/common.h
@@ -91,6 +91,7 @@
#include <assert.h>
#include <time.h>
#include <ctype.h>
+#include <errno.h>
#include <sys/time.h>
#include <X11/Xlib.h>
@@ -200,6 +201,9 @@
/// @brief Length of generic buffers.
#define BUF_LEN 80
+/// @brief Length of file reading buffers.
+#define FILE_BUF_LEN 1024
+
#define ROUNDED_PERCENT 0.05
#define ROUNDED_PIXELS 10
@@ -493,6 +497,19 @@ typedef struct {
.unifm_tex = -1, \
}
+/// @brief GL program to draw windows, with source.
+typedef struct {
+ /// @brief The source string of the fragment shader.
+ char *fshader_str;
+ /// @brief The GLSL program data.
+ glx_prog_main_t prog;
+} glx_prog_main_src_t;
+
+#define GLX_PROG_MAIN_SRC_INIT { \
+ .fshader_str = NULL, \
+ .prog = GLX_PROG_MAIN_INIT \
+}
+
#endif
#endif
@@ -566,6 +583,9 @@ typedef struct _options_t {
#ifdef CONFIG_VSYNC_OPENGL_GLSL
/// Custom GLX program used for painting window.
glx_prog_main_t glx_prog_win;
+ /// @brief Custom GLX program rules used for painting window.
+ /// TODO: Move the generated GL programs to session_t
+ c2_lptr_t *glx_prog_win_rules;
#endif
/// Whether to fork to background.
bool fork_after_register;
@@ -1218,6 +1238,11 @@ typedef struct _win {
bool blur_background_last;
#ifdef CONFIG_VSYNC_OPENGL_GLSL
+ /// @brief The matched custom GLX window-painting program.
+ glx_prog_main_t *pcustom_prog;
+ /// @brief Cache of custom GLX window-painting program rules.
+ const c2_lptr_t *cache_gpmrule;
+
/// Textures and FBO background blur use.
glx_blur_cache_t glx_blur_cache;
#endif
@@ -2135,6 +2160,11 @@ xr_glx_sync(session_t *ps, Drawable d, XSyncFence *pfence);
bool
glx_init(session_t *ps, bool need_render);
+#ifdef CONFIG_VSYNC_OPENGL_GLSL
+void
+glx_free_prog_main(session_t *ps, glx_prog_main_t *pprogram);
+#endif
+
void
glx_destroy(session_t *ps);
@@ -2484,6 +2514,12 @@ c2_matchd(session_t *ps, win *w, const c2_lptr_t *condlst,
#define c2_match(ps, w, condlst, cache) c2_matchd((ps), (w), (condlst), \
(cache), NULL)
+
+bool
+c2_foreach_condition(session_t *ps, const c2_lptr_t *pcondlst,
+ bool (*handler)(session_t *ps, void *data, void *extra_data),
+ void *extra_data);
+
#endif
///@}
@@ -2545,4 +2581,48 @@ hexdump(const char *data, int len) {
fflush(stdout);
}
+/**
+ * Read the contents of a file into a string.
+ *
+ * @param path the path of the file to read
+ *
+ * @return the contents of a file as a string, use <code>free()</code> when you
+ * wish to free it
+ */
+static inline char *
+read_file_to_str(const char *path) {
+ // mmap() would be be better, but it does not produce a null-terminated
+ // string
+
+ FILE *f = fopen(path, "r");
+ if (!f) {
+ printf_errf("(\"%s\"): Failed to open file for reading: %d", path, errno);
+ return NULL;
+ }
+ int length = 0;
+ // We could predict the size of the buffer with stat(), but I don't know if
+ // there's a risk of meeting synchronization issues, and it may fail on
+ // special files
+ int buf_length = FILE_BUF_LEN + 1;
+ char *buf = cmalloc(buf_length, char);
+ for (int read_length = 0;
+ 0 != (read_length = fread(&buf[length], 1, buf_length - length - 1, f));
+ ) {
+ length += read_length;
+ assert(length <= buf_length - 1);
+ if (likely(buf_length < length + FILE_BUF_LEN + 1)) {
+ buf_length += FILE_BUF_LEN;
+ buf = crealloc(buf, buf_length, char);
+ }
+ assert(buf_length >= length + FILE_BUF_LEN + 1);
+ }
+ if (ferror(f)) {
+ printf_errf("(\"%s\"): Failed to read file: %d", path, errno);
+ free(buf);
+ return NULL;
+ }
+ buf[length] = '\0';
+ return buf;
+}
+
#endif
diff --git a/src/compton.c b/src/compton.c
index 38e25ce..a032ef3 100644
--- a/src/compton.c
+++ b/src/compton.c
@@ -2596,6 +2596,31 @@ win_determine_blur_background(session_t *ps, win *w) {
}
/**
+ * @brief Update window-painting program of the given window according to GLX
+ * custom window-painting program rules.
+ *
+ * @param session a pointer to the session object
+ * @param w a pointer to the window object to update
+ */
+static void
+win_update_glx_prog_main_rule(session_t *ps, win *w) {
+ if (IsViewable != w->a.map_state)
+ return;
+
+#ifdef CONFIG_C2
+ glx_prog_main_t *pcustom_prog_old = w->pcustom_prog;
+ w->pcustom_prog = NULL;
+ void *val = NULL;
+ if (c2_matchd(ps, w, ps->o.glx_prog_win_rules, &w->cache_gpmrule, &val)) {
+ glx_prog_main_src_t *pglx_prog_main_src = val;
+ w->pcustom_prog = &pglx_prog_main_src->prog;
+ }
+ if (pcustom_prog_old != w->pcustom_prog)
+ add_damage_win(ps, w);
+#endif
+}
+
+/**
* Update window opacity according to opacity rules.
*/
static void
@@ -2653,6 +2678,10 @@ win_on_factor_change(session_t *ps, win *w) {
win_determine_blur_background(ps, w);
if (ps->o.opacity_rules)
win_update_opacity_rule(ps, w);
+#ifdef CONFIG_VSYNC_OPENGL_GLSL
+ if (ps->o.glx_prog_win_rules)
+ win_update_glx_prog_main_rule(ps, w);
+#endif
if (IsViewable == w->a.map_state && ps->o.paint_blacklist)
w->paint_excluded = win_match(ps, w, ps->o.paint_blacklist,
&w->cache_pblst);
@@ -4814,6 +4843,10 @@ usage(int ret) {
" GLX backend: Use specified GLSL fragment shader for rendering window\n"
" contents.\n"
"\n"
+ "--glx-prog-win-rule fragment-shader-path:condition\n"
+ " GLX backend: Use the specified GLSL shader if the window condition\n"
+ " matches on the window.\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"
@@ -5299,6 +5332,49 @@ parse_rule_opacity(session_t *ps, const char *src) {
#endif
}
+/**
+ * Parse the given GLX custom window-painting program rule.
+ *
+ * @param src the string of the rule to parse
+ *
+ * @return <code>true</code> if successful, <code>false</code> otherwise
+ */
+static inline bool
+parse_rule_glx_prog_win(session_t *ps, const char *src) {
+#ifdef CONFIG_C2
+ // Find fragment shader path
+ char *endptr = strchr(src, ':');
+ if (!endptr || endptr == src) {
+ printf_errf("(\"%s\"): No fragment shader path found", src);
+ return false;
+ }
+ char *path = mstrncpy(src, endptr - src);
+ ++endptr;
+
+ // Read the shader
+ char *fshader_str = read_file_to_str(path);
+ free(path);
+ path = NULL;
+ if (!fshader_str)
+ return false;
+
+ // Construct the structure
+ glx_prog_main_src_t *pprog_src = cmalloc(1, glx_prog_main_src_t);
+ {
+ static const glx_prog_main_src_t GLX_PROG_MAIN_SRC_DEF =
+ GLX_PROG_MAIN_SRC_INIT;
+ memcpy(pprog_src, &GLX_PROG_MAIN_SRC_DEF, sizeof(GLX_PROG_MAIN_SRC_DEF));
+ }
+ pprog_src->fshader_str = fshader_str;
+
+ // Parse pattern
+ return c2_parsed(ps, &ps->o.glx_prog_win_rules, endptr, pprog_src);
+#else
+ printf_errf("(\"%s\"): Condition support not compiled in.", src);
+ return false;
+#endif
+}
+
#ifdef CONFIG_LIBCONFIG
/**
* Get a file stream of the configuration file to read.
@@ -5753,6 +5829,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) {
{ "no-name-pixmap", no_argument, NULL, 320 },
{ "reredir-on-root-change", no_argument, NULL, 731 },
{ "glx-reinit-on-root-change", no_argument, NULL, 732 },
+ { "glx-prog-win-rule", required_argument, NULL, 1769 },
// Must terminate with a NULL entry
{ NULL, 0, NULL, 0 },
};
@@ -6024,6 +6101,11 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) {
P_CASEBOOL(319, no_x_selection);
P_CASEBOOL(731, reredir_on_root_change);
P_CASEBOOL(732, glx_reinit_on_root_change);
+ case 1769:
+ // --glx-prog-win-rule
+ if (!parse_rule_glx_prog_win(ps, optarg))
+ exit(1);
+ break;
default:
usage(1);
break;
@@ -6933,6 +7015,37 @@ cxinerama_upd_scrs(session_t *ps) {
#endif
}
+#ifdef CONFIG_VSYNC_OPENGL_GLSL
+
+/**
+ * @brief c2_foreach_condition() handler to initialize a GLX custom
+ * window-painting program rule.
+ */
+static bool
+init_rule_glx_prog_main_handler(session_t *ps, void *data, void *extra_data) {
+ glx_prog_main_src_t *glx_prog_main_src = data;
+ return glx_load_prog_main(ps, NULL, glx_prog_main_src->fshader_str,
+ &glx_prog_main_src->prog);
+}
+
+/**
+ * @brief c2_foreach_condition() handler to free resources in a GLX custom
+ * window-painting program rule.
+ */
+static bool
+free_rule_glx_prog_main_handler(session_t *ps, void *data, void *extra_data) {
+ glx_prog_main_src_t *glx_prog_main_src = data;
+
+ free(glx_prog_main_src->fshader_str);
+ glx_prog_main_src->fshader_str = NULL;
+ glx_free_prog_main(ps, &glx_prog_main_src->prog);
+ free(glx_prog_main_src);
+
+ return true;
+}
+
+#endif
+
/**
* Initialize a session.
*
@@ -7335,6 +7448,15 @@ session_init(session_t *ps_old, int argc, char **argv) {
#endif
}
+ // Initialize window GL shader rules
+#ifdef CONFIG_VSYNC_OPENGL_GLSL
+ if (BKEND_GLX == ps->o.backend && ps->o.glx_prog_win_rules) {
+ if (!c2_foreach_condition(ps, ps->o.glx_prog_win_rules,
+ init_rule_glx_prog_main_handler, NULL))
+ exit(1);
+ }
+#endif
+
// Initialize software optimization
if (ps->o.sw_opti)
ps->o.sw_opti = swopti_init(ps);
@@ -7517,6 +7639,11 @@ session_destroy(session_t *ps) {
free_wincondlst(&ps->o.opacity_rules);
free_wincondlst(&ps->o.paint_blacklist);
free_wincondlst(&ps->o.unredir_if_possible_blacklist);
+#ifdef CONFIG_VSYNC_OPENGL_GLSL
+ c2_foreach_condition(ps, ps->o.glx_prog_win_rules,
+ free_rule_glx_prog_main_handler, NULL);
+ free_wincondlst(&ps->o.glx_prog_win_rules);
+#endif
#endif
// Free tracked atom list
diff --git a/src/compton.h b/src/compton.h
index 4844c9c..c580093 100644
--- a/src/compton.h
+++ b/src/compton.h
@@ -703,7 +703,7 @@ win_render(session_t *ps, win *w, int x, int y, int wid, int hei,
render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg,
pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex),
- reg_paint, pcache_reg, (w ? &ps->o.glx_prog_win: NULL));
+ reg_paint, pcache_reg, (w ? (w->pcustom_prog ? w->pcustom_prog : &ps->o.glx_prog_win): NULL));
}
static inline void
diff --git a/src/opengl.c b/src/opengl.c
index 5a98f4e..fa25dc2 100644
--- a/src/opengl.c
+++ b/src/opengl.c
@@ -279,7 +279,7 @@ glx_init_end:
#ifdef CONFIG_VSYNC_OPENGL_GLSL
-static void
+void
glx_free_prog_main(session_t *ps, glx_prog_main_t *pprogram) {
if (!pprogram)
return;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment