backend: prepare to pluginify the backends

Instead of a fixed table, allow an arbitrary number of backends to be
registered through `backend_register`.

Slightly refactored configuration validation.

As a side-effect, you now have to explicitly specify a backend, because
due to the dynamic nature of backends, there is no way to choose a
default.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2024-05-23 05:53:43 +01:00
parent ca12415e22
commit e512542d71
No known key found for this signature in database
GPG Key ID: D3A4405BE6CC17F4
18 changed files with 396 additions and 222 deletions

View File

@ -32,6 +32,7 @@
* `override_redirect` in rules now only matches top-level windows that doesn't have a client window. Some window managers (e.g. awesome) set override_redirect for all window manager frame windows, causing this rule to match against everything (#625).
* Marginally improve performance when resizing/opening/closing windows. (#1190)
* Type and format specifiers are no longer used in rules. These specifiers are what you put after the colon (':') in rules, e.g. the `:32c` in `"_GTK_FRAME_EXTENTS@:32c"`. Now this information is ignored and the property is matched regardless of format or type.
* `backend` is now a required option. picom will not start if one is not specified explicitly.
## Deprecated features

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#include <inttypes.h>
#include <xcb/sync.h>
#include <xcb/xcb.h>
@ -13,20 +14,69 @@
#include "win.h"
#include "x.h"
extern struct backend_operations xrender_ops, dummy_ops;
#ifdef CONFIG_OPENGL
extern struct backend_operations glx_ops;
extern struct backend_operations egl_ops;
#endif
static struct backend_info {
UT_hash_handle hh;
const char *name;
struct backend_base *(*init)(session_t *ps, xcb_window_t target);
bool can_present;
} *backend_registry = NULL;
struct backend_operations *backend_list[NUM_BKEND] = {
[BKEND_XRENDER] = &xrender_ops,
[BKEND_DUMMY] = &dummy_ops,
#ifdef CONFIG_OPENGL
[BKEND_GLX] = &glx_ops,
[BKEND_EGL] = &egl_ops,
#endif
};
bool backend_register(uint64_t major, uint64_t minor, const char *name,
struct backend_base *(*init)(session_t *ps, xcb_window_t target),
bool can_present) {
if (major != PICOM_BACKEND_MAJOR) {
log_error("Backend %s has incompatible major version %" PRIu64
", expected %lu",
name, major, PICOM_BACKEND_MAJOR);
return false;
}
if (minor > PICOM_BACKEND_MINOR) {
log_error("Backend %s has incompatible minor version %" PRIu64
", expected %lu",
name, minor, PICOM_BACKEND_MINOR);
return false;
}
struct backend_info *info = NULL;
HASH_FIND_STR(backend_registry, name, info);
if (info) {
log_error("Backend %s is already registered", name);
return false;
}
info = cmalloc(struct backend_info);
info->name = name;
info->init = init;
info->can_present = can_present;
HASH_ADD_KEYPTR(hh, backend_registry, info->name, strlen(info->name), info);
return true;
}
struct backend_info *backend_find(const char *name) {
struct backend_info *info = NULL;
HASH_FIND_STR(backend_registry, name, info);
return info;
}
struct backend_base *
backend_init(struct backend_info *info, session_t *ps, xcb_window_t target) {
return info->init(ps, target);
}
struct backend_info *backend_iter(void) {
return backend_registry;
}
struct backend_info *backend_iter_next(struct backend_info *info) {
return info->hh.next;
}
const char *backend_name(struct backend_info *info) {
return info->name;
}
bool backend_can_present(struct backend_info *info) {
return info->can_present;
}
void handle_device_reset(session_t *ps) {
log_error("Device reset detected");

View File

@ -13,6 +13,10 @@
#include "types.h"
#include "x.h"
#define PICOM_BACKEND_MAJOR (1UL)
#define PICOM_BACKEND_MINOR (0UL)
#define PICOM_BACKEND_MAKE_VERSION(major, minor) ((major) * 1000 + (minor))
typedef struct session session_t;
struct managed_win;
@ -450,12 +454,30 @@ struct backend_operations {
enum device_status (*device_status)(backend_t *backend_data);
};
extern struct backend_operations *backend_list[];
struct backend_info;
bool backend_execute(struct backend_base *backend, image_handle target, unsigned ncmds,
const struct backend_command cmds[ncmds]);
/// Register a new backend, `major` and `minor` should be the version of the picom backend
/// interface. You should just pass `PICOM_BACKEND_MAJOR` and `PICOM_BACKEND_MINOR` here.
/// `name` is the name of the backend, `init` is the function to initialize the backend,
/// `can_present` should be true if the backend can present the back buffer to the screen,
/// false otherwise (e.g. if the backend does off screen rendering, etc.)
bool backend_register(uint64_t major, uint64_t minor, const char *name,
struct backend_base *(*init)(session_t *ps, xcb_window_t target),
bool can_present);
struct backend_info *backend_find(const char *name);
struct backend_base *
backend_init(struct backend_info *info, session_t *ps, xcb_window_t target);
struct backend_info *backend_iter(void);
struct backend_info *backend_iter_next(struct backend_info *info);
const char *backend_name(struct backend_info *info);
bool backend_can_present(struct backend_info *info);
void log_backend_command_(enum log_level level, const char *func,
const struct backend_command *cmd);
#define log_backend_command(level, cmd) \
log_backend_command_(LOG_LEVEL_##level, __func__, &(cmd));
/// Define a backend entry point. (Note constructor priority 202 is used here because 1xx
/// is reversed by test.h, and 201 is used for logging initialization.)
#define BACKEND_ENTRYPOINT(func) static void __attribute__((constructor(202))) func(void)

View File

@ -227,3 +227,10 @@ struct backend_operations dummy_ops = {
.destroy_blur_context = dummy_destroy_blur_context,
.get_blur_size = dummy_get_blur_size,
};
BACKEND_ENTRYPOINT(dummy_register) {
if (!backend_register(PICOM_BACKEND_MAJOR, PICOM_BACKEND_MINOR, "dummy",
dummy_ops.init, false)) {
log_error("Failed to register dummy backend");
}
}

View File

@ -120,6 +120,8 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
return NULL;
}
log_warn("The egl backend is still experimental, use with care.");
gd = ccalloc(1, struct egl_data);
gd->display = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT, ps->c.dpy,
(EGLint[]){
@ -389,3 +391,10 @@ void eglext_init(EGLDisplay dpy) {
#endif
#undef check_ext
}
BACKEND_ENTRYPOINT(egl_register) {
if (!backend_register(PICOM_BACKEND_MAJOR, PICOM_BACKEND_MINOR, "egl",
egl_ops.init, true)) {
log_error("Failed to register egl backend");
}
}

View File

@ -575,3 +575,10 @@ void glxext_init(Display *dpy, int screen) {
#endif
#undef check_ext
}
BACKEND_ENTRYPOINT(glx_register) {
if (!backend_register(PICOM_BACKEND_MAJOR, PICOM_BACKEND_MINOR, "glx",
glx_ops.init, true)) {
log_error("Failed to register glx backend");
}
}

View File

@ -867,7 +867,12 @@ static void xrender_get_blur_size(void *blur_context, int *width, int *height) {
struct backend_operations xrender_ops;
static backend_t *xrender_init(session_t *ps, xcb_window_t target) {
if (ps->o.dithered_present) {
log_warn("\"dithered-present\" is not supported by the xrender backend.");
log_warn("\"dithered-present\" is not supported by the xrender backend, "
"it will be ignored.");
}
if (ps->o.max_brightness < 1.0) {
log_warn("\"max-brightness\" is not supported by the xrender backend, it "
"will be ignored.");
}
auto xd = ccalloc(1, struct xrender_data);
@ -1043,4 +1048,11 @@ struct backend_operations xrender_ops = {
// end
};
BACKEND_ENTRYPOINT(xrender_register) {
if (!backend_register(PICOM_BACKEND_MAJOR, PICOM_BACKEND_MINOR, "xrender",
xrender_ops.init, true)) {
log_error("Failed to register xrender backend");
}
}
// vim: set noet sw=8 ts=8:

View File

@ -462,7 +462,7 @@ static inline xcb_window_t get_tgt_window(session_t *ps) {
* Check if current backend uses GLX.
*/
static inline bool bkend_use_glx(session_t *ps) {
return BKEND_GLX == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend;
return BKEND_GLX == ps->o.legacy_backend || BKEND_XR_GLX_HYBRID == ps->o.legacy_backend;
}
/**

View File

@ -635,8 +635,8 @@ void *parse_window_shader_prefix_with_cwd(const char *src, const char **end, voi
bool parse_config(options_t *opt, const char *config_file) {
// clang-format off
*opt = (struct options){
.backend = BKEND_XRENDER,
.legacy_backends = false,
.legacy_backend = BKEND_XRENDER,
.use_legacy_backends = false,
.glx_no_stencil = false,
.mark_wmwin_focused = false,
.mark_ovredir_focused = false,

View File

@ -158,11 +158,13 @@ typedef struct options {
bool debug_mode;
// === General ===
/// Use the legacy backends?
bool legacy_backends;
bool use_legacy_backends;
/// Path to write PID to.
char *write_pid_path;
/// The backend in use.
int backend;
/// Name of the backend
struct backend_info *backend;
/// The backend in use (for legacy backends).
int legacy_backend;
/// Log level.
int log_level;
/// Whether to sync X drawing with X Sync fence to avoid certain delay
@ -407,7 +409,6 @@ static inline attr_pure int parse_backend(const char *str) {
"version will be removed soon.");
return BKEND_XR_GLX_HYBRID;
}
log_error("Invalid backend argument: %s", str);
return NUM_BKEND;
}

View File

@ -743,9 +743,10 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) {
lcfg_lookup_bool(&cfg, "vsync", &opt->vsync);
// --backend
if (config_lookup_string(&cfg, "backend", &sval)) {
opt->backend = parse_backend(sval);
if (opt->backend >= NUM_BKEND) {
log_fatal("Cannot parse backend");
opt->legacy_backend = parse_backend(sval);
opt->backend = backend_find(sval);
if (opt->legacy_backend >= NUM_BKEND && opt->backend == NULL) {
log_fatal("Invalid backend: %s", sval);
goto out;
}
}

View File

@ -853,7 +853,6 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus
dbus_set_error_const(err, DBUS_ERROR_INVALID_ARGS, NULL);
return DBUS_HANDLER_RESULT_HANDLED;
}
assert((size_t)ps->o.backend < sizeof(BACKEND_STRS) / sizeof(BACKEND_STRS[0]));
#define append(tgt, type, ret) \
if (!strcmp(#tgt, target)) { \
@ -864,6 +863,18 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus
}
#define append_session_option(tgt, type) append(tgt, type, ps->o.tgt)
if (!strcmp("backend", target)) {
assert(!ps->o.use_legacy_backends ||
(size_t)ps->o.legacy_backend < ARR_SIZE(BACKEND_STRS));
const char *name = ps->o.use_legacy_backends
? BACKEND_STRS[ps->o.legacy_backend]
: backend_name(ps->o.backend);
if (reply != NULL && !cdbus_append_string(reply, name)) {
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
return DBUS_HANDLER_RESULT_HANDLED;
}
append(version, string, PICOM_VERSION);
append(pid, int32, getpid());
append(display, string, DisplayString(ps->c.dpy));
@ -875,7 +886,7 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus
append(unredir_if_possible_delay, int32, (int32_t)ps->o.unredir_if_possible_delay);
append(refresh_rate, int32, 0);
append(sw_opti, boolean, false);
append(backend, string, BACKEND_STRS[ps->o.backend]);
append(backend, string, BACKEND_STRS[ps->o.legacy_backend]);
append_session_option(unredir_if_possible, boolean);
append_session_option(write_pid_path, string);

View File

@ -44,17 +44,17 @@ void print_diagnostics(session_t *ps, const char *config_file, bool compositor_r
printf("\n### Drivers (inaccurate):\n\n");
print_drivers(ps->drivers);
for (int i = 0; i < NUM_BKEND; i++) {
if (backend_list[i] && backend_list[i]->diagnostics) {
printf("\n### Backend: %s\n\n", BACKEND_STRS[i]);
auto data = backend_list[i]->init(ps, session_get_target_window(ps));
if (!data) {
printf(" Cannot initialize this backend\n");
} else {
backend_list[i]->diagnostics(data);
backend_list[i]->deinit(data);
}
for (auto i = backend_iter(); i; i = backend_iter_next(i)) {
auto backend_data = backend_init(i, ps, session_get_target_window(ps));
if (!backend_data) {
printf(" Cannot initialize backend %s\n", backend_name(i));
continue;
}
if (backend_data->ops->diagnostics) {
printf("\n### Backend: %s\n\n", backend_name(i));
backend_data->ops->diagnostics(backend_data);
}
backend_data->ops->deinit(backend_data);
}
}

View File

@ -642,7 +642,7 @@ static inline GLuint glx_gen_texture(GLenum tex_tgt, int width, int height) {
*/
bool glx_bind_texture(session_t *ps attr_unused, glx_texture_t **pptex, int x, int y,
int width, int height) {
if (ps->o.backend != BKEND_GLX && ps->o.backend != BKEND_XR_GLX_HYBRID) {
if (ps->o.legacy_backend != BKEND_GLX && ps->o.legacy_backend != BKEND_XR_GLX_HYBRID) {
return true;
}
@ -698,7 +698,7 @@ bool glx_bind_texture(session_t *ps attr_unused, glx_texture_t **pptex, int x, i
*/
bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, int width,
int height, bool repeat, const struct glx_fbconfig_info *fbcfg) {
if (ps->o.backend != BKEND_GLX && ps->o.backend != BKEND_XR_GLX_HYBRID) {
if (ps->o.legacy_backend != BKEND_GLX && ps->o.legacy_backend != BKEND_XR_GLX_HYBRID) {
return true;
}

View File

@ -298,6 +298,18 @@ store_benchmark_wid(const struct picom_option * /*opt*/, const struct picom_arg
return true;
}
static bool store_backend(const struct picom_option * /*opt*/, const struct picom_arg * /*arg*/,
const char *arg_str, void *output) {
struct options *opt = (struct options *)output;
opt->legacy_backend = parse_backend(arg_str);
opt->backend = backend_find(arg_str);
if (opt->legacy_backend == NUM_BKEND && opt->backend == NULL) {
log_error("Invalid backend: %s", arg_str);
return false;
}
return true;
}
#define WINDOW_SHADER_RULE \
{ .parse_prefix = parse_window_shader_prefix_with_cwd, .free_value = free, }
@ -380,7 +392,7 @@ static const struct picom_option picom_options[] = {
"rendered screen. Reduces banding artifacts, but might cause performance "
"degradation. Only works with OpenGL."},
[341] = {"no-frame-pacing" , DISABLE(frame_pacing) , "Disable frame pacing. This might increase the latency."},
[733] = {"legacy-backends" , ENABLE(legacy_backends) , "Use deprecated version of the backends."},
[733] = {"legacy-backends" , ENABLE(use_legacy_backends) , "Use deprecated version of the backends."},
[800] = {"monitor-repaint" , ENABLE(monitor_repaint) , "Highlight the updated area of the screen. For debugging."},
[801] = {"diagnostics" , ENABLE(print_diagnostics) , "Print diagnostic information"},
[802] = {"debug-mode" , ENABLE(debug_mode) , "Render into a separate window, and don't take over the screen. Useful when "
@ -405,7 +417,7 @@ static const struct picom_option picom_options[] = {
[259] = {"shadow-blue" , FLOAT(shadow_blue, 0, 1) , "Blue color value of shadow (0.0 - 1.0, defaults to 0)."},
[261] = {"inactive-dim" , FLOAT(inactive_dim, 0, 1) , "Dim inactive windows. (0.0 - 1.0, defaults to 0)"},
[283] = {"blur-background" , FIXED(blur_method, BLUR_METHOD_KERNEL) , "Blur background of semi-transparent / ARGB windows. May impact performance"},
[290] = {"backend" , PARSE_WITH(parse_backend, NUM_BKEND, backend) , "Backend. Possible values are: " BACKENDS},
[290] = {"backend" , DO(store_backend) , "Backend. Possible values are: " BACKENDS},
[293] = {"benchmark" , INTEGER(benchmark, 0, INT_MAX) , "Benchmark mode. Repeatedly paint until reaching the specified cycles."},
[297] = {"active-opacity" , FLOAT(active_opacity, 0, 1) , "Default opacity for active windows. (0.0 - 1.0)"},
[302] = {"resize-damage" , INTEGER(resize_damage, INT_MIN, INT_MAX)}, // only used by legacy backends
@ -483,7 +495,7 @@ static const struct picom_option picom_options[] = {
['z'] = {"clear-shadow" , SAY_DEPRECATED(false, CLEAR_SHADOW_DEPRECATION , IGNORE(no_argument))},
[272] = {"xinerama-shadow-crop", SAY_DEPRECATED(false, "Use --crop-shadow-to-monitor instead.", ENABLE(crop_shadow_to_monitor))},
[287] = {"logpath" , SAY_DEPRECATED(false, "Use --log-file instead." , STRING(logpath))},
[289] = {"opengl" , SAY_DEPRECATED(false, "Use --backend=glx instead." , FIXED(backend, BKEND_GLX))},
[289] = {"opengl" , SAY_DEPRECATED(false, "Use --backend=glx instead." , FIXED(legacy_backend, BKEND_GLX))},
[305] = {"shadow-exclude-reg" , SAY_DEPRECATED(true, "Use --clip-shadow-above instead." , REJECT(required_argument))},
#undef CLEAR_SHADOW_DEPRECATION
@ -721,6 +733,143 @@ err:
return true;
}
static bool sanitize_options(struct options *opt) {
if (opt->use_legacy_backends) {
if (opt->legacy_backend == BKEND_EGL) {
log_error("The egl backend is not supported with "
"--legacy-backends");
return false;
}
if (opt->monitor_repaint && opt->legacy_backend != BKEND_XRENDER) {
log_warn("For legacy backends, --monitor-repaint is only "
"implemented for "
"xrender.");
}
if (opt->debug_mode) {
log_error("Debug mode does not work with the legacy backends.");
return false;
}
if (opt->transparent_clipping) {
log_error("Transparent clipping does not work with the legacy "
"backends");
return false;
}
if (opt->max_brightness < 1.0) {
log_warn("--max-brightness is not supported by the legacy "
"backends. Falling back to 1.0.");
opt->max_brightness = 1.0;
}
if (opt->blur_method == BLUR_METHOD_DUAL_KAWASE) {
log_warn("Dual-kawase blur is not implemented by the legacy "
"backends.");
opt->blur_method = BLUR_METHOD_NONE;
}
if (opt->number_of_scripts > 0) {
log_warn("Custom animations are not supported by the legacy "
"backends. Disabling animations.");
for (size_t i = 0; i < ARR_SIZE(opt->animations); i++) {
opt->animations[i].script = NULL;
}
for (int i = 0; i < opt->number_of_scripts; i++) {
script_free(opt->all_scripts[i]);
}
free(opt->all_scripts);
opt->all_scripts = NULL;
opt->number_of_scripts = 0;
}
if (opt->window_shader_fg || opt->window_shader_fg_rules) {
log_warn("The new shader interface is not supported by the "
"legacy glx backend. You may want to use "
"--glx-fshader-win instead.");
opt->window_shader_fg = NULL;
c2_list_free(&opt->window_shader_fg_rules, free);
}
if (opt->legacy_backend == BKEND_XRENDER) {
bool has_neg = false;
for (int i = 0; i < opt->blur_kernel_count; i++) {
auto kernel = opt->blur_kerns[i];
for (int j = 0; j < kernel->h * kernel->w; j++) {
if (kernel->data[j] < 0) {
has_neg = true;
break;
}
}
if (has_neg) {
log_warn("A convolution kernel with negative "
"values may not work properly under X "
"Render backend.");
break;
}
}
}
} else {
if (opt->backend == NULL) {
auto valid_backend_name =
backend_find(BACKEND_STRS[opt->legacy_backend]) != NULL;
if (!valid_backend_name) {
log_error("Backend \"%s\" is only available as part of "
"the legacy backends.",
BACKEND_STRS[opt->legacy_backend]);
} else {
// If the backend name is a valid new backend, then
// it must not have been specified by the user, because
// otherwise opt->backend wouldn't be NULL.
log_error("Backend not specified. You must choose one "
"explicitly. Valid ones are: ");
for (auto i = backend_iter(); i; i = backend_iter_next(i)) {
log_error("\t%s", backend_name(i));
}
}
return false;
}
if (opt->glx_fshader_win_str) {
log_warn("--glx-fshader-win has been replaced by "
"\"--window-shader-fg\" for the new backends.");
}
if (opt->max_brightness < 1.0 && opt->use_damage) {
log_warn("--max-brightness requires --no-use-damage. "
"Falling back to 1.0.");
opt->max_brightness = 1.0;
}
}
if (opt->write_pid_path && *opt->write_pid_path != '/') {
log_warn("--write-pid-path is not an absolute path");
}
// Sanitize parameters for dual-filter kawase blur
if (opt->blur_method == BLUR_METHOD_DUAL_KAWASE) {
if (opt->blur_strength <= 0 && opt->blur_radius > 500) {
log_warn("Blur radius >500 not supported by dual_kawase "
"method, "
"capping to 500.");
opt->blur_radius = 500;
}
if (opt->blur_strength > 20) {
log_warn("Blur strength >20 not supported by dual_kawase "
"method, "
"capping to 20.");
opt->blur_strength = 20;
}
}
if (opt->resize_damage < 0) {
log_warn("Negative --resize-damage will not work correctly.");
}
return true;
}
/**
* Process arguments and configuration files.
*/
@ -748,83 +897,22 @@ bool get_cfg(options_t *opt, int argc, char *const *argv) {
}
log_set_level_tls(opt->log_level);
if (opt->monitor_repaint && opt->backend != BKEND_XRENDER && opt->legacy_backends) {
log_warn("--monitor-repaint has no effect when backend is not xrender");
}
if (opt->window_shader_fg) {
scoped_charp cwd = getcwd(NULL, 0);
scoped_charp tmp = opt->window_shader_fg;
opt->window_shader_fg = locate_auxiliary_file("shaders", tmp, cwd);
if (!opt->window_shader_fg) {
log_error("Couldn't find the specified window shader file \"%s\"", tmp);
log_error("Couldn't find the specified window shader "
"file \"%s\"",
tmp);
return false;
}
}
if (opt->write_pid_path && *opt->write_pid_path != '/') {
log_warn("--write-pid-path is not an absolute path");
}
if (opt->backend == BKEND_EGL) {
if (opt->legacy_backends) {
log_error("The egl backend is not supported with "
"--legacy-backends");
return false;
}
log_warn("The egl backend is still experimental, use with care.");
}
if (!opt->legacy_backends && !backend_list[opt->backend]) {
log_error("Backend \"%s\" is only available as part of the legacy "
"backends.",
BACKEND_STRS[opt->backend]);
if (!sanitize_options(opt)) {
return false;
}
if (opt->debug_mode && opt->legacy_backends) {
log_error("Debug mode does not work with the legacy backends.");
return false;
}
if (opt->transparent_clipping && opt->legacy_backends) {
log_error("Transparent clipping does not work with the legacy "
"backends");
return false;
}
if (opt->glx_fshader_win_str && !opt->legacy_backends) {
log_warn("--glx-fshader-win has been replaced by "
"\"--window-shader-fg\" for the new backends.");
}
if (opt->window_shader_fg || opt->window_shader_fg_rules) {
if (opt->backend == BKEND_XRENDER || opt->legacy_backends) {
log_warn(opt->backend == BKEND_XRENDER
? "Shader interface is not supported by the xrender "
"backend."
: "The new shader interface is not supported by the "
"legacy glx backend. You may want to use "
"--glx-fshader-win instead.");
opt->window_shader_fg = NULL;
c2_list_free(&opt->window_shader_fg_rules, free);
}
}
// Range checking and option assignments
if (opt->max_brightness < 1.0) {
if (opt->backend == BKEND_XRENDER || opt->legacy_backends) {
log_warn("--max-brightness is not supported by the %s backend. "
"Falling back to 1.0.",
opt->backend == BKEND_XRENDER ? "xrender" : "legacy glx");
opt->max_brightness = 1.0;
} else if (opt->use_damage) {
log_warn("--max-brightness requires --no-use-damage. Falling "
"back to 1.0.");
opt->max_brightness = 1.0;
}
}
// --blur-background-frame implies --blur-background
if (opt->blur_background_frame && opt->blur_method == BLUR_METHOD_NONE) {
opt->blur_method = BLUR_METHOD_KERNEL;
@ -846,57 +934,6 @@ bool get_cfg(options_t *opt, int argc, char *const *argv) {
CHECK(opt->blur_kernel_count);
}
// Sanitize parameters for dual-filter kawase blur
if (opt->blur_method == BLUR_METHOD_DUAL_KAWASE) {
if (opt->blur_strength <= 0 && opt->blur_radius > 500) {
log_warn("Blur radius >500 not supported by dual_kawase method, "
"capping to 500.");
opt->blur_radius = 500;
}
if (opt->blur_strength > 20) {
log_warn("Blur strength >20 not supported by dual_kawase method, "
"capping to 20.");
opt->blur_strength = 20;
}
if (opt->legacy_backends) {
log_warn("Dual-kawase blur is not implemented by the legacy "
"backends.");
}
}
if (opt->resize_damage < 0) {
log_warn("Negative --resize-damage will not work correctly.");
}
if (opt->backend == BKEND_XRENDER) {
for (int i = 0; i < opt->blur_kernel_count; i++) {
auto kernel = opt->blur_kerns[i];
for (int j = 0; j < kernel->h * kernel->w; j++) {
if (kernel->data[j] < 0) {
log_warn("A convolution kernel with negative "
"values may not work properly under X "
"Render backend.");
goto check_end;
}
}
}
check_end:;
}
if (opt->legacy_backends && opt->number_of_scripts > 0) {
log_warn("Custom animations are not supported by the legacy "
"backends. Disabling animations.");
for (size_t i = 0; i < ARR_SIZE(opt->animations); i++) {
opt->animations[i].script = NULL;
}
for (int i = 0; i < opt->number_of_scripts; i++) {
script_free(opt->all_scripts[i]);
}
free(opt->all_scripts);
opt->all_scripts = NULL;
opt->number_of_scripts = 0;
}
if (opt->fading_enable) {
generate_fading_config(opt);
}
@ -918,8 +955,8 @@ void options_postprocess_c2_lists(struct c2_state *state, struct x_connection *c
c2_list_postprocess(state, c->c, option->corner_radius_rules) &&
c2_list_postprocess(state, c->c, option->focus_blacklist) &&
c2_list_postprocess(state, c->c, option->transparent_clipping_blacklist))) {
log_error("Post-processing of conditionals failed, some of your rules "
"might not work");
log_error("Post-processing of conditionals failed, some of your "
"rules might not work");
}
}

View File

@ -658,18 +658,16 @@ static int mark_pixmap_stale(struct win *w, void *data) {
/// Init the backend and bind all the window pixmap to backend images
static bool initialize_backend(session_t *ps) {
if (!ps->o.legacy_backends) {
if (!ps->o.use_legacy_backends) {
assert(!ps->backend_data);
// Reinitialize win_data
assert(backend_list[ps->o.backend]);
ps->backend_data =
backend_list[ps->o.backend]->init(ps, session_get_target_window(ps));
backend_init(ps->o.backend, ps, session_get_target_window(ps));
if (!ps->backend_data) {
log_fatal("Failed to initialize backend, aborting...");
quit(ps);
return false;
}
ps->backend_data->ops = backend_list[ps->o.backend];
if (!initialize_blur(ps)) {
log_fatal("Failed to prepare for background blur, aborting...");
@ -677,24 +675,30 @@ static bool initialize_backend(session_t *ps) {
}
// Create shaders
HASH_ITER2(ps->shaders, shader) {
assert(shader->backend_shader == NULL);
shader->backend_shader = ps->backend_data->ops->create_shader(
ps->backend_data, shader->source);
if (shader->backend_shader == NULL) {
log_warn("Failed to create shader for shader file %s, "
"this shader will not be used",
shader->key);
} else {
if (ps->backend_data->ops->get_shader_attributes) {
shader->attributes =
ps->backend_data->ops->get_shader_attributes(
ps->backend_data, shader->backend_shader);
if (!ps->backend_data->ops->create_shader && ps->shaders) {
log_warn("Shaders are not supported by selected backend %s, "
"they will be ignored",
backend_name(ps->o.backend));
} else {
HASH_ITER2(ps->shaders, shader) {
assert(shader->backend_shader == NULL);
shader->backend_shader = ps->backend_data->ops->create_shader(
ps->backend_data, shader->source);
if (shader->backend_shader == NULL) {
log_warn("Failed to create shader for shader "
"file %s, this shader will not be used",
shader->key);
} else {
shader->attributes = 0;
if (ps->backend_data->ops->get_shader_attributes) {
shader->attributes =
ps->backend_data->ops->get_shader_attributes(
ps->backend_data,
shader->backend_shader);
}
log_debug("Shader %s has attributes %" PRIu64,
shader->key, shader->attributes);
}
log_debug("Shader %s has attributes %" PRIu64,
shader->key, shader->attributes);
}
}
@ -736,7 +740,7 @@ static void configure_root(session_t *ps) {
bool has_root_change = false;
if (ps->redirected) {
// On root window changes
if (!ps->o.legacy_backends) {
if (!ps->o.use_legacy_backends) {
assert(ps->backend_data);
has_root_change = ps->backend_data->ops->root_change != NULL;
} else {
@ -779,7 +783,7 @@ static void configure_root(session_t *ps) {
ps->damage_ring.cursor = ps->damage_ring.count - 1;
#ifdef CONFIG_OPENGL
// GLX root change callback
if (BKEND_GLX == ps->o.backend && ps->o.legacy_backends) {
if (BKEND_GLX == ps->o.legacy_backend && ps->o.use_legacy_backends) {
glx_on_root_change(ps);
}
#endif
@ -1375,10 +1379,10 @@ uint8_t session_redirection_mode(session_t *ps) {
if (ps->o.debug_mode) {
// If the backend is not rendering to the screen, we don't need to
// take over the screen.
assert(!ps->o.legacy_backends);
assert(!ps->o.use_legacy_backends);
return XCB_COMPOSITE_REDIRECT_AUTOMATIC;
}
if (!ps->o.legacy_backends && !backend_list[ps->o.backend]->present) {
if (!ps->o.use_legacy_backends && !backend_can_present(ps->o.backend)) {
// if the backend doesn't render anything, we don't need to take over the
// screen.
return XCB_COMPOSITE_REDIRECT_AUTOMATIC;
@ -1415,7 +1419,7 @@ static bool redirect_start(session_t *ps) {
return false;
}
if (!ps->o.legacy_backends) {
if (!ps->o.use_legacy_backends) {
assert(ps->backend_data);
ps->damage_ring.count = ps->backend_data->ops->max_buffer_age;
ps->layout_manager =
@ -1431,7 +1435,8 @@ static bool redirect_start(session_t *ps) {
}
ps->frame_pacing = ps->o.frame_pacing && ps->o.vsync;
if ((ps->o.legacy_backends || ps->o.benchmark || !ps->backend_data->ops->last_render_time) &&
if ((ps->o.use_legacy_backends || ps->o.benchmark ||
!ps->backend_data->ops->last_render_time) &&
ps->frame_pacing) {
// Disable frame pacing if we are using a legacy backend or if we are in
// benchmark mode, or if the backend doesn't report render time
@ -1777,7 +1782,7 @@ static void draw_callback_impl(EV_P_ session_t *ps, int revents attr_unused) {
static int paint = 0;
log_verbose("Render start, frame %d", paint);
if (!ps->o.legacy_backends) {
if (!ps->o.use_legacy_backends) {
uint64_t after_damage_us = 0;
now = get_time_timespec();
auto render_start_us =
@ -2211,7 +2216,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
}
}
if (ps->o.legacy_backends) {
if (ps->o.use_legacy_backends) {
ps->shadow_context =
(void *)gaussian_kernel_autodetect_deviation(ps->o.shadow_radius);
sum_kernel_preprocess((conv *)ps->shadow_context);
@ -2332,9 +2337,9 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
// The old backends doesn't have a automatic redirection mode
log_info("The compositor is started in automatic redirection mode.");
assert(!ps->o.legacy_backends);
assert(!ps->o.use_legacy_backends);
if (backend_list[ps->o.backend]->present) {
if (backend_can_present(ps->o.backend)) {
// If the backend has `present`, we couldn't be in automatic
// redirection mode unless we are in debug mode.
assert(ps->o.debug_mode);
@ -2348,7 +2353,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
apply_driver_workarounds(ps, ps->drivers);
// Initialize filters, must be preceded by OpenGL context creation
if (ps->o.legacy_backends && !init_render(ps)) {
if (ps->o.use_legacy_backends && !init_render(ps)) {
log_fatal("Failed to initialize the backend");
exit(1);
}
@ -2371,7 +2376,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
}
}
if (bkend_use_glx(ps) && ps->o.legacy_backends) {
if (bkend_use_glx(ps) && ps->o.use_legacy_backends) {
auto gl_logger = gl_string_marker_logger_new();
if (gl_logger) {
log_info("Enabling gl string marker");
@ -2618,7 +2623,7 @@ static void session_destroy(session_t *ps) {
ps->damage_ring.x_region = XCB_NONE;
}
if (!ps->o.legacy_backends) {
if (!ps->o.use_legacy_backends) {
// backend is deinitialized in unredirect()
assert(ps->backend_data == NULL);
} else {
@ -2635,7 +2640,7 @@ static void session_destroy(session_t *ps) {
// Flush all events
xcb_aux_sync(ps->c.c);
ev_io_stop(ps->loop, &ps->xiow);
if (ps->o.legacy_backends) {
if (ps->o.use_legacy_backends) {
free_conv((conv *)ps->shadow_context);
}
destroy_atoms(ps->atoms);
@ -2684,6 +2689,18 @@ static void session_run(session_t *ps) {
#define PICOM_MAIN(...) main(__VA_ARGS__)
#endif
/// Early initialization of logging system. To catch early logs, especially
/// from backend entrypoint functions.
static void __attribute__((constructor(201))) init_early_logging(void) {
log_init_tls();
log_set_level_tls(LOG_LEVEL_WARN);
auto stderr_logger = stderr_logger_new();
if (stderr_logger != NULL) {
log_add_target_tls(stderr_logger);
}
}
/**
* The function that everybody knows.
*/
@ -2692,16 +2709,6 @@ int PICOM_MAIN(int argc, char **argv) {
// correctly
setlocale(LC_ALL, "");
// Initialize logging system for early logging
log_init_tls();
{
auto stderr_logger = stderr_logger_new();
if (stderr_logger) {
log_add_target_tls(stderr_logger);
}
}
parse_debug_options(&global_debug_options);
int exit_code;

View File

@ -113,7 +113,8 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h
* Check if current backend uses XRender for rendering.
*/
static inline bool bkend_use_xrender(session_t *ps) {
return BKEND_XRENDER == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend;
return BKEND_XRENDER == ps->o.legacy_backend ||
BKEND_XR_GLX_HYBRID == ps->o.legacy_backend;
}
int maximum_buffer_age(session_t *ps) {
@ -154,7 +155,7 @@ static inline void xrfilter_reset(session_t *ps, xcb_render_picture_t p) {
/// Set the input/output clip region of the target buffer (not the actual target!)
static inline void attr_nonnull(1, 2) set_tgt_clip(session_t *ps, region_t *reg) {
switch (ps->o.backend) {
switch (ps->o.legacy_backend) {
case BKEND_XRENDER:
case BKEND_XR_GLX_HYBRID:
x_set_picture_clip_region(&ps->c, ps->tgt_buffer.pict, 0, 0, reg);
@ -243,7 +244,7 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int f
int fullhei, double opacity, bool argb, bool neg, int cr,
xcb_render_picture_t pict, glx_texture_t *ptex, const region_t *reg_paint,
const glx_prog_main_t *pprogram, clip_t *clip) {
switch (ps->o.backend) {
switch (ps->o.legacy_backend) {
case BKEND_XRENDER:
case BKEND_XR_GLX_HYBRID: {
auto alpha_step = (int)(opacity * MAX_ALPHA);
@ -381,7 +382,7 @@ static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) {
}
#ifdef CONFIG_OPENGL
if (BKEND_GLX == ps->o.backend && !glx_tex_bound(ppaint->ptex, XCB_NONE)) {
if (BKEND_GLX == ps->o.legacy_backend && !glx_tex_bound(ppaint->ptex, XCB_NONE)) {
return false;
}
#endif
@ -562,7 +563,7 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint)
dim_opacity *= window_opacity;
}
switch (ps->o.backend) {
switch (ps->o.legacy_backend) {
case BKEND_XRENDER:
case BKEND_XR_GLX_HYBRID: {
auto cval = (uint16_t)(0xffff * dim_opacity);
@ -656,7 +657,7 @@ static bool get_root_tile(session_t *ps) {
ps->root_tile_fill = fill;
ps->root_tile_paint.pixmap = pixmap;
#ifdef CONFIG_OPENGL
if (BKEND_GLX == ps->o.backend) {
if (BKEND_GLX == ps->o.legacy_backend) {
return paint_bind_tex(ps, &ps->root_tile_paint, 0, 0, true, 0, visual, false);
}
#endif
@ -775,7 +776,8 @@ win_paint_shadow(session_t *ps, struct managed_win *w, region_t *reg_paint) {
bool should_clip =
(w->corner_radius > 0) && (!ps->o.wintype_option[w->window_type].full_shadow);
if (should_clip) {
if (ps->o.backend == BKEND_XRENDER || ps->o.backend == BKEND_XR_GLX_HYBRID) {
if (ps->o.legacy_backend == BKEND_XRENDER ||
ps->o.legacy_backend == BKEND_XR_GLX_HYBRID) {
uint32_t max_ntraps = to_u32_checked(w->corner_radius);
xcb_render_trapezoid_t traps[4 * max_ntraps + 3];
uint32_t n = make_rounded_window_shape(
@ -913,7 +915,7 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t
factor_center = pct * 8.0 / (1.1 - pct);
}
switch (ps->o.backend) {
switch (ps->o.legacy_backend) {
case BKEND_XRENDER:
case BKEND_XR_GLX_HYBRID: {
// Normalize blur kernels
@ -1043,14 +1045,14 @@ void paint_all(session_t *ps, struct managed_win *t) {
}
}
if (BKEND_GLX != ps->o.backend) {
if (BKEND_GLX != ps->o.legacy_backend) {
ps->tgt_buffer.pict = x_create_picture_with_visual_and_pixmap(
&ps->c, ps->c.screen_info->root_visual, ps->tgt_buffer.pixmap,
0, 0);
}
}
if (BKEND_XRENDER == ps->o.backend) {
if (BKEND_XRENDER == ps->o.legacy_backend) {
x_set_picture_clip_region(&ps->c, ps->tgt_picture, 0, 0, &region);
}
@ -1170,7 +1172,7 @@ void paint_all(session_t *ps, struct managed_win *t) {
#ifdef CONFIG_OPENGL
// If rounded corners backup the region first
if (w->corner_radius > 0 && ps->o.backend == BKEND_GLX) {
if (w->corner_radius > 0 && ps->o.legacy_backend == BKEND_GLX) {
const int16_t x = w->g.x;
const int16_t y = w->g.y;
auto const wid = to_u16_checked(w->widthb);
@ -1193,7 +1195,7 @@ void paint_all(session_t *ps, struct managed_win *t) {
#ifdef CONFIG_OPENGL
// Rounded corners for XRender is implemented inside render()
// Round window corners
if (w->corner_radius > 0 && ps->o.backend == BKEND_GLX) {
if (w->corner_radius > 0 && ps->o.legacy_backend == BKEND_GLX) {
auto const wid = to_u16_checked(w->widthb);
auto const hei = to_u16_checked(w->heightb);
glx_round_corners_dst(ps, w, w->glx_texture_bg, w->g.x,
@ -1237,7 +1239,7 @@ void paint_all(session_t *ps, struct managed_win *t) {
auto rwidth = to_u16_checked(ps->root_width);
auto rheight = to_u16_checked(ps->root_height);
switch (ps->o.backend) {
switch (ps->o.legacy_backend) {
case BKEND_XRENDER:
if (ps->o.monitor_repaint) {
// Copy the screen content to a new picture, and highlight the
@ -1375,7 +1377,7 @@ static bool init_alpha_picts(session_t *ps) {
}
bool init_render(session_t *ps) {
if (ps->o.backend == BKEND_DUMMY) {
if (ps->o.legacy_backend == BKEND_DUMMY) {
return false;
}
@ -1400,7 +1402,7 @@ bool init_render(session_t *ps) {
}
// Initialize window GL shader
if (BKEND_GLX == ps->o.backend && ps->o.glx_fshader_win_str) {
if (BKEND_GLX == ps->o.legacy_backend && ps->o.glx_fshader_win_str) {
#ifdef CONFIG_OPENGL
if (!glx_load_prog_main(NULL, ps->o.glx_fshader_win_str, &ps->glx_prog_win)) {
return false;
@ -1429,7 +1431,7 @@ bool init_render(session_t *ps) {
ccalloc(ps->o.blur_kernel_count, struct x_convolution_kernel *);
bool ret = false;
if (ps->o.backend == BKEND_GLX) {
if (ps->o.legacy_backend == BKEND_GLX) {
#ifdef CONFIG_OPENGL
ret = glx_init_blur(ps);
#else
@ -1465,7 +1467,7 @@ bool init_render(session_t *ps) {
}
// Initialize our rounded corners fragment shader
if (ps->o.corner_radius > 0 && ps->o.backend == BKEND_GLX) {
if (ps->o.corner_radius > 0 && ps->o.legacy_backend == BKEND_GLX) {
#ifdef CONFIG_OPENGL
if (!glx_init_rounded_corners(ps)) {
log_error("Failed to init rounded corners shader.");

View File

@ -832,11 +832,18 @@ void x_create_convolution_kernel(const conv *kernel, double center,
buf[1] = DOUBLE_TO_XFIXED(kernel->h);
double sum = center;
bool has_neg = false;
for (int i = 0; i < kernel->w * kernel->h; i++) {
if (i == kernel->w * kernel->h / 2) {
// Ignore center
continue;
}
sum += kernel->data[i];
if (kernel->data[i] < 0 && !has_neg) {
has_neg = true;
log_warn("A X convolution kernel with negative values may not "
"work properly.");
}
}
// Note for floating points a / b != a * (1 / b), but this shouldn't have any real