config: add options `window-shader-fg` and `window-shader-fg-rules`

Used for setting custom window shaders and rules for choosing custom
window shaders.

Added a "c2_userdata_free" parameter to c2_list_free, so allocated userdata
stored in nodes can be freed.

Signed-off-by: Bernd Busse <bernd@busse-net.de>
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2022-07-17 17:49:35 +01:00
parent 896acabab1
commit 654772b8cf
No known key found for this signature in database
GPG Key ID: D3A4405BE6CC17F4
12 changed files with 462 additions and 118 deletions

View File

@ -54,7 +54,7 @@ jobs:
command: ninja -vC build test
- run:
name: test config file parsing
command: xvfb-run -s "-screen 0 640x480x24" build/src/picom --config tests/configs/parsing_test.conf --no-vsync --diagnostics
command: xvfb-run -s "-screen 0 640x480x24" build/src/picom --experimental-backends --config tests/configs/parsing_test.conf --no-vsync --diagnostics
- run:
name: run testsuite
command: tests/run_tests.sh build/src/picom

View File

@ -236,7 +236,7 @@ May also be one of the predefined kernels: `3x3box` (default), `5x5box`, `7x7box
Use X Sync fence to sync clients' draw calls, to make sure all draw calls are finished before picom starts drawing. Needed on nvidia-drivers with GLX backend for some users.
*--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 for rendering window contents. See `compton-default-fshader-win.glsl` and `compton-fake-transparency-fshader-win.glsl` in the source tree for examples. Doesn't work with `--experimental-backends` enabled.
*--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.
@ -259,6 +259,12 @@ May also be one of the predefined kernels: `3x3box` (default), `5x5box`, `7x7box
*--transparent-clipping*::
Make transparent windows clip other windows like non-transparent windows do, instead of blending on top of them.
*--window-shader-fg* 'SHADER'::
Specify GLSL fragment shader path for rendering window contents. Only works when `--experimental-backends` is enabled.
*--window-shader-fg-rule* 'SHADER':'CONDITION'::
Specify GLSL fragment shader path for rendering window contents using patterns. Pattern should be in the format of `SHADER:PATTERN`, similar to `--opacity-rule`. Leading and trailing whitespaces in `SHADER` will be trimmed. If `SHADER` is "default", then the default shader will be used for matching windows. (This also unfortunately means you can't use a shader file named "default"). Only works when `--experimental-backends` is enabled.
FORMAT OF CONDITIONS
--------------------
Some options accept a condition string to match certain windows. A condition string is formed by one or more conditions, joined by logical operators.

View File

@ -1151,11 +1151,16 @@ static void c2_free(c2_ptr_t p) {
/**
* Free a condition tree in c2_lptr_t.
*/
c2_lptr_t *c2_free_lptr(c2_lptr_t *lp) {
if (!lp)
c2_lptr_t *c2_free_lptr(c2_lptr_t *lp, c2_userdata_free f) {
if (!lp) {
return NULL;
}
c2_lptr_t *pnext = lp->next;
if (f) {
f(lp->data);
}
lp->data = NULL;
c2_free(lp->ptr);
free(lp);

View File

@ -18,9 +18,10 @@ typedef struct _c2_lptr c2_lptr_t;
typedef struct session session_t;
struct managed_win;
typedef void (*c2_userdata_free)(void *);
c2_lptr_t *c2_parse(c2_lptr_t **pcondlst, const char *pattern, void *data);
c2_lptr_t *c2_free_lptr(c2_lptr_t *lp);
c2_lptr_t *c2_free_lptr(c2_lptr_t *lp, c2_userdata_free f);
bool c2_match(session_t *ps, const struct managed_win *w, const c2_lptr_t *condlst,
void **pdata);
@ -34,8 +35,8 @@ void *c2_list_get_data(const c2_lptr_t *condlist);
/**
* Destroy a condition list.
*/
static inline void c2_list_free(c2_lptr_t **pcondlst) {
while ((*pcondlst = c2_free_lptr(*pcondlst))) {
static inline void c2_list_free(c2_lptr_t **pcondlst, c2_userdata_free f) {
while ((*pcondlst = c2_free_lptr(*pcondlst, f))) {
}
*pcondlst = NULL;
}

View File

@ -130,6 +130,13 @@ typedef struct _latom {
struct _latom *next;
} latom_t;
struct shader_info {
char *key;
char *source;
void *backend_shader;
UT_hash_handle hh;
};
/// Structure containing all necessary data for a session.
typedef struct session {
// === Event handlers ===
@ -150,6 +157,8 @@ typedef struct session {
ev_signal usr1_signal;
/// Signal handler for SIGINT
ev_signal int_signal;
// === Backend related ===
/// backend data
backend_t *backend_data;
/// backend blur context
@ -160,6 +169,8 @@ typedef struct session {
void *file_watch_handle;
/// libev mainloop
struct ev_loop *loop;
/// Shaders
struct shader_info *shaders;
// === Display related ===
/// Whether the X server is grabbed by us

View File

@ -3,13 +3,21 @@
// Copyright (c) 2013 Richard Grenville <pyxlcy@gmail.com>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <xcb/render.h> // for xcb_render_fixed_t, XXX
#include <test.h>
#include "c2.h"
#include "common.h"
#include "compiler.h"
@ -23,6 +31,98 @@
#include "config.h"
const char *xdg_config_home(void) {
char *xdgh = getenv("XDG_CONFIG_HOME");
char *home = getenv("HOME");
const char *default_dir = "/.config";
if (!xdgh) {
if (!home) {
return NULL;
}
xdgh = cvalloc(strlen(home) + strlen(default_dir) + 1);
strcpy(xdgh, home);
strcat(xdgh, default_dir);
} else {
xdgh = strdup(xdgh);
}
return xdgh;
}
char **xdg_config_dirs(void) {
char *xdgd = getenv("XDG_CONFIG_DIRS");
size_t count = 0;
if (!xdgd) {
xdgd = "/etc/xdg";
}
for (int i = 0; xdgd[i]; i++) {
if (xdgd[i] == ':') {
count++;
}
}
// Store the string and the result pointers together so they can be
// freed together
char **dir_list = cvalloc(sizeof(char *) * (count + 2) + strlen(xdgd) + 1);
auto dirs = strcpy((char *)dir_list + sizeof(char *) * (count + 2), xdgd);
auto path = dirs;
for (size_t i = 0; i < count; i++) {
dir_list[i] = path;
path = strchr(path, ':');
*path = '\0';
path++;
}
dir_list[count] = path;
size_t fill = 0;
for (size_t i = 0; i <= count; i++) {
if (dir_list[i][0] == '/') {
dir_list[fill] = dir_list[i];
fill++;
}
}
dir_list[fill] = NULL;
return dir_list;
}
TEST_CASE(xdg_config_dirs) {
auto old_var = getenv("XDG_CONFIG_DIRS");
if (old_var) {
old_var = strdup(old_var);
}
unsetenv("XDG_CONFIG_DIRS");
auto result = xdg_config_dirs();
TEST_STREQUAL(result[0], "/etc/xdg");
TEST_EQUAL(result[1], NULL);
free(result);
setenv("XDG_CONFIG_DIRS", ".:.:/etc/xdg:.:/:", 1);
result = xdg_config_dirs();
TEST_STREQUAL(result[0], "/etc/xdg");
TEST_STREQUAL(result[1], "/");
TEST_EQUAL(result[2], NULL);
free(result);
setenv("XDG_CONFIG_DIRS", ":", 1);
result = xdg_config_dirs();
TEST_EQUAL(result[0], NULL);
free(result);
if (old_var) {
setenv("XDG_CONFIG_DIRS", old_var, 1);
free(old_var);
}
}
/**
* Parse a long number.
*/
@ -438,6 +538,114 @@ bool parse_rule_opacity(c2_lptr_t **res, const char *src) {
return c2_parse(res, endptr, (void *)val);
}
/// Search for auxiliary file under a base directory
static char *locate_auxiliary_file_at(const char *base, const char *scope, const char *file) {
scoped_charp path = mstrjoin(base, scope);
mstrextend(&path, "/");
mstrextend(&path, file);
if (access(path, O_RDONLY) == 0) {
// Canonicalize path to avoid duplicates
char *abspath = realpath(path, NULL);
return abspath;
}
return NULL;
}
/**
* Get a path of an auxiliary file to read, could be a shader file, or any supplimenrary
* file.
*
* Follows the XDG specification to search for the shader file in configuration locations.
*
* The search order is:
* 1) If an absolute path is given, use it directly.
* 2) Search for the file directly under `include_dir`.
* 3) Search for the file in the XDG configuration directories, under path
* /picom/<scope>/
*/
char *locate_auxiliary_file(const char *scope, const char *path, const char *include_dir) {
if (!path || strlen(path) == 0) {
return NULL;
}
// Filename is absolute path, so try to load from there
if (path[0] == '/') {
if (access(path, O_RDONLY) == 0) {
return realpath(path, NULL);
}
}
// First try to load file from the include directory (i.e. relative to the
// config file)
if (include_dir && strlen(include_dir)) {
char *ret = locate_auxiliary_file_at(include_dir, "", path);
if (ret) {
return ret;
}
}
// Fall back to searching in user config directory
scoped_charp picom_scope = mstrjoin("/picom/", scope);
scoped_charp config_home = (char *)xdg_config_home();
char *ret = locate_auxiliary_file_at(config_home, picom_scope, path);
if (ret) {
return ret;
}
// Fall back to searching in system config directory
auto config_dirs = xdg_config_dirs();
for (int i = 0; config_dirs[i]; i++) {
ret = locate_auxiliary_file_at(config_dirs[i], picom_scope, path);
if (ret) {
free(config_dirs);
return ret;
}
}
free(config_dirs);
return ret;
}
/**
* Parse a list of window shader rules.
*/
bool parse_rule_window_shader(c2_lptr_t **res, const char *src, const char *include_dir) {
if (!src) {
return false;
}
// Find custom shader terminator
const char *endptr = strchr(src, ':');
if (!endptr) {
log_error("Custom shader terminator not found: %s", src);
return false;
}
// Parse and create custom shader
scoped_charp untrimed_shader_source = strdup(src);
if (!untrimed_shader_source) {
return false;
}
auto source_end = strchr(untrimed_shader_source, ':');
*source_end = '\0';
size_t length;
char *tmp = (char *)trim_both(untrimed_shader_source, &length);
tmp[length] = '\0';
char *shader_source = NULL;
if (strcasecmp(tmp, "default") != 0) {
shader_source = locate_auxiliary_file("shaders", tmp, include_dir);
if (!shader_source) {
log_error("Custom shader file \"%s\" not found for rule: %s", tmp, src);
free(shader_source);
return false;
}
}
return c2_parse(res, ++endptr, (void *)shader_source);
}
/**
* Add a pattern to a condition linked list.
*/
@ -565,6 +773,8 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
.blur_background_blacklist = NULL,
.blur_kerns = NULL,
.blur_kernel_count = 0,
.window_shader_fg = NULL,
.window_shader_fg_rules = NULL,
.inactive_dim = 0.0,
.inactive_dim_fixed = false,
.invert_color_list = NULL,

View File

@ -15,6 +15,8 @@
#include <xcb/xcb.h>
#include <xcb/xfixes.h>
#include "uthash_extra.h"
#ifdef CONFIG_LIBCONFIG
#include <libconfig.h>
#endif
@ -206,6 +208,10 @@ typedef struct options {
struct conv **blur_kerns;
/// Number of convolution kernels
int blur_kernel_count;
/// Custom fragment shader for painting windows
char *window_shader_fg;
/// Rules to change custom fragment shader for painting windows.
c2_lptr_t *window_shader_fg_rules;
/// 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
@ -255,6 +261,9 @@ bool must_use parse_int(const char *, int *);
struct conv **must_use parse_blur_kern_lst(const char *, bool *hasneg, int *count);
bool must_use parse_geometry(session_t *, const char *, region_t *);
bool must_use parse_rule_opacity(c2_lptr_t **, const char *);
bool must_use parse_rule_window_shader(c2_lptr_t **, const char *, const char *);
char *must_use locate_auxiliary_file(const char *scope, const char *path,
const char *include_dir);
enum blur_method must_use parse_blur_method(const char *src);
/**
@ -263,6 +272,9 @@ enum blur_method must_use parse_blur_method(const char *src);
bool condlst_add(c2_lptr_t **, const char *);
#ifdef CONFIG_LIBCONFIG
const char *xdg_config_home(void);
char **xdg_config_dirs(void);
/// Parse a configuration file
/// Returns the actually config_file name used, allocated on heap
/// Outputs:

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2012-2014 Richard Grenville <pyxlcy@gmail.com>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -37,98 +38,6 @@ static inline int lcfg_lookup_bool(const config_t *config, const char *path, boo
return ret;
}
const char *xdg_config_home(void) {
char *xdgh = getenv("XDG_CONFIG_HOME");
char *home = getenv("HOME");
const char *default_dir = "/.config";
if (!xdgh) {
if (!home) {
return NULL;
}
xdgh = cvalloc(strlen(home) + strlen(default_dir) + 1);
strcpy(xdgh, home);
strcat(xdgh, default_dir);
} else {
xdgh = strdup(xdgh);
}
return xdgh;
}
char **xdg_config_dirs(void) {
char *xdgd = getenv("XDG_CONFIG_DIRS");
size_t count = 0;
if (!xdgd) {
xdgd = "/etc/xdg";
}
for (int i = 0; xdgd[i]; i++) {
if (xdgd[i] == ':') {
count++;
}
}
// Store the string and the result pointers together so they can be
// freed together
char **dir_list = cvalloc(sizeof(char *) * (count + 2) + strlen(xdgd) + 1);
auto dirs = strcpy((char *)dir_list + sizeof(char *) * (count + 2), xdgd);
auto path = dirs;
for (size_t i = 0; i < count; i++) {
dir_list[i] = path;
path = strchr(path, ':');
*path = '\0';
path++;
}
dir_list[count] = path;
size_t fill = 0;
for (size_t i = 0; i <= count; i++) {
if (dir_list[i][0] == '/') {
dir_list[fill] = dir_list[i];
fill++;
}
}
dir_list[fill] = NULL;
return dir_list;
}
TEST_CASE(xdg_config_dirs) {
auto old_var = getenv("XDG_CONFIG_DIRS");
if (old_var) {
old_var = strdup(old_var);
}
unsetenv("XDG_CONFIG_DIRS");
auto result = xdg_config_dirs();
TEST_STREQUAL(result[0], "/etc/xdg");
TEST_EQUAL(result[1], NULL);
free(result);
setenv("XDG_CONFIG_DIRS", ".:.:/etc/xdg:.:/:", 1);
result = xdg_config_dirs();
TEST_STREQUAL(result[0], "/etc/xdg");
TEST_STREQUAL(result[1], "/");
TEST_EQUAL(result[2], NULL);
free(result);
setenv("XDG_CONFIG_DIRS", ":", 1);
result = xdg_config_dirs();
TEST_EQUAL(result[0], NULL);
free(result);
if (old_var) {
setenv("XDG_CONFIG_DIRS", old_var, 1);
free(old_var);
}
}
/// Search for config file under a base directory
FILE *open_config_file_at(const char *base, char **out_path) {
static const char *config_paths[] = {"/picom.conf", "/picom/picom.conf",
@ -251,6 +160,36 @@ parse_cfg_condlst_opct(options_t *opt, const config_t *pcfg, const char *name) {
}
}
/**
* Parse a window shader rule list in configuration file.
*/
static inline void parse_cfg_condlst_shader(options_t *opt, const config_t *pcfg,
const char *name, const char *include_dir) {
config_setting_t *setting = config_lookup(pcfg, name);
if (setting) {
// Parse an array of options
if (config_setting_is_array(setting)) {
int i = config_setting_length(setting);
while (i--) {
if (!parse_rule_window_shader(
&opt->window_shader_fg_rules,
config_setting_get_string_elem(setting, i),
include_dir)) {
exit(1);
}
}
}
// Treat it as a single pattern if it's a string
else if (config_setting_type(setting) == CONFIG_TYPE_STRING) {
if (!parse_rule_window_shader(&opt->window_shader_fg_rules,
config_setting_get_string(setting),
include_dir)) {
exit(1);
}
}
}
}
static inline void parse_wintype_config(const config_t *cfg, const char *member_name,
win_option_t *o, win_option_mask_t *mask) {
char *str = mstrjoin("wintypes.", member_name);
@ -602,6 +541,16 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
opt->max_brightness = 1.0;
}
// --window-shader-fg
if (config_lookup_string(&cfg, "window-shader-fg", &sval)) {
opt->window_shader_fg =
locate_auxiliary_file("shaders", sval, config_get_include_dir(&cfg));
}
// --window-shader-fg-rule
parse_cfg_condlst_shader(opt, &cfg, "window-shader-fg-rule",
config_get_include_dir(&cfg));
// --glx-use-gpushader4
if (config_lookup_bool(&cfg, "glx-use-gpushader4", &ival)) {
log_error("glx-use-gpushader4 has been removed, please remove it "

View File

@ -346,7 +346,17 @@ static void usage(const char *argv0, int ret) {
"\n"
"--transparent-clipping\n"
" Make transparent windows clip other windows like non-transparent windows\n"
" do, instead of blending on top of them\n";
" do, instead of blending on top of them\n"
"\n"
"--window-shader-fg shader\n"
" Specify GLSL fragment shader path for rendering window contents. Only\n"
" works when `--experimental-backends` is enabled.\n"
"\n"
"--window-shader-fg-rule shader:condition\n"
" Specify GLSL fragment shader path for rendering window contents using\n"
" patterns. Pattern should be in the format of `SHADER PATH:PATTERN`,\n"
" similar to `--opacity-rule`. Only works when `--experimental-backends`\n"
" is enabled.\n";
FILE *f = (ret ? stderr : stdout);
fprintf(f, usage_text, argv0);
#undef WARNING_DISABLED
@ -442,6 +452,8 @@ static const struct option longopts[] = {
{"corner-radius", required_argument, NULL, 333},
{"rounded-corners-exclude", required_argument, NULL, 334},
{"clip-shadow-above", required_argument, NULL, 335},
{"window-shader-fg", required_argument, NULL, 336},
{"window-shader-fg-rule", required_argument, NULL, 337},
{"experimental-backends", no_argument, NULL, 733},
{"monitor-repaint", no_argument, NULL, 800},
{"diagnostics", no_argument, NULL, 801},
@ -792,6 +804,24 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
case 317:
opt->glx_fshader_win_str = strdup(optarg);
break;
case 336: {
// --window-shader-fg
scoped_charp cwd = getcwd(NULL, 0);
opt->window_shader_fg =
locate_auxiliary_file("shaders", optarg, cwd);
if (!opt->window_shader_fg) {
exit(1);
}
break;
}
case 337: {
// --window-shader-fg-rule
scoped_charp cwd = getcwd(NULL, 0);
if (!parse_rule_window_shader(&opt->window_shader_fg_rules, optarg, cwd)) {
exit(1);
}
break;
}
case 321: {
enum log_level tmp_level = string_to_log_level(optarg);
if (tmp_level == LOG_LEVEL_INVALID) {
@ -803,13 +833,8 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
}
P_CASEBOOL(319, no_x_selection);
P_CASEBOOL(323, use_damage);
case 324:
opt->use_damage = false;
break;
case 325:
opt->vsync = false;
break;
case 324: opt->use_damage = false; break;
case 325: opt->vsync = false; break;
case 326:
opt->max_brightness = atof(optarg);
break;
@ -850,7 +875,9 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
break;
P_CASEBOOL(733, experimental_backends);
P_CASEBOOL(800, monitor_repaint);
case 801: opt->print_diagnostics = true; break;
case 801:
opt->print_diagnostics = true;
break;
P_CASEBOOL(802, debug_mode);
P_CASEBOOL(803, no_ewmh_fullscreen);
default: usage(argv[0], 1); break;
@ -895,6 +922,25 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
return false;
}
if (opt->glx_fshader_win_str && opt->experimental_backends) {
log_warn("--glx-fshader-win has been replaced by "
"\"--window-shader-fg\" for the experimental backends.");
}
if (opt->window_shader_fg || opt->window_shader_fg_rules) {
if (!opt->experimental_backends || opt->backend != BKEND_GLX) {
log_warn("The new window shader interface only works with the "
"experimental glx backend.%s",
(opt->backend == BKEND_GLX) ? " You may want to use "
"\"--glx-fshader-win\" "
"instead on the legacy "
"glx backend."
: "");
opt->window_shader_fg = NULL;
c2_list_free(&opt->window_shader_fg_rules, free);
}
}
// Range checking and option assignments
opt->fade_delta = max2(opt->fade_delta, 1);
opt->shadow_radius = max2(opt->shadow_radius, 0);

View File

@ -431,6 +431,13 @@ static void destroy_backend(session_t *ps) {
free_paint(ps, &w->paint);
}
HASH_ITER2(ps->shaders, shader) {
if (shader->backend_shader != NULL) {
// Free the shader here.
shader->backend_shader = NULL;
}
}
if (ps->backend_data && ps->root_image) {
ps->backend_data->ops->release_image(ps->backend_data, ps->root_image);
ps->root_image = NULL;
@ -1526,6 +1533,62 @@ static void config_file_change_cb(void *_ps) {
reset_enable(ps->loop, NULL, 0);
}
static bool load_shader_source(session_t *ps, const char *path) {
if (!path) {
// Using the default shader.
return false;
}
log_info("Loading shader source from %s", path);
struct shader_info *shader = NULL;
HASH_FIND_STR(ps->shaders, path, shader);
if (shader) {
log_debug("Shader already loaded, reusing");
return false;
}
shader = ccalloc(1, struct shader_info);
shader->key = strdup(path);
HASH_ADD_KEYPTR(hh, ps->shaders, shader->key, strlen(shader->key), shader);
FILE *f = fopen(path, "r");
if (!f) {
log_error("Failed to open custom shader file: %s", path);
goto err;
}
struct stat statbuf;
if (fstat(fileno(f), &statbuf) < 0) {
log_error("Failed to access custom shader file: %s", path);
goto err;
}
auto num_bytes = (size_t)statbuf.st_size;
shader->source = ccalloc(num_bytes + 1, char);
auto read_bytes = fread(shader->source, sizeof(char), num_bytes, f);
if (read_bytes < num_bytes || ferror(f)) {
// This is a difficult to hit error case, review thoroughly.
log_error("Failed to read custom shader at %s. (read %lu bytes, expected "
"%lu bytes)",
path, read_bytes, num_bytes);
goto err;
}
return false;
err:
HASH_DEL(ps->shaders, shader);
if (f) {
fclose(f);
}
free(shader->source);
free(shader->key);
free(shader);
return true;
}
static bool load_shader_source_for_condition(const c2_lptr_t *cond, void *data) {
return load_shader_source(data, c2_list_get_data(cond));
}
/**
* Initialize a session.
*
@ -1753,6 +1816,10 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
return NULL;
}
if (ps->o.window_shader_fg) {
log_debug("Default window shader: \"%s\"", ps->o.window_shader_fg);
}
if (ps->o.logpath) {
auto l = file_logger_new(ps->o.logpath);
if (l) {
@ -1802,6 +1869,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
c2_list_postprocess(ps, ps->o.fade_blacklist) &&
c2_list_postprocess(ps, ps->o.blur_background_blacklist) &&
c2_list_postprocess(ps, ps->o.invert_color_list) &&
c2_list_postprocess(ps, ps->o.window_shader_fg_rules) &&
c2_list_postprocess(ps, ps->o.opacity_rules) &&
c2_list_postprocess(ps, ps->o.rounded_corners_blacklist) &&
c2_list_postprocess(ps, ps->o.focus_blacklist))) {
@ -1809,6 +1877,22 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
"might not work");
}
// Load shader source file specified in the shader rules
if (c2_list_foreach(ps->o.window_shader_fg_rules, load_shader_source_for_condition, ps)) {
log_error("Failed to load shader source file for some of the window "
"shader rules");
}
if (load_shader_source(ps, ps->o.window_shader_fg)) {
log_error("Failed to load window shader source file");
}
if (log_get_level_tls() <= LOG_LEVEL_DEBUG) {
HASH_ITER2(ps->shaders, shader) {
log_debug("Shader %s:", shader->key);
log_debug("%s", shader->source);
}
}
ps->gaussian_map = gaussian_kernel_autodetect_deviation(ps->o.shadow_radius);
sum_kernel_preprocess(ps->gaussian_map);
@ -2164,16 +2248,17 @@ static void session_destroy(session_t *ps) {
list_init_head(&ps->window_stack);
// Free blacklists
c2_list_free(&ps->o.shadow_blacklist);
c2_list_free(&ps->o.shadow_clip_list);
c2_list_free(&ps->o.fade_blacklist);
c2_list_free(&ps->o.focus_blacklist);
c2_list_free(&ps->o.invert_color_list);
c2_list_free(&ps->o.blur_background_blacklist);
c2_list_free(&ps->o.opacity_rules);
c2_list_free(&ps->o.paint_blacklist);
c2_list_free(&ps->o.unredir_if_possible_blacklist);
c2_list_free(&ps->o.rounded_corners_blacklist);
c2_list_free(&ps->o.shadow_blacklist, NULL);
c2_list_free(&ps->o.shadow_clip_list, NULL);
c2_list_free(&ps->o.fade_blacklist, NULL);
c2_list_free(&ps->o.focus_blacklist, NULL);
c2_list_free(&ps->o.invert_color_list, NULL);
c2_list_free(&ps->o.blur_background_blacklist, NULL);
c2_list_free(&ps->o.opacity_rules, NULL);
c2_list_free(&ps->o.paint_blacklist, NULL);
c2_list_free(&ps->o.unredir_if_possible_blacklist, NULL);
c2_list_free(&ps->o.rounded_corners_blacklist, NULL);
c2_list_free(&ps->o.window_shader_fg_rules, free);
// Free tracked atom list
{
@ -2224,6 +2309,17 @@ static void session_destroy(session_t *ps) {
free(ps->o.glx_fshader_win_str);
free_xinerama_info(ps);
// Release custom window shaders
free(ps->o.window_shader_fg);
struct shader_info *shader, *tmp;
HASH_ITER(hh, ps->shaders, shader, tmp) {
HASH_DEL(ps->shaders, shader);
assert(shader->backend_shader == NULL);
free(shader->source);
free(shader->key);
free(shader);
}
#ifdef CONFIG_VSYNC_DRM
// Close file opened for DRM VSync
if (ps->drm_fd >= 0) {

View File

@ -417,3 +417,10 @@ wintypes:
popup_menu = { opacity = 0.8; }
dropdown_menu = { opacity = 0.8; }
};
window-shader-fg-rule =
[
"shader.frag:name = 'test'",
" shader.frag :name = 'a'",
"default:name = 'b'"
]

View File

@ -0,0 +1 @@
vec4 window_shader() {}