From e512542d718500744b7f4a670ab20dfcc1811edc Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 23 May 2024 05:53:43 +0100 Subject: [PATCH] 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 --- CHANGELOG.md | 1 + src/backend/backend.c | 76 +++++++-- src/backend/backend.h | 28 +++- src/backend/dummy/dummy.c | 7 + src/backend/gl/egl.c | 9 ++ src/backend/gl/glx.c | 7 + src/backend/xrender/xrender.c | 14 +- src/common.h | 2 +- src/config.c | 4 +- src/config.h | 9 +- src/config_libconfig.c | 7 +- src/dbus.c | 15 +- src/diagnostic.c | 20 +-- src/opengl.c | 4 +- src/options.c | 279 +++++++++++++++++++--------------- src/picom.c | 93 ++++++------ src/render.c | 36 ++--- src/x.c | 7 + 18 files changed, 396 insertions(+), 222 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d74a484c..b18455a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/backend/backend.c b/src/backend/backend.c index 3580faa1..36840986 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Yuxuan Shui +#include #include #include @@ -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"); diff --git a/src/backend/backend.h b/src/backend/backend.h index 139a3067..ff882d0f 100644 --- a/src/backend/backend.h +++ b/src/backend/backend.h @@ -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) diff --git a/src/backend/dummy/dummy.c b/src/backend/dummy/dummy.c index 84d7edcd..210e6ad2 100644 --- a/src/backend/dummy/dummy.c +++ b/src/backend/dummy/dummy.c @@ -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"); + } +} diff --git a/src/backend/gl/egl.c b/src/backend/gl/egl.c index 35b7793c..52aa3fe5 100644 --- a/src/backend/gl/egl.c +++ b/src/backend/gl/egl.c @@ -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"); + } +} diff --git a/src/backend/gl/glx.c b/src/backend/gl/glx.c index feaccfcf..b7745821 100644 --- a/src/backend/gl/glx.c +++ b/src/backend/gl/glx.c @@ -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"); + } +} diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index 4096923d..0269218f 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -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: diff --git a/src/common.h b/src/common.h index b7b220e9..f7baecf2 100644 --- a/src/common.h +++ b/src/common.h @@ -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; } /** diff --git a/src/config.c b/src/config.c index 6bc511ed..2f884ea0 100644 --- a/src/config.c +++ b/src/config.c @@ -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, diff --git a/src/config.h b/src/config.h index a1561a19..b86e1b3b 100644 --- a/src/config.h +++ b/src/config.h @@ -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; } diff --git a/src/config_libconfig.c b/src/config_libconfig.c index 8582397c..662d75f3 100644 --- a/src/config_libconfig.c +++ b/src/config_libconfig.c @@ -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; } } diff --git a/src/dbus.c b/src/dbus.c index e60aceb2..55b8939e 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -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); diff --git a/src/diagnostic.c b/src/diagnostic.c index 9278a57b..0595859a 100644 --- a/src/diagnostic.c +++ b/src/diagnostic.c @@ -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); } } diff --git a/src/opengl.c b/src/opengl.c index ea09e1cc..704461fc 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -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; } diff --git a/src/options.c b/src/options.c index eb7f3ea8..71042d7f 100644 --- a/src/options.c +++ b/src/options.c @@ -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"); } } diff --git a/src/picom.c b/src/picom.c index b5e15b5d..6a22ab58 100644 --- a/src/picom.c +++ b/src/picom.c @@ -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; diff --git a/src/render.c b/src/render.c index 77a85a2e..17a05b96 100644 --- a/src/render.c +++ b/src/render.c @@ -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, ®ion); } @@ -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."); diff --git a/src/x.c b/src/x.c index 78d9c68b..897f4156 100644 --- a/src/x.c +++ b/src/x.c @@ -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