Created
March 8, 2015 13:19
-
-
Save richardgv/af42ea1fc377f447a38e to your computer and use it in GitHub Desktop.
chjj/compton #266: Add --glx-prog-win-rule
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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