From 04b80760d839e925e8c4bcb5d94a82408d2c93b3 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Mon, 10 Oct 2022 13:55:05 +0100 Subject: [PATCH 01/61] win: fix leak in win_bind_mask Related: #905 Signed-off-by: Yuxuan Shui --- src/config.h | 1 - src/win.c | 33 ++++++++++++++++++++++++++------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/config.h b/src/config.h index a58321f..b91a5ef 100644 --- a/src/config.h +++ b/src/config.h @@ -53,7 +53,6 @@ enum open_window_animation { OPEN_WINDOW_ANIMATION_SLIDE_OUT_CENTER, OPEN_WINDOW_ANIMATION_ZOOM, OPEN_WINDOW_ANIMATION_MINIMIZE, - OPEN_WINDOW_ANIMATION_MAXIMIZE, OPEN_WINDOW_ANIMATION_SQUEEZE, OPEN_WINDOW_ANIMATION_SQUEEZE_BOTTOM, OPEN_WINDOW_ANIMATION_INVALID, diff --git a/src/win.c b/src/win.c index 7cabdea..7553c6b 100644 --- a/src/win.c +++ b/src/win.c @@ -357,6 +357,8 @@ bool win_bind_mask(struct backend_base *b, struct managed_win *w) { pixman_region32_translate(®_bound_local, -w->g.x, -w->g.y); w->mask_image = b->ops->make_mask( b, (geometry_t){.width = w->g.width, .height = w->g.height}, ®_bound_local); + pixman_region32_fini(®_bound_local); + if (!w->mask_image) { return false; } @@ -671,14 +673,23 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) { add_damage_from_win(ps, w); } + + // Determine if a window should animate if (win_should_animate(ps, w)) { + if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) { + win_on_win_size_change(ps, w); + win_update_bounding_shape(ps, w); + damaged = true; + win_clear_flags(w, WIN_FLAGS_SIZE_STALE); + } + if (w->pending_g.y < 0 && w->g.y > 0 && abs(w->pending_g.y - w->g.y) >= w->pending_g.height) w->dwm_mask = ANIM_PREV_TAG; else if (w->pending_g.y > 0 && w->g.y < 0 && abs(w->pending_g.y - w->g.y) >= w->pending_g.height) w->dwm_mask = ANIM_NEXT_TAG; - if (!was_visible || w->dwm_mask || (w->window_type == WINTYPE_NOTIFICATION && w->state == WSTATE_MAPPING)) { + if (!was_visible || w->dwm_mask) { // Set window-open animation init_animation(ps, w); @@ -744,12 +755,12 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) { w->g = w->pending_g; } - if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) { - win_on_win_size_change(ps, w); - win_update_bounding_shape(ps, w); - damaged = true; - win_clear_flags(w, WIN_FLAGS_SIZE_STALE); - } + if (!win_should_animate(ps, w) && win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) { + win_on_win_size_change(ps, w); + win_update_bounding_shape(ps, w); + damaged = true; + win_clear_flags(w, WIN_FLAGS_SIZE_STALE); + } if (win_check_flags_all(w, WIN_FLAGS_POSITION_STALE)) { damaged = true; @@ -3025,6 +3036,10 @@ win_is_fullscreen_xcb(xcb_connection_t *c, const struct atom *a, const xcb_windo void win_set_flags(struct managed_win *w, uint64_t flags) { log_debug("Set flags %" PRIu64 " to window %#010x (%s)", flags, w->base.id, w->name); if (unlikely(w->state == WSTATE_DESTROYING)) { + if (w->animation_progress != 1.0) { + // Return because animation will trigger some of the flags + return; + } log_error("Flags set on a destroyed window %#010x (%s)", w->base.id, w->name); return; } @@ -3037,6 +3052,10 @@ void win_clear_flags(struct managed_win *w, uint64_t flags) { log_debug("Clear flags %" PRIu64 " from window %#010x (%s)", flags, w->base.id, w->name); if (unlikely(w->state == WSTATE_DESTROYING)) { + if (w->animation_progress != 1.0) { + // Return because animation will trigger some of the flags + return; + } log_warn("Flags cleared on a destroyed window %#010x (%s)", w->base.id, w->name); return; From 1e5de4067b156800d43d2cbd8cb80d655e3a5f64 Mon Sep 17 00:00:00 2001 From: Stefan Radziuk Date: Wed, 1 Dec 2021 22:51:57 +0000 Subject: [PATCH 02/61] picom upto date sync with yshui, full anim support --- man/picom.1.asciidoc | 3 +++ picom.sample.conf | 6 ++++++ src/config_libconfig.c | 3 +++ src/options.c | 11 +++++++++++ src/picom.c | 2 +- src/win.c | 4 ++++ src/win.h | 3 +++ 7 files changed, 31 insertions(+), 1 deletion(-) diff --git a/man/picom.1.asciidoc b/man/picom.1.asciidoc index 82085f2..17de2c8 100644 --- a/man/picom.1.asciidoc +++ b/man/picom.1.asciidoc @@ -259,6 +259,9 @@ 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. +*--transparent-clipping-exclude* 'CONDITION':: + Specify a list of conditions of windows that should never have transparent clipping applied. Useful for screenshot tools, where you need to be able to see through transparent parts of the window. + *--window-shader-fg* 'SHADER':: Specify GLSL fragment shader path for rendering window contents. Does not work when *--legacy-backends* is enabled. Shader is searched first relative to the directory the configuration file is in, then in the usual places for a configuration file. See section *SHADER INTERFACE* below for more details on the interface. diff --git a/picom.sample.conf b/picom.sample.conf index 950ad37..f357a26 100644 --- a/picom.sample.conf +++ b/picom.sample.conf @@ -357,6 +357,12 @@ window-shader-fg = "default"; # transparent-clipping = false; +# Specify a list of conditions of windows that should never have transparent +# clipping applied. Useful for screenshot tools, where you need to be able to +# see through transparent parts of the window. +# +# transparent-clipping-exclude = [] + # Set the log level. Possible values are: # "trace", "debug", "info", "warn", "error" # in increasing level of importance. Case doesn't matter. diff --git a/src/config_libconfig.c b/src/config_libconfig.c index 21e8896..c6d5aa8 100644 --- a/src/config_libconfig.c +++ b/src/config_libconfig.c @@ -461,6 +461,9 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad lcfg_lookup_bool(&cfg, "no-ewmh-fullscreen", &opt->no_ewmh_fullscreen); // --transparent-clipping lcfg_lookup_bool(&cfg, "transparent-clipping", &opt->transparent_clipping); + // --transparent-clipping-exclude + parse_cfg_condlst(&cfg, &opt->transparent_clipping_blacklist, + "transparent-clipping-exclude"); // --shadow-exclude parse_cfg_condlst(&cfg, &opt->shadow_blacklist, "shadow-exclude"); // --clip-shadow-above diff --git a/src/options.c b/src/options.c index 381330e..9bc3991 100644 --- a/src/options.c +++ b/src/options.c @@ -378,6 +378,11 @@ static void usage(const char *argv0, int ret) { " Make transparent windows clip other windows like non-transparent windows\n" " do, instead of blending on top of them\n" "\n" + "--transparent-clipping-exclude condition\n" + " Specify a list of conditions of windows that should never have\n" + " transparent clipping applied. Useful for screenshot tools, where you\n" + " need to be able to see through transparent parts of the window.\n" + "\n" "--window-shader-fg shader\n" " Specify GLSL fragment shader path for rendering window contents. Does\n" " not work when `--legacy-backends` is enabled.\n" @@ -485,6 +490,7 @@ static const struct option longopts[] = { {"clip-shadow-above", required_argument, NULL, 335}, {"window-shader-fg", required_argument, NULL, 336}, {"window-shader-fg-rule", required_argument, NULL, 337}, + {"transparent-clipping-exclude", required_argument, NULL, 338}, {"legacy-backends", no_argument, NULL, 733}, {"monitor-repaint", no_argument, NULL, 800}, {"diagnostics", no_argument, NULL, 801}, @@ -861,6 +867,11 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable, } break; } + case 338: { + // --transparent-clipping-exclude + condlst_add(&opt->transparent_clipping_blacklist, optarg); + break; + } case 321: { enum log_level tmp_level = string_to_log_level(optarg); if (tmp_level == LOG_LEVEL_INVALID) { diff --git a/src/picom.c b/src/picom.c index 0f24b00..170f89e 100644 --- a/src/picom.c +++ b/src/picom.c @@ -1003,7 +1003,7 @@ paint_preprocess(session_t *ps, bool *fade_running, bool *animation_running) { // we add the window region to the ignored region // Otherwise last_reg_ignore shouldn't change if ((w->mode != WMODE_TRANS && !ps->o.force_win_blend) || - ps->o.transparent_clipping) { + (ps->o.transparent_clipping && !w->transparent_clipping_excluded)) { // w->mode == WMODE_SOLID or WMODE_FRAME_TRANS region_t *tmp = rc_region_new(); if (w->mode == WMODE_SOLID) { diff --git a/src/win.c b/src/win.c index 7553c6b..aad7a38 100644 --- a/src/win.c +++ b/src/win.c @@ -1498,6 +1498,9 @@ void win_on_factor_change(session_t *ps, struct managed_win *w) { w->fade_excluded = c2_match(ps, w, ps->o.fade_blacklist, NULL); + w->transparent_clipping_excluded = + c2_match(ps, w, ps->o.transparent_clipping_blacklist, NULL); + win_update_opacity_target(ps, w); w->reg_ignore_valid = false; @@ -1846,6 +1849,7 @@ struct win *fill_win(session_t *ps, struct win *w) { .rounded_corners = false, .paint_excluded = false, .fade_excluded = false, + .transparent_clipping_excluded = false, .unredir_if_possible_excluded = false, .prop_shadow = -1, // following 4 are set in win_mark_client diff --git a/src/win.h b/src/win.h index 63a0572..619341f 100644 --- a/src/win.h +++ b/src/win.h @@ -266,6 +266,9 @@ struct managed_win { /// Whether fading is excluded by the rules. Calculated. bool fade_excluded; + /// Whether transparent clipping is excluded by the rules. + bool transparent_clipping_excluded; + // Frame-opacity-related members /// Current window frame opacity. Affected by window opacity. double frame_opacity; From 0004173ecd446fa3dc01e9890bcd6eec594d92ab Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sun, 21 Aug 2022 14:57:39 +0100 Subject: [PATCH 03/61] Rename COMPTON_VERSION to PICOM_VERSION Signed-off-by: Yuxuan Shui --- meson.build | 2 +- src/dbus.c | 2 +- src/diagnostic.c | 2 +- src/options.c | 4 ++-- src/picom.c | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/meson.build b/meson.build index e333c31..0150a37 100644 --- a/meson.build +++ b/meson.build @@ -15,7 +15,7 @@ if git.found() endif endif -add_global_arguments('-DCOMPTON_VERSION="'+version+'"', language: 'c') +add_global_arguments('-DPICOM_VERSION="'+version+'"', language: 'c') if get_option('buildtype') == 'release' add_global_arguments('-DNDEBUG', language: 'c') diff --git a/src/dbus.c b/src/dbus.c index e037167..8b17b30 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -1153,7 +1153,7 @@ static bool cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { // version if (!strcmp("version", target)) { - cdbus_reply_string(ps, msg, COMPTON_VERSION); + cdbus_reply_string(ps, msg, PICOM_VERSION); return true; } diff --git a/src/diagnostic.c b/src/diagnostic.c index d275b1a..79ac5ac 100644 --- a/src/diagnostic.c +++ b/src/diagnostic.c @@ -12,7 +12,7 @@ #include "common.h" void print_diagnostics(session_t *ps, const char *config_file, bool compositor_running) { - printf("**Version:** " COMPTON_VERSION "\n"); + printf("**Version:** " PICOM_VERSION "\n"); //printf("**CFLAGS:** %s\n", "??"); printf("\n### Extensions:\n\n"); printf("* Shape: %s\n", ps->shape_exists ? "Yes" : "No"); diff --git a/src/options.c b/src/options.c index 9bc3991..b0509f8 100644 --- a/src/options.c +++ b/src/options.c @@ -26,7 +26,7 @@ static void usage(const char *argv0, int ret) { #define WARNING_DISABLED " (DISABLED AT COMPILE TIME)" static const char *usage_text = - "picom (" COMPTON_VERSION ")\n" + "picom (" PICOM_VERSION ")\n" "Please report bugs to https://github.com/yshui/picom\n\n" "usage: %s [options]\n" "Options:\n" @@ -533,7 +533,7 @@ bool get_early_config(int argc, char *const *argv, char **config_file, bool *all } else if (o == 314) { *all_xerrors = true; } else if (o == 318) { - printf("%s\n", COMPTON_VERSION); + printf("%s\n", PICOM_VERSION); return true; } else if (o == '?' || o == ':') { usage(argv[0], 1); diff --git a/src/picom.c b/src/picom.c index 170f89e..7c52950 100644 --- a/src/picom.c +++ b/src/picom.c @@ -1264,7 +1264,7 @@ static int register_cm(session_t *ps) { ps->c, xcb_change_property_checked( ps->c, XCB_PROP_MODE_REPLACE, ps->reg_win, get_atom(ps->atoms, "COMPTON_VERSION"), XCB_ATOM_STRING, 8, - (uint32_t)strlen(COMPTON_VERSION), COMPTON_VERSION)); + (uint32_t)strlen(PICOM_VERSION), PICOM_VERSION)); if (e) { log_error_x_error(e, "Failed to set COMPTON_VERSION."); free(e); From fbe7ed569938e94eef294d900f44d7be027e2abc Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sun, 21 Aug 2022 15:07:44 +0100 Subject: [PATCH 04/61] options: improve usage message printing Laying the usage message out by hand is tedious, also error prone because the option names are duplicated at 2 places and have to be consistent. Create a struct to hold the option names and help messages, and do layout programmatically. Signed-off-by: Yuxuan Shui --- src/options.c | 274 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 272 insertions(+), 2 deletions(-) diff --git a/src/options.c b/src/options.c index b0509f8..0e6044d 100644 --- a/src/options.c +++ b/src/options.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include // for xcb_render_fixed_t, XXX @@ -20,6 +22,222 @@ #pragma GCC diagnostic error "-Wunused-parameter" +struct picom_option { + const char *long_name; + int has_arg; + int val; + const char *arg_name; + const char *help; +}; + +// clang-format off +static const struct option *longopts = NULL; +static const struct picom_option picom_options[] = { +#ifdef CONFIG_LIBCONFIG + {"config" , required_argument, 256, NULL , "Path to the configuration file."}, +#endif + {"help" , no_argument , 'h', NULL , "Print this help message and exit."}, + {"shadow-radius" , required_argument, 'r', NULL , "The blur radius for shadows. (default 12)"}, + {"shadow-opacity" , required_argument, 'o', NULL , "The translucency for shadows. (default .75)"}, + {"shadow-offset-x" , required_argument, 'l', NULL , "The left offset for shadows. (default -15)"}, + {"shadow-offset-y" , required_argument, 't', NULL , "The top offset for shadows. (default -15)"}, + {"fade-in-step" , required_argument, 'I', NULL , "Opacity change between steps while fading in. (default 0.028)"}, + {"fade-out-step" , required_argument, 'O', NULL , "Opacity change between steps while fading out. (default 0.03)"}, + {"fade-delta" , required_argument, 'D', NULL , "The time between steps in a fade in milliseconds. (default 10)"}, + {"menu-opacity" , required_argument, 'm', NULL , "The opacity for menus. (default 1.0)"}, + {"shadow" , no_argument , 'c', NULL , "Enabled client-side shadows on windows."}, + {"clear-shadow" , no_argument , 'z', NULL , "Don't dreaw shadow behind the window."}, + {"fading" , no_argument , 'f', NULL , "Fade windows in/out when opening/closing and when opacity changes, " + "unless --no-fading-openclose is used."}, + {"inactive-opacity" , required_argument, 'i', NULL , "Opacity of inactive windows. (0.1 - 1.0)"}, + {"frame-opacity" , required_argument, 'e', NULL , "Opacity of window titlebars and borders. (0.1 - 1.0)"}, + {"daemon" , no_argument , 'b', NULL , "Daemonize process."}, + {"shadow-red" , required_argument, 257, NULL , "Red color value of shadow (0.0 - 1.0, defaults to 0)."}, + {"shadow-green" , required_argument, 258, NULL , "Green color value of shadow (0.0 - 1.0, defaults to 0)."}, + {"shadow-blue" , required_argument, 259, NULL , "Blue color value of shadow (0.0 - 1.0, defaults to 0)."}, + {"inactive-opacity-override" , no_argument , 260, NULL , "Inactive opacity set by -i overrides value of _NET_WM_WINDOW_OPACITY."}, + {"inactive-dim" , required_argument, 261, NULL , "Dim inactive windows. (0.0 - 1.0, defaults to 0)"}, + {"mark-wmwin-focused" , no_argument , 262, NULL , "Try to detect WM windows and mark them as active."}, + {"shadow-exclude" , required_argument, 263, NULL , "Exclude conditions for shadows."}, + {"mark-ovredir-focused" , no_argument , 264, NULL , "Mark windows that have no WM frame as active."}, + {"no-fading-openclose" , no_argument , 265, NULL , "Do not fade on window open/close."}, + {"shadow-ignore-shaped" , no_argument , 266, NULL , "Do not paint shadows on shaped windows. (Deprecated, use --shadow-exclude " + "\'bounding_shaped\' or --shadow-exclude \'bounding_shaped && " + "!rounded_corners\' instead.)"}, + {"detect-rounded-corners" , no_argument , 267, NULL , "Try to detect windows with rounded corners and don't consider them shaped " + "windows. Affects --shadow-ignore-shaped, --unredir-if-possible, and " + "possibly others. You need to turn this on manually if you want to match " + "against rounded_corners in conditions."}, + {"detect-client-opacity" , no_argument , 268, NULL , "Detect _NET_WM_WINDOW_OPACITY on client windows, useful for window " + "managers not passing _NET_WM_WINDOW_OPACITY of client windows to frame"}, + {"refresh-rate" , required_argument, 269, NULL , NULL}, + {"vsync" , optional_argument, 270, NULL , "Enable VSync"}, + {"sw-opti" , no_argument , 274, NULL , NULL}, + {"vsync-aggressive" , no_argument , 275, NULL , NULL}, + {"use-ewmh-active-win" , no_argument , 276, NULL , "Use _NET_WM_ACTIVE_WINDOW on the root window to determine which window is " + "focused instead of using FocusIn/Out events"}, + {"respect-prop-shadow" , no_argument , 277, NULL , NULL}, + {"unredir-if-possible" , no_argument , 278, NULL , "Unredirect all windows if a full-screen opaque window is detected, to " + "maximize performance for full-screen applications."}, + {"focus-exclude" , required_argument, 279, "COND" , "Specify a list of conditions of windows that should always be considered focused."}, + {"inactive-dim-fixed" , no_argument , 280, NULL , "Use fixed inactive dim value."}, + {"detect-transient" , no_argument , 281, NULL , "Use WM_TRANSIENT_FOR to group windows, and consider windows in the same " + "group focused at the same time."}, + {"detect-client-leader" , no_argument , 282, NULL , "Use WM_CLIENT_LEADER to group windows, and consider windows in the same group " + "focused at the same time. This usually means windows from the same application " + "will be considered focused or unfocused at the same time. WM_TRANSIENT_FOR has " + "higher priority if --detect-transient is enabled, too."}, + {"blur-background" , no_argument , 283, NULL , "Blur background of semi-transparent / ARGB windows. May impact performance"}, + {"blur-background-frame" , no_argument , 284, NULL , "Blur background of windows when the window frame is not opaque. Implies " + "--blur-background."}, + {"blur-background-fixed" , no_argument , 285, NULL , "Use fixed blur strength instead of adjusting according to window opacity."}, +#ifdef CONFIG_DBUS + {"dbus" , no_argument , 286, NULL , "Enable remote control via D-Bus. See the D-BUS API section in the man page " + "for more details."}, +#endif + {"logpath" , required_argument, 287, NULL , NULL}, + {"invert-color-include" , required_argument, 288, "COND" , "Specify a list of conditions of windows that should be painted with " + "inverted color."}, + {"opengl" , no_argument , 289, NULL , NULL}, + {"backend" , required_argument, 290, NULL , "Backend. Possible values are: xrender" +#ifdef CONFIG_OPENGL + ", glx" +#endif + }, + {"glx-no-stencil" , no_argument , 291, NULL , NULL}, + {"benchmark" , required_argument, 293, NULL , "Benchmark mode. Repeatedly paint until reaching the specified cycles."}, + {"benchmark-wid" , required_argument, 294, NULL , "Specify window ID to repaint in benchmark mode. If omitted or is 0, the whole" + " screen is repainted."}, + {"blur-background-exclude" , required_argument, 296, "COND" , "Exclude conditions for background blur."}, + {"active-opacity" , required_argument, 297, NULL , "Default opacity for active windows. (0.0 - 1.0)"}, + {"glx-no-rebind-pixmap" , no_argument , 298, NULL , NULL}, + {"glx-swap-method" , required_argument, 299, NULL , NULL}, + {"fade-exclude" , required_argument, 300, "COND" , "Exclude conditions for fading."}, + {"blur-kern" , required_argument, 301, NULL , "Specify the blur convolution kernel, see man page for more details"}, + {"resize-damage" , required_argument, 302, NULL , NULL}, // only used by legacy backends + {"glx-use-gpushader4" , no_argument , 303, NULL , NULL}, + {"opacity-rule" , required_argument, 304, "OPACITY:COND", "Specify a list of opacity rules, see man page for more details"}, + {"shadow-exclude-reg" , required_argument, 305, NULL , NULL}, + {"paint-exclude" , required_argument, 306, NULL , NULL}, + {"xinerama-shadow-crop" , no_argument , 307, NULL , "Crop shadow of a window fully on a particular Xinerama screen to the screen."}, + {"unredir-if-possible-exclude" , required_argument, 308, "COND" , "Conditions of windows that shouldn't be considered full-screen for " + "unredirecting screen."}, + {"unredir-if-possible-delay" , required_argument, 309, NULL, "Delay before unredirecting the window, in milliseconds. Defaults to 0."}, + {"write-pid-path" , required_argument, 310, "PATH" , "Write process ID to a file."}, + {"vsync-use-glfinish" , no_argument , 311, NULL , NULL}, + {"xrender-sync-fence" , no_argument , 313, NULL , "Additionally use X Sync fence to sync clients' draw calls. Needed on " + "nvidia-drivers with GLX backend for some users."}, + {"show-all-xerrors" , no_argument , 314, NULL , NULL}, + {"no-fading-destroyed-argb" , no_argument , 315, NULL , "Do not fade destroyed ARGB windows with WM frame. Workaround bugs in Openbox, " + "Fluxbox, etc."}, + {"force-win-blend" , no_argument , 316, NULL , "Force all windows to be painted with blending. Useful if you have a custom " + "shader that could turn opaque pixels transparent."}, + {"glx-fshader-win" , required_argument, 317, NULL , NULL}, + {"version" , no_argument , 318, NULL , "Print version number and exit."}, + {"no-x-selection" , no_argument , 319, NULL , NULL}, + {"log-level" , required_argument, 321, NULL , "Log level, possible values are: trace, debug, info, warn, error"}, + {"log-file" , required_argument, 322, NULL , "Path to the log file."}, + {"use-damage" , no_argument , 323, NULL , "Render only the damaged (changed) part of the screen"}, + {"no-use-damage" , no_argument , 324, NULL , "Disable the use of damage information. This cause the whole screen to be" + "redrawn everytime, instead of the part of the screen that has actually " + "changed. Potentially degrades the performance, but might fix some artifacts."}, + {"no-vsync" , no_argument , 325, NULL , "Disable VSync"}, + {"max-brightness" , required_argument, 326, NULL , "Dims windows which average brightness is above this threshold. Requires " + "--no-use-damage. (default: 1.0, meaning no dimming)"}, + {"transparent-clipping" , no_argument , 327, NULL , "Make transparent windows clip other windows like non-transparent windows do, " + "instead of blending on top of them"}, + {"transparent-clipping-exclude", required_argument, 338, "COND" , "Specify a list of conditions of windows that should never have " + "transparent clipping applied. Useful for screenshot tools, where you " + "need to be able to see through transparent parts of the window."}, + {"blur-method" , required_argument, 328, NULL , "The algorithm used for background bluring. Available choices are: 'none' to " + "disable, 'gaussian', 'box' or 'kernel' for custom convolution blur with " + "--blur-kern. Note: 'gaussian' and 'box' is not supported by --legacy-backends."}, + {"blur-size" , required_argument, 329, NULL , "The radius of the blur kernel for 'box' and 'gaussian' blur method."}, + {"blur-deviation" , required_argument, 330, NULL , "The standard deviation for the 'gaussian' blur method."}, + {"blur-strength" , required_argument, 331, NULL , "The strength level of the 'dual_kawase' blur method."}, + {"shadow-color" , required_argument, 332, NULL , "Color of shadow, as a hex RGB string (defaults to #000000)"}, + {"corner-radius" , required_argument, 333, NULL , "Sets the radius of rounded window corners. When > 0, the compositor will " + "round the corners of windows. (defaults to 0)."}, + {"rounded-corners-exclude" , required_argument, 334, "COND" , "Exclude conditions for rounded corners."}, + {"clip-shadow-above" , required_argument, 335, NULL , "Specify a list of conditions of windows to not paint a shadow over, such " + "as a dock window."}, + {"window-shader-fg" , required_argument, 336, "PATH" , "Specify GLSL fragment shader path for rendering window contents. Does not" + " work when `--legacy-backends` is enabled. See man page for more details."}, + {"window-shader-fg-rule" , required_argument, 337, "PATH:COND" , "Specify GLSL fragment shader path for rendering window contents using " + "patterns. Pattern should be in the format of SHADER_PATH:PATTERN, " + "similar to --opacity-rule. SHADER_PATH can be \"default\", in which case " + "the default shader will be used. Does not work when --legacy-backends is " + "enabled. See man page for more details"}, + {"legacy-backends" , no_argument , 733, NULL , "Use deprecated version of the backends."}, + {"monitor-repaint" , no_argument , 800, NULL , "Highlight the updated area of the screen. For debugging."}, + {"diagnostics" , no_argument , 801, NULL , "Print diagnostic information"}, + {"debug-mode" , no_argument , 802, NULL , "Render into a separate window, and don't take over the screen. Useful when " + "you want to attach a debugger to picom"}, + {"no-ewmh-fullscreen" , no_argument , 803, NULL , "Do not use EWMH to detect fullscreen windows. Reverts to checking if a " + "window is fullscreen based only on its size and coordinates."}, +}; +// clang-format on + +static void setup_longopts(void) { + auto opts = ccalloc(ARR_SIZE(picom_options) + 1, struct option); + for (size_t i = 0; i < ARR_SIZE(picom_options); i++) { + opts[i].name = picom_options[i].long_name; + opts[i].has_arg = picom_options[i].has_arg; + opts[i].flag = NULL; + opts[i].val = picom_options[i].val; + } + longopts = opts; +} + +void print_help(const char *help, size_t indent, size_t curr_indent, size_t line_wrap, + FILE *f) { + if (curr_indent > indent) { + fputs("\n", f); + curr_indent = 0; + } + + if (line_wrap - indent <= 1) { + line_wrap = indent + 2; + } + + size_t pos = 0; + size_t len = strlen(help); + while (pos < len) { + fprintf(f, "%*s", (int)(indent - curr_indent), ""); + curr_indent = 0; + size_t towrite = line_wrap - indent; + while (help[pos] == ' ') { + pos++; + } + if (pos + towrite > len) { + towrite = len - pos; + fwrite(help + pos, 1, towrite, f); + } else { + auto space_break = towrite; + while (space_break > 0 && help[pos + space_break - 1] != ' ') { + space_break--; + } + + bool print_hyphen = false; + if (space_break == 0) { + print_hyphen = true; + towrite--; + } else { + towrite = space_break; + } + + fwrite(help + pos, 1, towrite, f); + + if (print_hyphen) { + fputs("-", f); + } + } + + fputs("\n", f); + pos += towrite; + } +} + /** * Print usage text. */ @@ -394,8 +612,58 @@ static void usage(const char *argv0, int ret) { " case the default shader will be used. Does not work when\n" " `--legacy-backends` is enabled.\n"; FILE *f = (ret ? stderr : stdout); - fprintf(f, usage_text, argv0); -#undef WARNING_DISABLED + fprintf(f, "picom (%s)\n", PICOM_VERSION); + fprintf(f, "Standalone X11 compositor\n"); + fprintf(f, "Please report bugs to https://github.com/yshui/picom\n\n"); + + fprintf(f, "Usage: %s [OPTION]...\n\n", argv0); + fprintf(f, "OPTIONS:\n"); + + int line_wrap = 80; + struct winsize window_size = {0}; + if (ioctl(fileno(f), TIOCGWINSZ, &window_size) != -1) { + line_wrap = window_size.ws_col; + } + + size_t help_indent = 0; + for (size_t i = 0; i < ARR_SIZE(picom_options); i++) { + if (picom_options[i].help == NULL) { + // Hide options with no help message. + continue; + } + auto option_len = strlen(picom_options[i].long_name) + 2 + 4; + if (picom_options[i].arg_name) { + option_len += strlen(picom_options[i].arg_name) + 1; + } + if (option_len > help_indent && option_len < 30) { + help_indent = option_len; + } + } + help_indent += 6; + + for (size_t i = 0; i < ARR_SIZE(picom_options); i++) { + if (picom_options[i].help == NULL) { + continue; + } + size_t option_len = 8; + fprintf(f, " "); + if ((picom_options[i].val > 'a' && picom_options[i].val < 'z') || + (picom_options[i].val > 'A' && picom_options[i].val < 'Z')) { + fprintf(f, "-%c, ", picom_options[i].val); + } else { + fprintf(f, " "); + } + fprintf(f, "--%s", picom_options[i].long_name); + option_len += strlen(picom_options[i].long_name) + 2; + if (picom_options[i].arg_name) { + fprintf(f, "=%s", picom_options[i].arg_name); + option_len += strlen(picom_options[i].arg_name) + 1; + } + fprintf(f, " "); + option_len += 2; + print_help(picom_options[i].help, help_indent, option_len, + (size_t)line_wrap, f); + } } static const char *shortopts = "D:I:O:r:o:m:l:t:i:e:hscnfFCazGb"; @@ -512,6 +780,8 @@ static const struct option longopts[] = { /// Return true if we should quit bool get_early_config(int argc, char *const *argv, char **config_file, bool *all_xerrors, bool *fork, int *exit_code) { + setup_longopts(); + int o = 0, longopt_idx = -1; // Pre-parse the commandline arguments to check for --config and invalid From f7a950a6385488375308cd69c1c486e577da78be Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sat, 29 Oct 2022 20:44:02 +0100 Subject: [PATCH 05/61] Bump version number Signed-off-by: Yuxuan Shui --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 0150a37..11da327 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('picom', 'c', version: '9', +project('picom', 'c', version: '10', default_options: ['c_std=c11', 'warning_level=1']) cc = meson.get_compiler('c') From f7596fd43a3f5bd38b96cc86ef6c285dd8be7b53 Mon Sep 17 00:00:00 2001 From: Alp Date: Mon, 31 Oct 2022 22:12:16 +0300 Subject: [PATCH 06/61] rounded corner delay fix --- src/win.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/win.c b/src/win.c index aad7a38..119093c 100644 --- a/src/win.c +++ b/src/win.c @@ -677,13 +677,7 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) { // Determine if a window should animate if (win_should_animate(ps, w)) { - if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) { - win_on_win_size_change(ps, w); - win_update_bounding_shape(ps, w); - damaged = true; - win_clear_flags(w, WIN_FLAGS_SIZE_STALE); - } - + win_update_bounding_shape(ps, w); if (w->pending_g.y < 0 && w->g.y > 0 && abs(w->pending_g.y - w->g.y) >= w->pending_g.height) w->dwm_mask = ANIM_PREV_TAG; else if (w->pending_g.y > 0 && w->g.y < 0 && abs(w->pending_g.y - w->g.y) >= w->pending_g.height) @@ -749,13 +743,12 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) { } } - w->animation_progress = 0.0; - + w->animation_progress = 0.0; } else { w->g = w->pending_g; } - if (!win_should_animate(ps, w) && win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) { + if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) { win_on_win_size_change(ps, w); win_update_bounding_shape(ps, w); damaged = true; From dbb81b51160569a400131f0dd49270b50d7b824e Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sat, 29 Oct 2022 20:59:28 +0100 Subject: [PATCH 07/61] doc: remove mention of raw string pattern Signed-off-by: Yuxuan Shui --- man/picom.1.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/picom.1.asciidoc b/man/picom.1.asciidoc index 17de2c8..7dc070f 100644 --- a/man/picom.1.asciidoc +++ b/man/picom.1.asciidoc @@ -302,7 +302,7 @@ With greater-than/less-than operators it looks like: 'OPERATOR' is one of `=` (equals), `<`, `>`, `<=`, `=>`, or nothing (exists). Exists operator checks whether a property exists on a window (but for predefined targets, exists means != 0 then). -'PATTERN' is either an integer or a string enclosed by single or double quotes. Python-3-style escape sequences and raw string are supported in the string format. +'PATTERN' is either an integer or a string enclosed by single or double quotes. Python-3-style escape sequences are supported in the string format. Supported logical operators are `&&` (and) and `||` (or). `&&` has higher precedence than `||`, left-to-right associativity. Use parentheses to change precedence. From fa21c44ee7da0f3158f65901cf63dc448af8f4ee Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 11 Nov 2022 15:51:11 +0000 Subject: [PATCH 08/61] backend: gl: fix shadow from mask The intermediate texture used for shadow from mask calculation did not properly set the min/mag filter to linear, which is required by the blur methods. Because they use texture interpolation to accelerate the convolution calculation. Fixes #916 Signed-off-by: Yuxuan Shui --- src/backend/gl/gl_common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index ff6091c..cce182a 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -1219,6 +1219,8 @@ void *gl_shadow_from_mask(backend_t *base, void *mask, auto source_texture = gl_new_texture(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, source_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, new_inner->width, new_inner->height, 0, GL_RED, GL_UNSIGNED_BYTE, NULL); glBindTexture(GL_TEXTURE_2D, 0); From 143488156753d683968bb9d41194a5f3e5ce5871 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 15 Nov 2022 18:03:31 +0000 Subject: [PATCH 09/61] win: assert we won't clobber existing mask Signed-off-by: Yuxuan Shui --- src/win.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/win.c b/src/win.c index 119093c..1ce3161 100644 --- a/src/win.c +++ b/src/win.c @@ -353,6 +353,7 @@ static inline bool win_bind_pixmap(struct backend_base *b, struct managed_win *w } bool win_bind_mask(struct backend_base *b, struct managed_win *w) { + assert(!w->mask_image); auto reg_bound_local = win_get_bounding_shape_global_by_val(w); pixman_region32_translate(®_bound_local, -w->g.x, -w->g.y); w->mask_image = b->ops->make_mask( From a10a64f9844b952618472a3275cbc9bcd3140ab6 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 16 Nov 2022 15:39:17 +0000 Subject: [PATCH 10/61] Fix typo Fixes #922 Signed-off-by: Yuxuan Shui --- man/picom.1.asciidoc | 2 +- picom.sample.conf | 4 ++-- src/common.h | 4 ++-- src/options.c | 2 +- tests/configs/parsing_test.conf | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/man/picom.1.asciidoc b/man/picom.1.asciidoc index 7dc070f..61af5a2 100644 --- a/man/picom.1.asciidoc +++ b/man/picom.1.asciidoc @@ -230,7 +230,7 @@ May also be one of the predefined kernels: `3x3box` (default), `5x5box`, `7x7box GLX backend: Avoid rebinding pixmap on window damage. Probably could improve performance on rapid window content changes, but is known to break things on some drivers (LLVMpipe, xf86-video-intel, etc.). Recommended if it works. *--no-use-damage*:: - Disable the use of damage information. This cause the whole screen to be redrawn everytime, instead of the part of the screen has actually changed. Potentially degrades the performance, but might fix some artifacts. + Disable the use of damage information. This cause the whole screen to be redrawn every time, instead of the part of the screen has actually changed. Potentially degrades the performance, but might fix some artifacts. *--xrender-sync-fence*:: 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. diff --git a/picom.sample.conf b/picom.sample.conf index f357a26..00cdb0c 100644 --- a/picom.sample.conf +++ b/picom.sample.conf @@ -316,7 +316,7 @@ glx-no-stencil = true; # glx-no-rebind-pixmap = false # Disable the use of damage information. -# This cause the whole screen to be redrawn everytime, instead of the part of the screen +# This cause the whole screen to be redrawn every time, instead of the part of the screen # has actually changed. Potentially degrades the performance, but might fix some artifacts. # The opposing option is use-damage # @@ -411,7 +411,7 @@ log-level = "warn"; # transparent, and you want shadows in those areas. # # clip-shadow-above::: -# Controls wether shadows that would have been drawn above the window should +# Controls whether shadows that would have been drawn above the window should # be clipped. Useful for dock windows that should have no shadow painted on top. # # redir-ignore::: diff --git a/src/common.h b/src/common.h index efc84b6..cb02da3 100644 --- a/src/common.h +++ b/src/common.h @@ -152,7 +152,7 @@ typedef struct session { /// Use an ev_idle callback for drawing /// So we only start drawing when events are processed ev_idle draw_idle; - /// Called everytime we have timeouts or new data on socket, + /// Called every time we have timeouts or new data on socket, /// so we can be sure if xcb read from X socket at anytime during event /// handling, we will not left any event unhandled in the queue ev_prepare event_check; @@ -244,7 +244,7 @@ typedef struct session { /// Whether we need to redraw the screen bool redraw_needed; - /// Cache a xfixes region so we don't need to allocate it everytime. + /// Cache a xfixes region so we don't need to allocate it every time. /// A workaround for yshui/picom#301 xcb_xfixes_region_t damaged_region; /// The region needs to painted on next paint. diff --git a/src/options.c b/src/options.c index 0e6044d..6357907 100644 --- a/src/options.c +++ b/src/options.c @@ -139,7 +139,7 @@ static const struct picom_option picom_options[] = { {"log-file" , required_argument, 322, NULL , "Path to the log file."}, {"use-damage" , no_argument , 323, NULL , "Render only the damaged (changed) part of the screen"}, {"no-use-damage" , no_argument , 324, NULL , "Disable the use of damage information. This cause the whole screen to be" - "redrawn everytime, instead of the part of the screen that has actually " + "redrawn every time, instead of the part of the screen that has actually " "changed. Potentially degrades the performance, but might fix some artifacts."}, {"no-vsync" , no_argument , 325, NULL , "Disable VSync"}, {"max-brightness" , required_argument, 326, NULL , "Dims windows which average brightness is above this threshold. Requires " diff --git a/tests/configs/parsing_test.conf b/tests/configs/parsing_test.conf index 5269e6e..7236017 100644 --- a/tests/configs/parsing_test.conf +++ b/tests/configs/parsing_test.conf @@ -311,7 +311,7 @@ detect-transient = true; # glx-no-rebind-pixmap = false # Disable the use of damage information. -# This cause the whole screen to be redrawn everytime, instead of the part of the screen +# This cause the whole screen to be redrawn every time, instead of the part of the screen # has actually changed. Potentially degrades the performance, but might fix some artifacts. # The opposing option is use-damage # @@ -400,7 +400,7 @@ log-level = "warn"; # transparent, and you want shadows in those areas. # # clip-shadow-above::: -# Controls wether shadows that would have been drawn above the window should +# Controls whether shadows that would have been drawn above the window should # be clipped. Useful for dock windows that should have no shadow painted on top. # # redir-ignore::: From 8f848c2b1d8cad736e8d72e22bd62f9ab006316c Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 17 Nov 2022 22:07:59 +0000 Subject: [PATCH 11/61] backend: gl: fix crash when shadow radius is 0 Fixes #927 Signed-off-by: Yuxuan Shui --- src/backend/gl/gl_common.c | 65 +++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index cce182a..efa9e10 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -1180,18 +1180,23 @@ struct gl_shadow_context { struct backend_shadow_context *gl_create_shadow_context(backend_t *base, double radius) { auto ctx = ccalloc(1, struct gl_shadow_context); ctx->radius = radius; + ctx->blur_context = NULL; - struct gaussian_blur_args args = { - .size = (int)radius, - .deviation = gaussian_kernel_std_for_size(radius, 0.5 / 256.0), - }; - ctx->blur_context = gl_create_blur_context(base, BLUR_METHOD_GAUSSIAN, &args); + if (radius > 0) { + struct gaussian_blur_args args = { + .size = (int)radius, + .deviation = gaussian_kernel_std_for_size(radius, 0.5 / 256.0), + }; + ctx->blur_context = gl_create_blur_context(base, BLUR_METHOD_GAUSSIAN, &args); + } return (struct backend_shadow_context *)ctx; } void gl_destroy_shadow_context(backend_t *base attr_unused, struct backend_shadow_context *ctx) { auto ctx_ = (struct gl_shadow_context *)ctx; - gl_destroy_blur_context(base, (struct backend_blur_context *)ctx_->blur_context); + if (ctx_->blur_context) { + gl_destroy_blur_context(base, (struct backend_blur_context *)ctx_->blur_context); + } free(ctx_); } @@ -1252,27 +1257,32 @@ void *gl_shadow_from_mask(backend_t *base, void *mask, gl_check_err(); - glActiveTexture(GL_TEXTURE0); - auto tmp_texture = gl_new_texture(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, tmp_texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, new_inner->width, new_inner->height, 0, - GL_RED, GL_UNSIGNED_BYTE, NULL); - glBindTexture(GL_TEXTURE_2D, 0); + auto tmp_texture = source_texture; + if (gsctx->blur_context != NULL) { + glActiveTexture(GL_TEXTURE0); + tmp_texture = gl_new_texture(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, tmp_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, new_inner->width, + new_inner->height, 0, GL_RED, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - tmp_texture, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, tmp_texture, 0); - region_t reg_blur; - pixman_region32_init_rect(®_blur, 0, 0, (unsigned int)new_inner->width, - (unsigned int)new_inner->height); - // gl_blur expects reg_blur to be in X coordinate system (i.e. y flipped), but we - // are covering the whole texture so we don't need to worry about that. - gl_blur_impl(1.0, gsctx->blur_context, NULL, (coord_t){0}, ®_blur, NULL, - source_texture, - (geometry_t){.width = new_inner->width, .height = new_inner->height}, - fbo, gd->default_mask_texture); - pixman_region32_fini(®_blur); + region_t reg_blur; + pixman_region32_init_rect(®_blur, 0, 0, (unsigned int)new_inner->width, + (unsigned int)new_inner->height); + // gl_blur expects reg_blur to be in X coordinate system (i.e. y flipped), + // but we are covering the whole texture so we don't need to worry about + // that. + gl_blur_impl( + 1.0, gsctx->blur_context, NULL, (coord_t){0}, ®_blur, NULL, + source_texture, + (geometry_t){.width = new_inner->width, .height = new_inner->height}, + fbo, gd->default_mask_texture); + pixman_region32_fini(®_blur); + } // Colorize the shadow with color. log_debug("Colorize shadow"); @@ -1324,7 +1334,10 @@ void *gl_shadow_from_mask(backend_t *base, void *mask, glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glDeleteBuffers(2, bo); - glDeleteTextures(1, (GLuint[]){source_texture, tmp_texture}); + glDeleteTextures(1, (GLuint[]){source_texture}); + if (tmp_texture != source_texture) { + glDeleteTextures(1, (GLuint[]){tmp_texture}); + } glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &fbo); gl_check_err(); From 611f8b80c210e3fa5cdd8cb450e44077b027ee21 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 17 Nov 2022 22:16:27 +0000 Subject: [PATCH 12/61] backend: gl: handle blur context creation failure Signed-off-by: Yuxuan Shui --- src/backend/gl/gl_common.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index efa9e10..9eec872 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -1188,6 +1188,11 @@ struct backend_shadow_context *gl_create_shadow_context(backend_t *base, double .deviation = gaussian_kernel_std_for_size(radius, 0.5 / 256.0), }; ctx->blur_context = gl_create_blur_context(base, BLUR_METHOD_GAUSSIAN, &args); + if (!ctx->blur_context) { + log_error("Failed to create shadow context"); + free(ctx); + return NULL; + } } return (struct backend_shadow_context *)ctx; } From 935885d396ece40ab5c4238c4a1d0998e1eb941f Mon Sep 17 00:00:00 2001 From: Omar Polo Date: Fri, 18 Nov 2022 10:13:49 +0000 Subject: [PATCH 13/61] fix log_debug call --- src/picom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/picom.c b/src/picom.c index 7c52950..39837d8 100644 --- a/src/picom.c +++ b/src/picom.c @@ -539,8 +539,8 @@ static bool initialize_backend(session_t *ps) { } else { shader->attributes = 0; } - log_debug("Shader %s has attributes %ld", shader->key, - shader->attributes); + log_debug("Shader %s has attributes %" PRIu64, + shader->key, shader->attributes); } } From 89690c9843ed76c1592d047a5933b7fed408cfb1 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Mon, 21 Nov 2022 15:18:55 +0000 Subject: [PATCH 14/61] win: fix leaking of the mask image destroy_win_finish doesn't call win_release_images to free the images, so we need to add a release_mask call there. Related: #892 Signed-off-by: Yuxuan Shui --- src/win.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/win.c b/src/win.c index 1ce3161..65c1eb8 100644 --- a/src/win.c +++ b/src/win.c @@ -2426,6 +2426,7 @@ static void destroy_win_finish(session_t *ps, struct win *w) { assert(mw->shadow_image != NULL); win_release_shadow(ps->backend_data, mw); } + win_release_mask(ps->backend_data, mw); // Invalidate reg_ignore of windows below this one // TODO(yshui) what if next_w is not mapped?? From 11a195747ab37b70bf90db40e67a1d8904de9b76 Mon Sep 17 00:00:00 2001 From: Omar Polo Date: Fri, 18 Nov 2022 14:54:18 +0000 Subject: [PATCH 15/61] backend: egl: don't assume glEGLImageTargetTexStorage exists Use eglGetProcAddress instead. Fixes #932 --- src/backend/gl/egl.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/backend/gl/egl.c b/src/backend/gl/egl.c index 0c3c0d4..5c3b553 100644 --- a/src/backend/gl/egl.c +++ b/src/backend/gl/egl.c @@ -36,6 +36,8 @@ struct egl_data { EGLContext ctx; }; +static PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC glEGLImageTargetTexStorage = NULL; + /** * Free a glx_texture_t. */ @@ -202,6 +204,14 @@ static backend_t *egl_init(session_t *ps) { goto end; } + glEGLImageTargetTexStorage = + (PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC)eglGetProcAddress("glEGLImageTargetTexS" + "torageEXT"); + if (glEGLImageTargetTexStorage == NULL) { + log_error("Failed to get glEGLImageTargetTexStorageEXT."); + goto end; + } + gd->gl.decouple_texture_user_data = egl_decouple_user_data; gd->gl.release_user_data = egl_release_image; @@ -270,7 +280,7 @@ egl_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b wd->dim = 0; wd->inner->refcount = 1; glBindTexture(GL_TEXTURE_2D, inner->texture); - glEGLImageTargetTexStorageEXT(GL_TEXTURE_2D, eglpixmap->image, NULL); + glEGLImageTargetTexStorage(GL_TEXTURE_2D, eglpixmap->image, NULL); glBindTexture(GL_TEXTURE_2D, 0); gl_check_err(); From 9bf39b8d1d48a7636a3b81f9694e4e94f090cfe5 Mon Sep 17 00:00:00 2001 From: h7x4 Date: Thu, 24 Nov 2022 00:08:37 +0100 Subject: [PATCH 16/61] picom.sample.conf: Add egl to backend option doc --- picom.sample.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picom.sample.conf b/picom.sample.conf index 00cdb0c..62f7a50 100644 --- a/picom.sample.conf +++ b/picom.sample.conf @@ -219,7 +219,7 @@ blur-background-exclude = [ # Daemonize process. Fork to background after initialization. Causes issues with certain (badly-written) drivers. # daemon = false -# Specify the backend to use: `xrender`, `glx`, or `xr_glx_hybrid`. +# Specify the backend to use: `xrender`, `glx`, `egl` or `xr_glx_hybrid`. # `xrender` is the default one. # backend = "glx" From 49490ab99f1b04eac3a5db91f1c7cc816f7a3260 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sun, 27 Nov 2022 17:37:26 +0000 Subject: [PATCH 17/61] backend: egl: fix undefined symbols on old systems Users with an old EGL version won't be able to use the egl backend. OTOH we shouldn't prevent them from running picom because of a feature they won't even use. Don't assume the existence of EGL 1.5 symbols. Fixes #945 Signed-off-by: Yuxuan Shui --- src/backend/gl/egl.c | 46 ++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/src/backend/gl/egl.c b/src/backend/gl/egl.c index 5c3b553..5c40b00 100644 --- a/src/backend/gl/egl.c +++ b/src/backend/gl/egl.c @@ -37,6 +37,10 @@ struct egl_data { }; static PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC glEGLImageTargetTexStorage = NULL; +static PFNEGLCREATEIMAGEKHRPROC eglCreateImageProc = NULL; +static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageProc = NULL; +static PFNEGLGETPLATFORMDISPLAYPROC eglGetPlatformDisplayProc = NULL; +static PFNEGLCREATEPLATFORMWINDOWSURFACEPROC eglCreatePlatformWindowSurfaceProc = NULL; /** * Free a glx_texture_t. @@ -46,7 +50,7 @@ static void egl_release_image(backend_t *base, struct gl_texture *tex) { struct egl_pixmap *p = tex->user_data; // Release binding if (p->image != EGL_NO_IMAGE) { - eglDestroyImage(gd->display, p->image); + eglDestroyImageProc(gd->display, p->image); p->image = EGL_NO_IMAGE; } @@ -103,6 +107,20 @@ static bool egl_set_swap_interval(int interval, EGLDisplay dpy) { * Initialize OpenGL. */ static backend_t *egl_init(session_t *ps) { + bool success = false; + +#define get_proc(name, type) \ + name##Proc = (type)eglGetProcAddress(#name); \ + if (!name##Proc) { \ + log_error("Failed to get EGL function " #name); \ + goto end; \ + } + get_proc(eglCreateImage, PFNEGLCREATEIMAGEKHRPROC); + get_proc(eglDestroyImage, PFNEGLDESTROYIMAGEKHRPROC); + get_proc(eglGetPlatformDisplay, PFNEGLGETPLATFORMDISPLAYPROC); + get_proc(eglCreatePlatformWindowSurface, PFNEGLCREATEPLATFORMWINDOWSURFACEPROC); +#undef get_proc + // Check if we have the X11 platform const char *exts = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); if (strstr(exts, "EGL_EXT_platform_x11") == NULL) { @@ -110,14 +128,13 @@ static backend_t *egl_init(session_t *ps) { return NULL; } - bool success = false; auto gd = ccalloc(1, struct egl_data); - gd->display = eglGetPlatformDisplay(EGL_PLATFORM_X11_EXT, ps->dpy, - (EGLAttrib[]){ - EGL_PLATFORM_X11_SCREEN_EXT, - ps->scr, - EGL_NONE, - }); + gd->display = eglGetPlatformDisplayProc(EGL_PLATFORM_X11_EXT, ps->dpy, + (EGLAttrib[]){ + EGL_PLATFORM_X11_SCREEN_EXT, + ps->scr, + EGL_NONE, + }); if (gd->display == EGL_NO_DISPLAY) { log_error("Failed to get EGL display."); goto end; @@ -129,6 +146,11 @@ static backend_t *egl_init(session_t *ps) { goto end; } + if (major < 1 || (major == 1 && minor < 5)) { + log_error("EGL version too old, need at least 1.5."); + goto end; + } + // Check if EGL supports OpenGL const char *apis = eglQueryString(gd->display, EGL_CLIENT_APIS); if (strstr(apis, "OpenGL") == NULL) { @@ -172,7 +194,7 @@ static backend_t *egl_init(session_t *ps) { EGLConfig target_cfg = cfgs[0]; free(cfgs); - gd->target_win = eglCreatePlatformWindowSurface( + gd->target_win = eglCreatePlatformWindowSurfaceProc( gd->display, target_cfg, (xcb_window_t[]){session_get_target_window(ps)}, NULL); if (gd->target_win == EGL_NO_SURFACE) { log_error("Failed to create EGL surface."); @@ -260,8 +282,8 @@ egl_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b eglpixmap = cmalloc(struct egl_pixmap); eglpixmap->pixmap = pixmap; - eglpixmap->image = eglCreateImage(gd->display, gd->ctx, EGL_NATIVE_PIXMAP_KHR, - (EGLClientBuffer)(uintptr_t)pixmap, NULL); + eglpixmap->image = eglCreateImageProc(gd->display, gd->ctx, EGL_NATIVE_PIXMAP_KHR, + (EGLClientBuffer)(uintptr_t)pixmap, NULL); eglpixmap->owned = owned; if (eglpixmap->image == EGL_NO_IMAGE) { @@ -287,7 +309,7 @@ egl_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b return wd; err: if (eglpixmap && eglpixmap->image) { - eglDestroyImage(gd->display, eglpixmap->image); + eglDestroyImageProc(gd->display, eglpixmap->image); } free(eglpixmap); From 0a82f460e0d6e90a0d381bbfc2498b528d155870 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sun, 27 Nov 2022 18:07:05 +0000 Subject: [PATCH 18/61] backend: egl: fix warning --- src/backend/gl/egl.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/backend/gl/egl.c b/src/backend/gl/egl.c index 5c40b00..e6d4d90 100644 --- a/src/backend/gl/egl.c +++ b/src/backend/gl/egl.c @@ -108,6 +108,7 @@ static bool egl_set_swap_interval(int interval, EGLDisplay dpy) { */ static backend_t *egl_init(session_t *ps) { bool success = false; + struct egl_data *gd = NULL; #define get_proc(name, type) \ name##Proc = (type)eglGetProcAddress(#name); \ @@ -128,7 +129,7 @@ static backend_t *egl_init(session_t *ps) { return NULL; } - auto gd = ccalloc(1, struct egl_data); + gd = ccalloc(1, struct egl_data); gd->display = eglGetPlatformDisplayProc(EGL_PLATFORM_X11_EXT, ps->dpy, (EGLAttrib[]){ EGL_PLATFORM_X11_SCREEN_EXT, @@ -249,7 +250,9 @@ static backend_t *egl_init(session_t *ps) { end: if (!success) { - egl_deinit(&gd->gl.base); + if (gd != NULL) { + egl_deinit(&gd->gl.base); + } return NULL; } From ad5a0428036da53cf8d3dbcc50a49e10d37543b4 Mon Sep 17 00:00:00 2001 From: Evgeniy Baskov Date: Wed, 30 Nov 2022 20:00:46 +0300 Subject: [PATCH 19/61] win: consider border when creating the mask image With rounded corners, X11 native border and blur enabled, left and bottom 2*border_width pixels were not blurred, since mask did not include border_width, only content width and height. Create mask image with dimensions that include border width. Signed-off-by: Evgeniy Baskov --- src/win.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/win.c b/src/win.c index 65c1eb8..26c0200 100644 --- a/src/win.c +++ b/src/win.c @@ -357,7 +357,7 @@ bool win_bind_mask(struct backend_base *b, struct managed_win *w) { auto reg_bound_local = win_get_bounding_shape_global_by_val(w); pixman_region32_translate(®_bound_local, -w->g.x, -w->g.y); w->mask_image = b->ops->make_mask( - b, (geometry_t){.width = w->g.width, .height = w->g.height}, ®_bound_local); + b, (geometry_t){.width = w->widthb, .height = w->heightb}, ®_bound_local); pixman_region32_fini(®_bound_local); if (!w->mask_image) { From 9fe7e65e3ea5a75f59f8c8bb32f8589e679c5976 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 22 Nov 2022 16:12:36 +0000 Subject: [PATCH 20/61] c2: replace pcre with pcre2 Because pcre has been deprecated.[1] There are subtle changes from pcre to pcre2, so this could be a breaking change. Closes #895 [1]: https://www.pcre.org/original/changelog.txt Signed-off-by: Yuxuan Shui --- src/c2.c | 155 ++++++++++++++++++++++++------------------------ src/meson.build | 5 +- 2 files changed, 79 insertions(+), 81 deletions(-) diff --git a/src/c2.c b/src/c2.c index 80ecb24..f8af699 100644 --- a/src/c2.c +++ b/src/c2.c @@ -17,15 +17,8 @@ // libpcre #ifdef CONFIG_REGEX_PCRE -#include - -// For compatibility with #endif @@ -89,49 +82,52 @@ struct _c2_b { /// Structure for leaf element in a window condition struct _c2_l { bool neg : 1; - enum { C2_L_OEXISTS, - C2_L_OEQ, - C2_L_OGT, - C2_L_OGTEQ, - C2_L_OLT, - C2_L_OLTEQ, + enum { + C2_L_OEXISTS, + C2_L_OEQ, + C2_L_OGT, + C2_L_OGTEQ, + C2_L_OLT, + C2_L_OLTEQ, } op : 3; - enum { C2_L_MEXACT, - C2_L_MSTART, - C2_L_MCONTAINS, - C2_L_MWILDCARD, - C2_L_MPCRE, + enum { + C2_L_MEXACT, + C2_L_MSTART, + C2_L_MCONTAINS, + C2_L_MWILDCARD, + C2_L_MPCRE, } match : 3; bool match_ignorecase : 1; char *tgt; xcb_atom_t tgtatom; bool tgt_onframe; int index; - enum { C2_L_PUNDEFINED = -1, - C2_L_PID = 0, - C2_L_PX, - C2_L_PY, - C2_L_PX2, - C2_L_PY2, - C2_L_PWIDTH, - C2_L_PHEIGHT, - C2_L_PWIDTHB, - C2_L_PHEIGHTB, - C2_L_PBDW, - C2_L_PFULLSCREEN, - C2_L_POVREDIR, - C2_L_PARGB, - C2_L_PFOCUSED, - C2_L_PWMWIN, - C2_L_PBSHAPED, - C2_L_PROUNDED, - C2_L_PCLIENT, - C2_L_PWINDOWTYPE, - C2_L_PLEADER, - C2_L_PNAME, - C2_L_PCLASSG, - C2_L_PCLASSI, - C2_L_PROLE, + enum { + C2_L_PUNDEFINED = -1, + C2_L_PID = 0, + C2_L_PX, + C2_L_PY, + C2_L_PX2, + C2_L_PY2, + C2_L_PWIDTH, + C2_L_PHEIGHT, + C2_L_PWIDTHB, + C2_L_PHEIGHTB, + C2_L_PBDW, + C2_L_PFULLSCREEN, + C2_L_POVREDIR, + C2_L_PARGB, + C2_L_PFOCUSED, + C2_L_PWMWIN, + C2_L_PBSHAPED, + C2_L_PROUNDED, + C2_L_PCLIENT, + C2_L_PWINDOWTYPE, + C2_L_PLEADER, + C2_L_PNAME, + C2_L_PCLASSG, + C2_L_PCLASSI, + C2_L_PROLE, } predef; enum c2_l_type { C2_L_TUNDEFINED, @@ -142,15 +138,16 @@ struct _c2_l { C2_L_TDRAWABLE, } type; int format; - enum { C2_L_PTUNDEFINED, - C2_L_PTSTRING, - C2_L_PTINT, + enum { + C2_L_PTUNDEFINED, + C2_L_PTSTRING, + C2_L_PTINT, } ptntype; char *ptnstr; long ptnint; #ifdef CONFIG_REGEX_PCRE - pcre *regex_pcre; - pcre_extra *regex_pcre_extra; + pcre2_code *regex_pcre; + pcre2_match_data *regex_pcre_match; #endif }; @@ -1059,32 +1056,31 @@ static bool c2_l_postprocess(session_t *ps, c2_l_t *pleaf) { // PCRE patterns if (C2_L_PTSTRING == pleaf->ptntype && C2_L_MPCRE == pleaf->match) { #ifdef CONFIG_REGEX_PCRE - const char *error = NULL; - int erroffset = 0; - int options = 0; + int errorcode = 0; + PCRE2_SIZE erroffset = 0; + unsigned int options = 0; // Ignore case flag - if (pleaf->match_ignorecase) - options |= PCRE_CASELESS; + if (pleaf->match_ignorecase) { + options |= PCRE2_CASELESS; + } // Compile PCRE expression pleaf->regex_pcre = - pcre_compile(pleaf->ptnstr, options, &error, &erroffset, NULL); - if (!pleaf->regex_pcre) { - log_error("Pattern \"%s\": PCRE regular expression parsing " + pcre2_compile((PCRE2_SPTR)pleaf->ptnstr, PCRE2_ZERO_TERMINATED, + options, &errorcode, &erroffset, NULL); + if (pleaf->regex_pcre == NULL) { + PCRE2_UCHAR buffer[256]; + pcre2_get_error_message(errorcode, buffer, sizeof(buffer)); + log_error("Pattern \"%s\": PCRE regular expression " + "parsing " "failed on " - "offset %d: %s", - pleaf->ptnstr, erroffset, error); + "offset %zu: %s", + pleaf->ptnstr, erroffset, buffer); return false; } -#ifdef CONFIG_REGEX_PCRE_JIT - pleaf->regex_pcre_extra = - pcre_study(pleaf->regex_pcre, PCRE_STUDY_JIT_COMPILE, &error); - if (!pleaf->regex_pcre_extra) { - printf("Pattern \"%s\": PCRE regular expression study failed: %s", - pleaf->ptnstr, error); - } -#endif + pleaf->regex_pcre_match = + pcre2_match_data_create_from_pattern(pleaf->regex_pcre, NULL); // Free the target string // free(pleaf->tgt); @@ -1102,16 +1098,18 @@ static bool c2_tree_postprocess(session_t *ps, c2_ptr_t node) { if (!node.isbranch) { return c2_l_postprocess(ps, node.l); } - if (!c2_tree_postprocess(ps, node.b->opr1)) + if (!c2_tree_postprocess(ps, node.b->opr1)) { return false; + } return c2_tree_postprocess(ps, node.b->opr2); } bool c2_list_postprocess(session_t *ps, c2_lptr_t *list) { c2_lptr_t *head = list; while (head) { - if (!c2_tree_postprocess(ps, head->ptr)) + if (!c2_tree_postprocess(ps, head->ptr)) { return false; + } head = head->next; } return true; @@ -1124,8 +1122,9 @@ static void c2_free(c2_ptr_t p) { if (p.isbranch) { c2_b_t *const pbranch = p.b; - if (!pbranch) + if (!pbranch) { return; + } c2_free(pbranch->opr1); c2_free(pbranch->opr2); @@ -1135,14 +1134,15 @@ static void c2_free(c2_ptr_t p) { else { c2_l_t *const pleaf = p.l; - if (!pleaf) + if (!pleaf) { return; + } free(pleaf->tgt); free(pleaf->ptnstr); #ifdef CONFIG_REGEX_PCRE - pcre_free(pleaf->regex_pcre); - LPCRE_FREE_STUDY(pleaf->regex_pcre_extra); + pcre2_code_free(pleaf->regex_pcre); + pcre2_match_data_free(pleaf->regex_pcre_match); #endif free(pleaf); } @@ -1550,9 +1550,10 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w case C2_L_MPCRE: #ifdef CONFIG_REGEX_PCRE assert(strlen(tgt) <= INT_MAX); - res = (pcre_exec(pleaf->regex_pcre, - pleaf->regex_pcre_extra, tgt, - (int)strlen(tgt), 0, 0, NULL, 0) >= 0); + assert(pleaf->regex_pcre); + res = (pcre2_match(pleaf->regex_pcre, (PCRE2_SPTR)tgt, + strlen(tgt), 0, 0, + pleaf->regex_pcre_match, NULL) > 0); #else assert(0); #endif diff --git a/src/meson.build b/src/meson.build index 60d83a8..09eb07b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -44,11 +44,8 @@ if get_option('config_file') srcs += [ 'config_libconfig.c' ] endif if get_option('regex') - pcre = dependency('libpcre', required: true) + pcre = dependency('libpcre2-8', required: true) cflags += ['-DCONFIG_REGEX_PCRE'] - if pcre.version().version_compare('>=8.20') - cflags += ['-DCONFIG_REGEX_PCRE_JIT'] - endif deps += [pcre] endif From d647ccca16483b8a2c420b31901e3c321ef6bd75 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 30 Nov 2022 19:23:47 +0000 Subject: [PATCH 21/61] README: update pcre requirements Signed-off-by: Yuxuan Shui --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ccf0338..49ffedf 100644 --- a/README.md +++ b/README.md @@ -37,20 +37,20 @@ Assuming you already have all the usual building tools installed (e.g. gcc, pyth * libdbus (optional, disable with the `-Ddbus=false` meson configure flag) * libconfig (optional, disable with the `-Dconfig_file=false` meson configure flag) * libGL, libEGL (optional, disable with the `-Dopengl=false` meson configure flag) -* libpcre (optional, disable with the `-Dregex=false` meson configure flag) +* libpcre2 (optional, disable with the `-Dregex=false` meson configure flag) * libev * uthash On Debian based distributions (e.g. Ubuntu), the needed packages are ``` -libxext-dev libxcb1-dev libxcb-damage0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-render-util0-dev libxcb-render0-dev libxcb-randr0-dev libxcb-composite0-dev libxcb-image0-dev libxcb-present-dev libxcb-xinerama0-dev libxcb-glx0-dev libpixman-1-dev libdbus-1-dev libconfig-dev libgl-dev libegl-dev libpcre2-dev libpcre3-dev libevdev-dev uthash-dev libev-dev libx11-xcb-dev meson +libxext-dev libxcb1-dev libxcb-damage0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-render-util0-dev libxcb-render0-dev libxcb-randr0-dev libxcb-composite0-dev libxcb-image0-dev libxcb-present-dev libxcb-xinerama0-dev libxcb-glx0-dev libpixman-1-dev libdbus-1-dev libconfig-dev libgl-dev libegl-dev libpcre2-dev libevdev-dev uthash-dev libev-dev libx11-xcb-dev meson ``` On Fedora, the needed packages are ``` -dbus-devel gcc git libconfig-devel libdrm-devel libev-devel libX11-devel libX11-xcb libXext-devel libxcb-devel libGL-devel libEGL-devel meson pcre-devel pixman-devel uthash-devel xcb-util-image-devel xcb-util-renderutil-devel xorg-x11-proto-devel +dbus-devel gcc git libconfig-devel libdrm-devel libev-devel libX11-devel libX11-xcb libXext-devel libxcb-devel libGL-devel libEGL-devel meson pcre2-devel pixman-devel uthash-devel xcb-util-image-devel xcb-util-renderutil-devel xorg-x11-proto-devel ``` To build the documents, you need `asciidoc` From 144e78fd5dd0bf4c76620626f3c3bfe6e6f61220 Mon Sep 17 00:00:00 2001 From: Jake Date: Tue, 29 Nov 2022 22:20:22 -0800 Subject: [PATCH 22/61] Change dreaw -> draw --- src/options.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/options.c b/src/options.c index 6357907..6c043de 100644 --- a/src/options.c +++ b/src/options.c @@ -46,7 +46,7 @@ static const struct picom_option picom_options[] = { {"fade-delta" , required_argument, 'D', NULL , "The time between steps in a fade in milliseconds. (default 10)"}, {"menu-opacity" , required_argument, 'm', NULL , "The opacity for menus. (default 1.0)"}, {"shadow" , no_argument , 'c', NULL , "Enabled client-side shadows on windows."}, - {"clear-shadow" , no_argument , 'z', NULL , "Don't dreaw shadow behind the window."}, + {"clear-shadow" , no_argument , 'z', NULL , "Don't draw shadow behind the window."}, {"fading" , no_argument , 'f', NULL , "Fade windows in/out when opening/closing and when opacity changes, " "unless --no-fading-openclose is used."}, {"inactive-opacity" , required_argument, 'i', NULL , "Opacity of inactive windows. (0.1 - 1.0)"}, From dacadb9fc3f145f068b120aadd2764fcf6283b65 Mon Sep 17 00:00:00 2001 From: Maxim Solovyov Date: Thu, 1 Dec 2022 20:55:57 +0300 Subject: [PATCH 23/61] README: fix meson's warnings about setup commands this fixes `WARNING: Running the setup command as `meson [options]` instead of `meson setup [options]` is ambiguous and deprecated.` warning while setting up the project --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 49ffedf..b1711eb 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ To build the documents, you need `asciidoc` ```bash $ git submodule update --init --recursive -$ meson --buildtype=release . build +$ meson setup --buildtype=release . build $ ninja -C build ``` @@ -70,13 +70,12 @@ If you have libraries and/or headers installed at non-default location (e.g. und You can do that by setting the `CPPFLAGS` and `LDFLAGS` environment variables when running `meson`. Like this: ```bash -$ LDFLAGS="-L/path/to/libraries" CPPFLAGS="-I/path/to/headers" meson --buildtype=release . build - +$ LDFLAGS="-L/path/to/libraries" CPPFLAGS="-I/path/to/headers" meson setup --buildtype=release . build ``` As an example, on FreeBSD, you might have to run meson with: ```bash -$ LDFLAGS="-L/usr/local/lib" CPPFLAGS="-I/usr/local/include" meson --buildtype=release . build +$ LDFLAGS="-L/usr/local/lib" CPPFLAGS="-I/usr/local/include" meson setup --buildtype=release . build $ ninja -C build ``` From 4a7a9e807938f49a989924b11b4046675ce0cc8b Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 1 Dec 2022 21:21:19 +0000 Subject: [PATCH 24/61] Update README.md Signed-off-by: Yuxuan Shui --- README.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b1711eb..f6bdfe1 100644 --- a/README.md +++ b/README.md @@ -89,15 +89,21 @@ Default install prefix is `/usr/local`, you can change it with `meson configure ## How to Contribute -### Code +All contributions are welcome! -You can look at the [Projects](https://github.com/yshui/picom/projects) page, and see if there is anything that interests you. Or you can take a look at the [Issues](https://github.com/yshui/picom/issues). +New features you think should be included in picom, a fix for a bug you found - please open a PR! -### Non-code +You can take a look at the [Issues](https://github.com/yshui/picom/issues). -Even if you don't want to contribute code, you can still contribute by compiling and running this branch, and report any issue you can find. +Contributions to the documents and wiki are also appreciated. -Contributions to the documents and wiki will also be appreciated. +Even if you don't want to add anything to picom, you are still helping by compiling and running this branch, and report any issue you can find. + +### Become a Collaborator + +Becoming a collaborator of picom requires significant time commitment. You are expected to reply to issue reports, reviewing PRs, and sometimes fix bugs or implement new feature. You won't be able to push to the main branch directly, and all you code still has to go through code review. + +If this sounds good to you, feel free to contact me. ## Contributors From 4ecc3e65a9ac5a23383e6442f74b8923be8a39be Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 2 Dec 2022 17:51:30 +0000 Subject: [PATCH 25/61] man: fix typo Signed-off-by: Yuxuan Shui --- man/picom.1.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/picom.1.asciidoc b/man/picom.1.asciidoc index 61af5a2..e8c4cec 100644 --- a/man/picom.1.asciidoc +++ b/man/picom.1.asciidoc @@ -481,7 +481,7 @@ Available options of the 'blur' section are: :: An integer in the range 0-20. The strength of the 'dual_kawase' blur method. Corresponds to the *--blur-strength* command line option. If set to zero, the value requested by *--blur-size* is approximated (default: 5). *kernel*::: - A string. The kernel to use for the 'kernel' blur method, specified in the same format as the *--blur-kerns* option. Corresponds to the *--blur-kerns* command line option. + A string. The kernel to use for the 'kernel' blur method, specified in the same format as the *--blur-kern* option. Corresponds to the *--blur-kern* command line option. SIGNALS ------- From 123ef512109e647eae7b3022b434fab1b62b3197 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 30 Nov 2022 04:16:15 +0000 Subject: [PATCH 26/61] backend: gl: add dither Add bayer ordered dithering when presenting to screen. Reduce banding when using a strong blur. Also use 16-bit intermediary textures to preserve precision in the rendering pipeline. Related: #602 Signed-off-by: Yuxuan Shui --- src/backend/gl/blur.c | 2 +- src/backend/gl/gl_common.c | 6 ++++-- src/backend/gl/gl_common.h | 5 +++-- src/backend/gl/shaders.c | 30 ++++++++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/backend/gl/blur.c b/src/backend/gl/blur.c index b73aeee..aad1e5f 100644 --- a/src/backend/gl/blur.c +++ b/src/backend/gl/blur.c @@ -284,7 +284,7 @@ bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, } glBindTexture(GL_TEXTURE_2D, bctx->blur_textures[i]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_size->width, + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16, tex_size->width, tex_size->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); if (bctx->method == BLUR_METHOD_DUAL_KAWASE) { diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index 9eec872..40f95ca 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -633,7 +633,7 @@ void gl_resize(struct gl_data *gd, int width, int height) { assert(viewport_dimensions[1] >= gd->height); glBindTexture(GL_TEXTURE_2D, gd->back_texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_BGR, + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, NULL); gl_check_err(); @@ -879,7 +879,9 @@ bool gl_init(struct gl_data *gd, session_t *ps) { glUniformMatrix4fv(pml, 1, false, projection_matrix[0]); glUseProgram(0); - gd->present_prog = gl_create_program_from_str(present_vertex_shader, dummy_frag); + gd->present_prog = + gl_create_program_from_strv((const char *[]){present_vertex_shader, NULL}, + (const char *[]){present_frag, dither_glsl, NULL}); if (!gd->present_prog) { log_error("Failed to create the present shader"); return false; diff --git a/src/backend/gl/gl_common.h b/src/backend/gl/gl_common.h index 3a78865..bad75ed 100644 --- a/src/backend/gl/gl_common.h +++ b/src/backend/gl/gl_common.h @@ -288,5 +288,6 @@ static const GLuint vert_in_texcoord_loc = 1; #define QUOTE(...) #__VA_ARGS__ extern const char vertex_shader[], copy_with_mask_frag[], masking_glsl[], dummy_frag[], - fill_frag[], fill_vert[], interpolating_frag[], interpolating_vert[], win_shader_glsl[], - win_shader_default[], present_vertex_shader[], shadow_colorization_frag[]; + present_frag[], fill_frag[], fill_vert[], interpolating_frag[], interpolating_vert[], + win_shader_glsl[], win_shader_default[], present_vertex_shader[], dither_glsl[], + shadow_colorization_frag[]; diff --git a/src/backend/gl/shaders.c b/src/backend/gl/shaders.c index 4a18e62..bd620fe 100644 --- a/src/backend/gl/shaders.c +++ b/src/backend/gl/shaders.c @@ -9,6 +9,15 @@ const char dummy_frag[] = GLSL(330, } ); +const char present_frag[] = GLSL(330, + uniform sampler2D tex; + in vec2 texcoord; + vec4 dither(vec4, vec2); + void main() { + gl_FragColor = dither(texelFetch(tex, ivec2(texcoord.xy), 0), texcoord); + } +); + const char copy_with_mask_frag[] = GLSL(330, uniform sampler2D tex; in vec2 texcoord; @@ -174,6 +183,27 @@ const char vertex_shader[] = GLSL(330, texcoord = in_texcoord + texorig; } ); +const char dither_glsl[] = GLSL(330, + // Stolen from: https://www.shadertoy.com/view/7sfXDn + float bayer2(vec2 a) { + a = floor(a); + return fract(a.x / 2. + a.y * a.y * .75); + } + // 16 * 16 is 2^8, so in total we have equivalent of 16-bit + // color depth, should be enough? + float bayer(vec2 a16) { + vec2 a8 = a16 * .5; + vec2 a4 = a8 * .5; + vec2 a2 = a4 * .5; + float bayer32 = ((bayer2(a2) * .25 + bayer2( a4)) + * .25 + bayer2( a8)) + * .25 + bayer2(a16); + return bayer32; + } + vec4 dither(vec4 c, vec2 coord) { + return vec4(c + bayer(coord) / 255.0); + } +); const char shadow_colorization_frag[] = GLSL(330, uniform vec4 color; uniform sampler2D tex; From 2173654fbd80065ed0876bb6903ccdd3f4dfb568 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 30 Nov 2022 05:28:57 +0000 Subject: [PATCH 27/61] options: add dithered-present option See also 0a2cd0f14eebc41cc4c7b7c9a3db964a2b1a5ab0 Related: #602 Signed-off-by: Yuxuan Shui --- man/picom.1.asciidoc | 3 +++ picom.sample.conf | 5 +++++ src/backend/gl/blur.c | 16 ++++++++++------ src/backend/gl/gl_common.c | 22 ++++++++++++++++------ src/backend/gl/gl_common.h | 10 ++++++---- src/backend/xrender/xrender.c | 4 ++++ src/config_libconfig.c | 2 ++ src/options.c | 8 ++++++++ 8 files changed, 54 insertions(+), 16 deletions(-) diff --git a/man/picom.1.asciidoc b/man/picom.1.asciidoc index e8c4cec..b4ff69b 100644 --- a/man/picom.1.asciidoc +++ b/man/picom.1.asciidoc @@ -268,6 +268,9 @@ May also be one of the predefined kernels: `3x3box` (default), `5x5box`, `7x7box *--window-shader-fg-rule* 'SHADER':'CONDITION':: Specify GLSL fragment shader path for rendering window contents using patterns. Similar to *--opacity-rule*, arguments should be in the format of 'SHADER:CONDITION', e.g. "shader.frag:name = \'window\'". Leading and trailing whitespaces in 'SHADER' will be trimmed. If 'SHADER' is "default", then the default shader will be used for the matching windows. (This also unfortunately means you can't use a shader file named "default"). Does not work when *--legacy-backends* is enabled. +*--dithered-present* + Use higher precision during rendering, and apply dither when presenting the rendered screen. Reduces banding artifacts, but might cause performance degradation. Only works with OpenGL. + 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. diff --git a/picom.sample.conf b/picom.sample.conf index 62f7a50..52744cc 100644 --- a/picom.sample.conf +++ b/picom.sample.conf @@ -224,6 +224,11 @@ blur-background-exclude = [ # backend = "glx" +# Use higher precision during rendering, and apply dither when presenting the +# rendered screen. Reduces banding artifacts, but might cause performance +# degradation. Only works with OpenGL. +dithered-present = false; + # Enable/disable VSync. # vsync = true diff --git a/src/backend/gl/blur.c b/src/backend/gl/blur.c index aad1e5f..9297038 100644 --- a/src/backend/gl/blur.c +++ b/src/backend/gl/blur.c @@ -259,10 +259,10 @@ bool gl_dual_kawase_blur(double opacity, struct gl_blur_context *bctx, const rec return true; } -bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, - coord_t mask_dst, const region_t *reg_blur, - const region_t *reg_visible attr_unused, GLuint source_texture, - geometry_t source_size, GLuint target_fbo, GLuint default_mask) { +bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, coord_t mask_dst, + const region_t *reg_blur, const region_t *reg_visible attr_unused, + GLuint source_texture, geometry_t source_size, GLuint target_fbo, + GLuint default_mask, bool high_precision) { bool ret = false; if (source_size.width != bctx->fb_width || source_size.height != bctx->fb_height) { @@ -284,7 +284,11 @@ bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, } glBindTexture(GL_TEXTURE_2D, bctx->blur_textures[i]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16, tex_size->width, + GLint format = GL_RGBA8; + if (high_precision) { + format = GL_RGBA16; + } + glTexImage2D(GL_TEXTURE_2D, 0, format, tex_size->width, tex_size->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); if (bctx->method == BLUR_METHOD_DUAL_KAWASE) { @@ -406,7 +410,7 @@ bool gl_blur(backend_t *base, double opacity, void *ctx, void *mask, coord_t mas return gl_blur_impl(opacity, bctx, mask, mask_dst, reg_blur, reg_visible, gd->back_texture, (geometry_t){.width = gd->width, .height = gd->height}, - gd->back_fbo, gd->default_mask_texture); + gd->back_fbo, gd->default_mask_texture, gd->dithered_present); } static inline void gl_free_blur_shader(gl_blur_shader_t *shader) { diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index 40f95ca..c5dcc3c 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -628,13 +628,16 @@ void gl_resize(struct gl_data *gd, int width, int height) { gd->height = height; gd->width = width; + GLint format = GL_RGB8; + if (gd->dithered_present) { + format = GL_RGB16; + } assert(viewport_dimensions[0] >= gd->width); assert(viewport_dimensions[1] >= gd->height); glBindTexture(GL_TEXTURE_2D, gd->back_texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16, width, height, 0, GL_BGR, - GL_UNSIGNED_BYTE, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, NULL); gl_check_err(); } @@ -879,9 +882,16 @@ bool gl_init(struct gl_data *gd, session_t *ps) { glUniformMatrix4fv(pml, 1, false, projection_matrix[0]); glUseProgram(0); - gd->present_prog = - gl_create_program_from_strv((const char *[]){present_vertex_shader, NULL}, - (const char *[]){present_frag, dither_glsl, NULL}); + gd->dithered_present = ps->o.dithered_present; + if (gd->dithered_present) { + gd->present_prog = gl_create_program_from_strv( + (const char *[]){present_vertex_shader, NULL}, + (const char *[]){present_frag, dither_glsl, NULL}); + } else { + gd->present_prog = gl_create_program_from_strv( + (const char *[]){present_vertex_shader, NULL}, + (const char *[]){dummy_frag, NULL}); + } if (!gd->present_prog) { log_error("Failed to create the present shader"); return false; @@ -1287,7 +1297,7 @@ void *gl_shadow_from_mask(backend_t *base, void *mask, 1.0, gsctx->blur_context, NULL, (coord_t){0}, ®_blur, NULL, source_texture, (geometry_t){.width = new_inner->width, .height = new_inner->height}, - fbo, gd->default_mask_texture); + fbo, gd->default_mask_texture, gd->dithered_present); pixman_region32_fini(®_blur); } diff --git a/src/backend/gl/gl_common.h b/src/backend/gl/gl_common.h index bad75ed..b7ef2b4 100644 --- a/src/backend/gl/gl_common.h +++ b/src/backend/gl/gl_common.h @@ -109,6 +109,8 @@ struct gl_data { GLuint back_texture, back_fbo; GLuint present_prog; + bool dithered_present; + GLuint default_mask_texture; /// Called when an gl_texture is decoupled from the texture it refers. Returns @@ -163,10 +165,10 @@ void *gl_clone(backend_t *base, const void *image_data, const region_t *reg_visi bool gl_blur(backend_t *base, double opacity, void *ctx, void *mask, coord_t mask_dst, const region_t *reg_blur, const region_t *reg_visible); -bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, - coord_t mask_dst, const region_t *reg_blur, - const region_t *reg_visible attr_unused, GLuint source_texture, - geometry_t source_size, GLuint target_fbo, GLuint default_mask); +bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, coord_t mask_dst, + const region_t *reg_blur, const region_t *reg_visible attr_unused, + GLuint source_texture, geometry_t source_size, GLuint target_fbo, + GLuint default_mask, bool high_precision); void *gl_create_blur_context(backend_t *base, enum blur_method, void *args); void gl_destroy_blur_context(backend_t *base, void *ctx); struct backend_shadow_context *gl_create_shadow_context(backend_t *base, double radius); diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index 2b7f8e1..e85a85f 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -858,6 +858,10 @@ static void get_blur_size(void *blur_context, int *width, int *height) { } static backend_t *backend_xrender_init(session_t *ps) { + if (ps->o.dithered_present) { + log_warn("\"dithered-present\" is not supported by the xrender backend."); + } + auto xd = ccalloc(1, struct _xrender_data); init_backend_base(&xd->base, ps); diff --git a/src/config_libconfig.c b/src/config_libconfig.c index c6d5aa8..fdecbf7 100644 --- a/src/config_libconfig.c +++ b/src/config_libconfig.c @@ -461,6 +461,8 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad lcfg_lookup_bool(&cfg, "no-ewmh-fullscreen", &opt->no_ewmh_fullscreen); // --transparent-clipping lcfg_lookup_bool(&cfg, "transparent-clipping", &opt->transparent_clipping); + // --dithered_present + lcfg_lookup_bool(&cfg, "dithered-present", &opt->dithered_present); // --transparent-clipping-exclude parse_cfg_condlst(&cfg, &opt->transparent_clipping_blacklist, "transparent-clipping-exclude"); diff --git a/src/options.c b/src/options.c index 6c043de..752b5a4 100644 --- a/src/options.c +++ b/src/options.c @@ -168,6 +168,10 @@ static const struct picom_option picom_options[] = { "similar to --opacity-rule. SHADER_PATH can be \"default\", in which case " "the default shader will be used. Does not work when --legacy-backends is " "enabled. See man page for more details"}, + // 338 is transparent-clipping-exclude + {"dithered-present" , no_argument , 339, NULL , "Use higher precision during rendering, and apply dither when presenting the " + "rendered screen. Reduces banding artifacts, but might cause performance " + "degradation. Only works with OpenGL."}, {"legacy-backends" , no_argument , 733, NULL , "Use deprecated version of the backends."}, {"monitor-repaint" , no_argument , 800, NULL , "Highlight the updated area of the screen. For debugging."}, {"diagnostics" , no_argument , 801, NULL , "Print diagnostic information"}, @@ -1193,6 +1197,10 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable, // --clip-shadow-above condlst_add(&opt->shadow_clip_list, optarg); break; + case 339: + // --dithered-present + opt->dithered_present = true; + break; P_CASEBOOL(733, legacy_backends); P_CASEBOOL(800, monitor_repaint); case 801: From 7f18e74b8fcbd22404937489c72b7dd7432549e3 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 30 Nov 2022 12:13:09 +0000 Subject: [PATCH 28/61] backend: gl: don't add dither where it's not needed If a pixel is perfectly representable as an 8-bit number, don't add dither. Reduce artifacts where dither is unnecessary. Signed-off-by: Yuxuan Shui --- src/backend/gl/shaders.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/backend/gl/shaders.c b/src/backend/gl/shaders.c index bd620fe..90f636b 100644 --- a/src/backend/gl/shaders.c +++ b/src/backend/gl/shaders.c @@ -201,7 +201,9 @@ const char dither_glsl[] = GLSL(330, return bayer32; } vec4 dither(vec4 c, vec2 coord) { - return vec4(c + bayer(coord) / 255.0); + vec4 residual = mod(c, 1.0 / 255.0); + vec4 dithered = vec4(greaterThan(residual, vec4(1e-4))); + return vec4(c + dithered * bayer(coord) / 255.0); } ); const char shadow_colorization_frag[] = GLSL(330, From d21fb34e55b2e4cd4e6e73b3063cc4b7e30423c0 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 1 Dec 2022 18:54:22 +0000 Subject: [PATCH 29/61] backend: gl: try different back buffer formats Prefer RGB formats first, because they use less memory; but fallback to RGBA formats, as they are formats required by OpenGL. Signed-off-by: Yuxuan Shui --- src/backend/gl/gl_common.c | 30 +++++++++++++++++++----------- src/backend/gl/gl_common.h | 1 + 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index c5dcc3c..9f9ec3b 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -628,16 +628,13 @@ void gl_resize(struct gl_data *gd, int width, int height) { gd->height = height; gd->width = width; - GLint format = GL_RGB8; - if (gd->dithered_present) { - format = GL_RGB16; - } assert(viewport_dimensions[0] >= gd->width); assert(viewport_dimensions[1] >= gd->height); glBindTexture(GL_TEXTURE_2D, gd->back_texture); - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, gd->back_format, width, height, 0, GL_BGR, + GL_UNSIGNED_BYTE, NULL); gl_check_err(); } @@ -925,14 +922,25 @@ bool gl_init(struct gl_data *gd, session_t *ps) { glUniformMatrix4fv(pml, 1, false, projection_matrix[0]); glUseProgram(0); - // Set up the size of the back texture - gl_resize(gd, ps->root_width, ps->root_height); - + // Set up the size and format of the back texture glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gd->back_fbo); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - gd->back_texture, 0); glDrawBuffer(GL_COLOR_ATTACHMENT0); - if (!gl_check_fb_complete(GL_FRAMEBUFFER)) { + const GLint *format = (const GLint[]){GL_RGB8, GL_RGBA8}; + if (gd->dithered_present) { + format = (const GLint[]){GL_RGB16, GL_RGBA16}; + } + for (int i = 0; i < 2; i++) { + gd->back_format = format[i]; + gl_resize(gd, ps->root_width, ps->root_height); + + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, gd->back_texture, 0); + if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { + log_info("Using back buffer format %#x", gd->back_format); + break; + } + } + if (!gl_check_fb_complete(GL_DRAW_FRAMEBUFFER)) { return false; } glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); diff --git a/src/backend/gl/gl_common.h b/src/backend/gl/gl_common.h index b7ef2b4..4aad9c0 100644 --- a/src/backend/gl/gl_common.h +++ b/src/backend/gl/gl_common.h @@ -107,6 +107,7 @@ struct gl_data { gl_fill_shader_t fill_shader; gl_shadow_shader_t shadow_shader; GLuint back_texture, back_fbo; + GLint back_format; GLuint present_prog; bool dithered_present; From bc1f99f2aec56bf47732ac4ef016430d4447f497 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 2 Dec 2022 02:57:28 +0000 Subject: [PATCH 30/61] backend: gl: fix use-after-scope 'format' was pointing to an array with a shorter lifetime suggested by @tryone144 Co-authored-by: Bernd Busse --- src/backend/gl/gl_common.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index 9f9ec3b..ea08cd2 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -925,10 +925,8 @@ bool gl_init(struct gl_data *gd, session_t *ps) { // Set up the size and format of the back texture glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gd->back_fbo); glDrawBuffer(GL_COLOR_ATTACHMENT0); - const GLint *format = (const GLint[]){GL_RGB8, GL_RGBA8}; - if (gd->dithered_present) { - format = (const GLint[]){GL_RGB16, GL_RGBA16}; - } + const GLint *format = gd->dithered_present ? (const GLint[]){GL_RGB16, GL_RGBA16} + : (const GLint[]){GL_RGB8, GL_RGBA8}; for (int i = 0; i < 2; i++) { gd->back_format = format[i]; gl_resize(gd, ps->root_width, ps->root_height); From 9c3204cc72afa569e1818907744ecd5ccfd7c688 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Mon, 12 Dec 2022 08:51:42 +0000 Subject: [PATCH 31/61] utils: add rolling_max For tracking rolling max of a stream of integers. Signed-off-by: Yuxuan Shui --- src/utils.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/utils.h | 8 ++++ 2 files changed, 126 insertions(+), 2 deletions(-) diff --git a/src/utils.c b/src/utils.c index 8a27f39..8190226 100644 --- a/src/utils.c +++ b/src/utils.c @@ -4,6 +4,7 @@ #include "compiler.h" #include "string_utils.h" +#include "test.h" #include "utils.h" /// Report allocation failure without allocating memory @@ -36,8 +37,7 @@ void report_allocation_failure(const char *func, const char *file, unsigned int /// Calculates next closest power of two of 32bit integer n /// ref: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 /// -int next_power_of_two(int n) -{ +int next_power_of_two(int n) { n--; n |= n >> 1; n |= n >> 2; @@ -48,4 +48,120 @@ int next_power_of_two(int n) return n; } +/// Track the rolling maximum of a stream of integers. +struct rolling_max { + /// A priority queue holding the indices of the maximum element candidates. + /// The head of the queue is the index of the maximum element. + /// The indices in the queue are the "original" indices. + /// + /// There are only `capacity` elements in `elem`, all previous elements are + /// discarded. But the discarded elements' indices are not forgotten, that's why + /// it's called the "original" indices. + int *p; + int p_head, np; + + /// The elemets + int *elem; + int elem_head, nelem; + + int window_size; +}; + +void rolling_max_destroy(struct rolling_max *rm) { + free(rm->elem); + free(rm->p); + free(rm); +} + +struct rolling_max *rolling_max_new(int size) { + auto rm = ccalloc(1, struct rolling_max); + if (!rm) { + return NULL; + } + + rm->p = ccalloc(size, int); + rm->elem = ccalloc(size, int); + rm->window_size = size; + if (!rm->p || !rm->elem) { + goto err; + } + + return rm; + +err: + rolling_max_destroy(rm); + return NULL; +} + +void rolling_max_reset(struct rolling_max *rm) { + rm->p_head = 0; + rm->np = 0; + rm->nelem = 0; + rm->elem_head = 0; +} + +void rolling_max_push(struct rolling_max *rm, int val) { +#define IDX(n) ((n) % rm->window_size) + if (rm->nelem == rm->window_size) { + auto old_head = rm->elem_head; + // Discard the oldest element. + // rm->elem.pop_front(); + rm->nelem--; + rm->elem_head = IDX(rm->elem_head + 1); + + // Remove discarded element from the priority queue too. + assert(rm->np); + if (rm->p[rm->p_head] == old_head) { + // rm->p.pop_front() + rm->p_head = IDX(rm->p_head + 1); + rm->np--; + } + } + + // Add the new element to the queue. + // rm->elem.push_back(val) + rm->elem[IDX(rm->elem_head + rm->nelem)] = val; + rm->nelem++; + + // Update the prority queue. + // Remove all elements smaller than the new element from the queue. Because + // the new element will become the maximum element before them, and since they + // come b1efore the new element, they will have been popped before the new + // element, so they will never become the maximum element. + while (rm->np) { + int p_tail = IDX(rm->p_head + rm->np - 1); + if (rm->elem[rm->p[p_tail]] > val) { + break; + } + // rm->p.pop_back() + rm->np--; + } + // Add the new element to the end of the queue. + // rm->p.push_back(rm->start_index + rm->nelem - 1) + rm->p[IDX(rm->p_head + rm->np)] = IDX(rm->elem_head + rm->nelem - 1); + rm->np++; +#undef IDX +} + +int rolling_max_get_max(struct rolling_max *rm) { + if (rm->np == 0) { + return INT_MIN; + } + return rm->elem[rm->p[rm->p_head]]; +} + +TEST_CASE(rolling_max_test) { +#define NELEM 15 + auto rm = rolling_max_new(3); + const int data[NELEM] = {1, 2, 3, 1, 4, 5, 2, 3, 6, 5, 4, 3, 2, 0, 0}; + const int expected_max[NELEM] = {1, 2, 3, 3, 4, 5, 5, 5, 6, 6, 6, 5, 4, 3, 2}; + int max[NELEM] = {0}; + for (int i = 0; i < NELEM; i++) { + rolling_max_push(rm, data[i]); + max[i] = rolling_max_get_max(rm); + } + TEST_TRUE(memcmp(max, expected_max, sizeof(max)) == 0); +#undef NELEM +} + // vim: set noet sw=8 ts=8 : diff --git a/src/utils.h b/src/utils.h index a632107..efcf05d 100644 --- a/src/utils.h +++ b/src/utils.h @@ -303,6 +303,14 @@ static inline void free_charpp(char **str) { /// int next_power_of_two(int n); +struct rolling_max; + +struct rolling_max *rolling_max_new(int window_size); +void rolling_max_free(struct rolling_max *rm); +void rolling_max_reset(struct rolling_max *rm); +void rolling_max_push(struct rolling_max *rm, int val); +int rolling_max_get_max(struct rolling_max *rm); + // Some versions of the Android libc do not have timespec_get(), use // clock_gettime() instead. #ifdef __ANDROID__ From bf0832cd4c082aa3105b4338feb072ed2be814ab Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 13 Dec 2022 08:39:11 +0000 Subject: [PATCH 32/61] utils: add rolling_avg Signed-off-by: Yuxuan Shui --- src/utils.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/utils.h | 7 +++++ 2 files changed, 86 insertions(+) diff --git a/src/utils.c b/src/utils.c index 8190226..c080e53 100644 --- a/src/utils.c +++ b/src/utils.c @@ -164,4 +164,83 @@ TEST_CASE(rolling_max_test) { #undef NELEM } +/// A rolling average of a stream of integers. +struct rolling_avg { + /// The sum of the elements in the window. + int64_t sum; + + /// The elements in the window. + int *elem; + int head, nelem; + + int window_size; +}; + +struct rolling_avg *rolling_avg_new(int size) { + auto rm = ccalloc(1, struct rolling_avg); + if (!rm) { + return NULL; + } + + rm->elem = ccalloc(size, int); + rm->window_size = size; + if (!rm->elem) { + free(rm); + return NULL; + } + + return rm; +} + +void rolling_avg_destroy(struct rolling_avg *rm) { + free(rm->elem); + free(rm); +} + +void rolling_avg_reset(struct rolling_avg *ra) { + ra->sum = 0; + ra->nelem = 0; + ra->head = 0; +} + +void rolling_avg_push(struct rolling_avg *ra, int val) { + if (ra->nelem == ra->window_size) { + // Discard the oldest element. + // rm->elem.pop_front(); + ra->sum -= ra->elem[ra->head % ra->window_size]; + ra->nelem--; + ra->head = (ra->head + 1) % ra->window_size; + } + + // Add the new element to the queue. + // rm->elem.push_back(val) + ra->elem[(ra->head + ra->nelem) % ra->window_size] = val; + ra->sum += val; + ra->nelem++; +} + +double rolling_avg_get_avg(struct rolling_avg *ra) { + if (ra->nelem == 0) { + return 0; + } + return (double)ra->sum / (double)ra->nelem; +} + +TEST_CASE(rolling_avg_test) { +#define NELEM 15 + auto rm = rolling_avg_new(3); + const int data[NELEM] = {1, 2, 3, 1, 4, 5, 2, 3, 6, 5, 4, 3, 2, 0, 0}; + const double expected_avg[NELEM] = { + 1, 1.5, 2, 2, 8.0 / 3.0, 10.0 / 3.0, 11.0 / 3.0, 10.0 / 3.0, + 11.0 / 3.0, 14.0 / 3.0, 5, 4, 3, 5.0 / 3.0, 2.0 / 3.0}; + double avg[NELEM] = {0}; + for (int i = 0; i < NELEM; i++) { + rolling_avg_push(rm, data[i]); + avg[i] = rolling_avg_get_avg(rm); + } + for (int i = 0; i < NELEM; i++) { + TEST_EQUAL(avg[i], expected_avg[i]); + } +} + // vim: set noet sw=8 ts=8 : diff --git a/src/utils.h b/src/utils.h index efcf05d..271d882 100644 --- a/src/utils.h +++ b/src/utils.h @@ -311,6 +311,13 @@ void rolling_max_reset(struct rolling_max *rm); void rolling_max_push(struct rolling_max *rm, int val); int rolling_max_get_max(struct rolling_max *rm); +struct rolling_avg; +struct rolling_avg *rolling_avg_new(int window_size); +void rolling_avg_free(struct rolling_avg *ra); +void rolling_avg_reset(struct rolling_avg *ra); +void rolling_avg_push(struct rolling_avg *ra, int val); +double rolling_avg_get_avg(struct rolling_avg *ra); + // Some versions of the Android libc do not have timespec_get(), use // clock_gettime() instead. #ifdef __ANDROID__ From 43d8d3ed5dcffb6a031b3a54cd6bf57a251ce426 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 14 Dec 2022 07:36:07 +0000 Subject: [PATCH 33/61] backend: add a comment Signed-off-by: Yuxuan Shui --- src/backend/backend.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backend/backend.c b/src/backend/backend.c index 6cfe7fb..f5b5479 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -197,6 +197,8 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { // region will be because of blur, we assume the worst case here. // That is, the damaged window is at the bottom of the stack, and // all other windows have semi-transparent background + // + // TODO(yshui): maybe we don't need to resize reg_damage, only reg_paint? int resize_factor = 1; if (t) { resize_factor = t->stacking_rank; From e9ff18e1bdb640b889de6929ea2bfe4a515def9b Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 14 Dec 2022 07:37:27 +0000 Subject: [PATCH 34/61] x: add x_{create,destroy}_region Signed-off-by: Yuxuan Shui --- src/x.c | 32 ++++++++++++++++++++++++++++++++ src/x.h | 6 ++++++ 2 files changed, 38 insertions(+) diff --git a/src/x.c b/src/x.c index 795211d..7faa1c9 100644 --- a/src/x.c +++ b/src/x.c @@ -388,6 +388,38 @@ bool x_fetch_region(xcb_connection_t *c, xcb_xfixes_region_t r, pixman_region32_ return ret; } +uint32_t x_create_region(xcb_connection_t *c, const region_t *reg) { + if (!reg) { + return XCB_NONE; + } + + int nrects; + auto rects = pixman_region32_rectangles(reg, &nrects); + auto xrects = ccalloc(nrects, xcb_rectangle_t); + for (int i = 0; i < nrects; i++) { + xrects[i] = + (xcb_rectangle_t){.x = to_i16_checked(rects[i].x1), + .y = to_i16_checked(rects[i].y1), + .width = to_u16_checked(rects[i].x2 - rects[i].x1), + .height = to_u16_checked(rects[i].y2 - rects[i].y1)}; + } + + xcb_xfixes_region_t ret = x_new_id(c); + bool success = + XCB_AWAIT_VOID(xcb_xfixes_create_region, c, ret, to_u32_checked(nrects), xrects); + free(xrects); + if (!success) { + return XCB_NONE; + } + return ret; +} + +void x_destroy_region(xcb_connection_t *c, xcb_xfixes_region_t r) { + if (r != XCB_NONE) { + xcb_xfixes_destroy_region(c, r); + } +} + void x_set_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict, int16_t clip_x_origin, int16_t clip_y_origin, const region_t *reg) { diff --git a/src/x.h b/src/x.h index 3b8787c..a192886 100644 --- a/src/x.h +++ b/src/x.h @@ -216,6 +216,12 @@ x_create_picture_with_visual(xcb_connection_t *, xcb_drawable_t, int w, int h, /// Fetch a X region and store it in a pixman region bool x_fetch_region(xcb_connection_t *, xcb_xfixes_region_t r, region_t *res); +/// Create a X region from a pixman region +uint32_t x_create_region(xcb_connection_t *c, const region_t *reg); + +/// Destroy a X region +void x_destroy_region(xcb_connection_t *c, uint32_t region); + void x_set_picture_clip_region(xcb_connection_t *, xcb_render_picture_t, int16_t clip_x_origin, int16_t clip_y_origin, const region_t *); From 227cb55ca56a9459d049a7215584db940baa8b10 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 14 Dec 2022 07:40:42 +0000 Subject: [PATCH 35/61] backend: xrender: set update region for PresentPixmap request Signed-off-by: Yuxuan Shui --- src/backend/xrender/xrender.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index e85a85f..19c2ebf 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -606,13 +606,16 @@ static void present(backend_t *base, const region_t *region) { XCB_NONE, xd->back[xd->curr_back], orig_x, orig_y, 0, 0, orig_x, orig_y, region_width, region_height); + auto xregion = x_create_region(base->c, region); + // Make sure we got reply from PresentPixmap before waiting for events, // to avoid deadlock auto e = xcb_request_check( base->c, xcb_present_pixmap_checked( xd->base.c, xd->target_win, - xd->back_pixmap[xd->curr_back], 0, XCB_NONE, XCB_NONE, 0, + xd->back_pixmap[xd->curr_back], 0, XCB_NONE, xregion, 0, 0, XCB_NONE, XCB_NONE, XCB_NONE, 0, 0, 0, 0, 0, NULL)); + x_destroy_region(base->c, xregion); if (e) { log_error("Failed to present pixmap"); free(e); From eec8bf79d2ec6b0911716be2d4d465d732160b0b Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 14 Dec 2022 12:35:37 +0000 Subject: [PATCH 36/61] core: print error with full_sequence ev->sequence was just the lower 16 bits of the sequence number. Signed-off-by: Yuxuan Shui --- src/picom.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/picom.c b/src/picom.c index 39837d8..91be70f 100644 --- a/src/picom.c +++ b/src/picom.c @@ -297,7 +297,7 @@ void discard_ignore(session_t *ps, unsigned long sequence) { } } -static int should_ignore(session_t *ps, unsigned long sequence) { +static int should_ignore(session_t *ps, uint32_t sequence) { if (ps == NULL) { // Do not ignore errors until the session has been initialized return false; @@ -1136,7 +1136,7 @@ void root_damaged(session_t *ps) { * Xlib error handler function. */ static int xerror(Display attr_unused *dpy, XErrorEvent *ev) { - if (!should_ignore(ps_g, ev->serial)) { + if (!should_ignore(ps_g, (uint32_t)ev->serial)) { x_print_error(ev->serial, ev->request_code, ev->minor_code, ev->error_code); } return 0; @@ -1146,8 +1146,9 @@ static int xerror(Display attr_unused *dpy, XErrorEvent *ev) { * XCB error handler function. */ void ev_xcb_error(session_t *ps, xcb_generic_error_t *err) { - if (!should_ignore(ps, err->sequence)) { - x_print_error(err->sequence, err->major_code, err->minor_code, err->error_code); + if (!should_ignore(ps, err->full_sequence)) { + x_print_error(err->full_sequence, err->major_code, err->minor_code, + err->error_code); } } From c51020aef7920dc69355345add3d7aada9b821e2 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 14 Dec 2022 13:35:11 +0000 Subject: [PATCH 37/61] event: restore event sequence number after passing it to Xlib handlers We set event sequence number to the last sequence xlib knows about to silence its complaint about missing sequence numbers, but we forgot to restore it back afterwards. This used to break error ignoring mechanism in `should_ignore`. In the last commit we updated it to use full_sequence which incidently fixed this problem. But let's restore the sequence number anyway for good measure. Signed-off-by: Yuxuan Shui --- src/event.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/event.c b/src/event.c index f84b595..af071a5 100644 --- a/src/event.c +++ b/src/event.c @@ -121,11 +121,13 @@ static inline const char *ev_name(session_t *ps, xcb_generic_event_t *ev) { CASESTRRET(ClientMessage); } - if (ps->damage_event + XCB_DAMAGE_NOTIFY == ev->response_type) + if (ps->damage_event + XCB_DAMAGE_NOTIFY == ev->response_type) { return "Damage"; + } - if (ps->shape_exists && ev->response_type == ps->shape_event) + if (ps->shape_exists && ev->response_type == ps->shape_event) { return "ShapeNotify"; + } if (ps->xsync_exists) { int o = ev->response_type - ps->xsync_event; @@ -704,8 +706,11 @@ void ev_handle(session_t *ps, xcb_generic_event_t *ev) { // missing sequence numbers. // // We only need the low 16 bits + uint16_t seq = ev->sequence; ev->sequence = (uint16_t)(LastKnownRequestProcessed(ps->dpy) & 0xffff); proc(ps->dpy, &dummy, (xEvent *)ev); + // Restore the sequence number + ev->sequence = seq; } // XXX redraw needs to be more fine grained From 57956fb2194597c4104b8fd19cb9402868f050d9 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 14 Dec 2022 13:47:38 +0000 Subject: [PATCH 38/61] general: fix compiler warning about unused results Signed-off-by: Yuxuan Shui --- src/dbus.c | 5 ++++- src/log.c | 2 +- src/picom.c | 6 +++++- src/utils.c | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/dbus.c b/src/dbus.c index 8b17b30..2b17341 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -1435,7 +1435,10 @@ static bool cdbus_process_windows_root_introspect(session_t *ps, DBusMessage *ms continue; } char *tmp = NULL; - asprintf(&tmp, "\n", w->id); + if (asprintf(&tmp, "\n", w->id) < 0) { + log_fatal("Failed to allocate memory."); + abort(); + } mstrextend(&ret, tmp); free(tmp); } diff --git a/src/log.c b/src/log.c index 0b663e7..8a494b9 100644 --- a/src/log.c +++ b/src/log.c @@ -256,7 +256,7 @@ static void file_logger_write(struct log_target *tgt, const char *str, size_t le static void file_logger_writev(struct log_target *tgt, const struct iovec *vec, int vcnt) { auto f = (struct file_logger *)tgt; fflush(f->f); - writev(fileno(f->f), vec, vcnt); + ssize_t _ attr_unused = writev(fileno(f->f), vec, vcnt); } static void file_logger_destroy(struct log_target *tgt) { diff --git a/src/picom.c b/src/picom.c index 91be70f..0c12f69 100644 --- a/src/picom.c +++ b/src/picom.c @@ -2760,7 +2760,11 @@ int main(int argc, char **argv) { // Notify the parent that we are done. This might cause the parent // to quit, so only do this after setsid() int tmp = 1; - write(pfds[1], &tmp, sizeof tmp); + if (write(pfds[1], &tmp, sizeof tmp) != sizeof tmp) { + log_fatal("Failed to notify parent process"); + ret_code = 1; + break; + } close(pfds[1]); // We only do this once need_fork = false; diff --git a/src/utils.c b/src/utils.c index c080e53..a1114a0 100644 --- a/src/utils.c +++ b/src/utils.c @@ -27,7 +27,7 @@ void report_allocation_failure(const char *func, const char *file, unsigned int {.iov_base = (void *)msg2, .iov_len = sizeof(msg2) - 1}, }; - writev(STDERR_FILENO, v, ARR_SIZE(v)); + ssize_t _ attr_unused = writev(STDERR_FILENO, v, ARR_SIZE(v)); abort(); unreachable; From fe3f53f3a469bf0fdab47c229c6fbcb155a677a6 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 14 Dec 2022 13:57:09 +0000 Subject: [PATCH 39/61] x: fix CI build failure Signed-off-by: Yuxuan Shui --- src/x.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/x.c b/src/x.c index 7faa1c9..e71f1ee 100644 --- a/src/x.c +++ b/src/x.c @@ -394,7 +394,10 @@ uint32_t x_create_region(xcb_connection_t *c, const region_t *reg) { } int nrects; - auto rects = pixman_region32_rectangles(reg, &nrects); + // In older pixman versions, pixman_region32_rectangles doesn't take const + // region_t, instead of dealing with this version difference, just suppress the + // warning. + const pixman_box32_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects); auto xrects = ccalloc(nrects, xcb_rectangle_t); for (int i = 0; i < nrects; i++) { xrects[i] = From f1bff49b1c330da585b5bee07119e2fd80c3148f Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 14 Dec 2022 14:23:55 +0000 Subject: [PATCH 40/61] core: expand X error handling We used to have a list of X errors we should ignore in case they do occur. This commit expands that functionality to also allow us aborting on certain errors. Signed-off-by: Yuxuan Shui --- src/common.h | 44 +++++++++++++++++++------------- src/event.c | 2 +- src/picom.c | 72 ++++++++++++++++++++++++++++++++-------------------- src/picom.h | 2 +- src/x.c | 10 +++++++- src/x.h | 2 ++ 6 files changed, 85 insertions(+), 47 deletions(-) diff --git a/src/common.h b/src/common.h index cb02da3..21c76b7 100644 --- a/src/common.h +++ b/src/common.h @@ -36,9 +36,9 @@ #include #include #include -#include #include #include +#include #include "uthash_extra.h" #ifdef CONFIG_OPENGL @@ -55,11 +55,11 @@ #include "backend/driver.h" #include "compiler.h" #include "config.h" +#include "list.h" #include "region.h" +#include "render.h" #include "types.h" #include "utils.h" -#include "list.h" -#include "render.h" #include "win_defs.h" #include "x.h" @@ -83,10 +83,17 @@ struct glx_session; struct atom; struct conv; -typedef struct _ignore { - struct _ignore *next; +enum pending_reply_action { + PENDING_REPLY_ACTION_IGNORE, + PENDING_REPLY_ACTION_ABORT, + PENDING_REPLY_ACTION_DEBUG_ABORT, +}; + +typedef struct pending_reply { + struct pending_reply *next; unsigned long sequence; -} ignore_t; + enum pending_reply_action action; +} pending_reply_t; #ifdef CONFIG_OPENGL #ifdef DEBUG_GLX_DEBUG_CONTEXT @@ -262,18 +269,18 @@ typedef struct session { /// Time of last window animation step. In milliseconds. long animation_time; // TODO(dccsillag) turn into `long long`, like fade_time /// Head pointer of the error ignore linked list. - ignore_t *ignore_head; + pending_reply_t *pending_reply_head; /// Pointer to the next member of tail element of the error /// ignore linked list. - ignore_t **ignore_tail; + pending_reply_t **pending_reply_tail; // Cached blur convolution kernels. struct x_convolution_kernel **blur_kerns_cache; /// If we should quit - bool quit:1; + bool quit : 1; // TODO(yshui) use separate flags for dfferent kinds of updates so we don't // waste our time. /// Whether there are pending updates, like window creation, etc. - bool pending_updates:1; + bool pending_updates : 1; // === Expose event related === /// Pointer to an array of XRectangle-s of exposed region. @@ -474,18 +481,21 @@ static inline bool bkend_use_glx(session_t *ps) { return BKEND_GLX == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend; } -static void set_ignore(session_t *ps, unsigned long sequence) { - if (ps->o.show_all_xerrors) +static void set_ignore(session_t *ps, uint32_t sequence) { + if (ps->o.show_all_xerrors) { return; + } - auto i = cmalloc(ignore_t); - if (!i) - return; + auto i = cmalloc(pending_reply_t); + if (!i) { + abort(); + } i->sequence = sequence; i->next = 0; - *ps->ignore_tail = i; - ps->ignore_tail = &i->next; + i->action = PENDING_REPLY_ACTION_IGNORE; + *ps->pending_reply_tail = i; + ps->pending_reply_tail = &i->next; } /** diff --git a/src/event.c b/src/event.c index af071a5..12d2477 100644 --- a/src/event.c +++ b/src/event.c @@ -674,7 +674,7 @@ ev_selection_clear(session_t *ps, xcb_selection_clear_event_t attr_unused *ev) { void ev_handle(session_t *ps, xcb_generic_event_t *ev) { if ((ev->response_type & 0x7f) != KeymapNotify) { - discard_ignore(ps, ev->full_sequence); + discard_pending(ps, ev->full_sequence); } xcb_window_t wid = ev_window(ps, ev); diff --git a/src/picom.c b/src/picom.c index 0c12f69..c7cc504 100644 --- a/src/picom.c +++ b/src/picom.c @@ -282,14 +282,14 @@ static bool run_fade(session_t *ps, struct managed_win **_w, long long steps) { // === Error handling === -void discard_ignore(session_t *ps, unsigned long sequence) { - while (ps->ignore_head) { - if (sequence > ps->ignore_head->sequence) { - ignore_t *next = ps->ignore_head->next; - free(ps->ignore_head); - ps->ignore_head = next; - if (!ps->ignore_head) { - ps->ignore_tail = &ps->ignore_head; +void discard_pending(session_t *ps, uint32_t sequence) { + while (ps->pending_reply_head) { + if (sequence > ps->pending_reply_head->sequence) { + auto next = ps->pending_reply_head->next; + free(ps->pending_reply_head); + ps->pending_reply_head = next; + if (!ps->pending_reply_head) { + ps->pending_reply_tail = &ps->pending_reply_head; } } else { break; @@ -297,13 +297,28 @@ void discard_ignore(session_t *ps, unsigned long sequence) { } } -static int should_ignore(session_t *ps, uint32_t sequence) { +static void handle_error(session_t *ps, xcb_generic_error_t *ev) { if (ps == NULL) { // Do not ignore errors until the session has been initialized - return false; + return; } - discard_ignore(ps, sequence); - return ps->ignore_head && ps->ignore_head->sequence == sequence; + discard_pending(ps, ev->full_sequence); + if (ps->pending_reply_head && ps->pending_reply_head->sequence == ev->full_sequence) { + if (ps->pending_reply_head->action != PENDING_REPLY_ACTION_IGNORE) { + x_log_error(LOG_LEVEL_ERROR, ev->full_sequence, ev->major_code, + ev->minor_code, ev->error_code); + } + switch (ps->pending_reply_head->action) { + case PENDING_REPLY_ACTION_ABORT: + log_fatal("An unrecoverable X error occurred, aborting..."); + abort(); + case PENDING_REPLY_ACTION_DEBUG_ABORT: assert(false); break; + case PENDING_REPLY_ACTION_IGNORE: break; + } + return; + } + x_log_error(LOG_LEVEL_WARN, ev->full_sequence, ev->major_code, ev->minor_code, + ev->error_code); } // === Windows === @@ -1136,9 +1151,13 @@ void root_damaged(session_t *ps) { * Xlib error handler function. */ static int xerror(Display attr_unused *dpy, XErrorEvent *ev) { - if (!should_ignore(ps_g, (uint32_t)ev->serial)) { - x_print_error(ev->serial, ev->request_code, ev->minor_code, ev->error_code); - } + // Fake a xcb error, fill in just enough information + xcb_generic_error_t xcb_err; + xcb_err.full_sequence = (uint32_t)ev->serial; + xcb_err.major_code = ev->request_code; + xcb_err.minor_code = ev->minor_code; + xcb_err.error_code = ev->error_code; + handle_error(ps_g, &xcb_err); return 0; } @@ -1146,10 +1165,7 @@ static int xerror(Display attr_unused *dpy, XErrorEvent *ev) { * XCB error handler function. */ void ev_xcb_error(session_t *ps, xcb_generic_error_t *err) { - if (!should_ignore(ps, err->full_sequence)) { - x_print_error(err->full_sequence, err->major_code, err->minor_code, - err->error_code); - } + handle_error(ps, err); } /** @@ -1928,7 +1944,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy, ps->loop = EV_DEFAULT; pixman_region32_init(&ps->screen_reg); - ps->ignore_tail = &ps->ignore_head; + ps->pending_reply_tail = &ps->pending_reply_head; ps->o.show_all_xerrors = all_xerrors; @@ -2529,26 +2545,28 @@ static void session_destroy(session_t *ps) { // Free ignore linked list { - ignore_t *next = NULL; - for (ignore_t *ign = ps->ignore_head; ign; ign = next) { + pending_reply_t *next = NULL; + for (auto ign = ps->pending_reply_head; ign; ign = next) { next = ign->next; free(ign); } // Reset head and tail - ps->ignore_head = NULL; - ps->ignore_tail = &ps->ignore_head; + ps->pending_reply_head = NULL; + ps->pending_reply_tail = &ps->pending_reply_head; } // Free tgt_{buffer,picture} and root_picture - if (ps->tgt_buffer.pict == ps->tgt_picture) + if (ps->tgt_buffer.pict == ps->tgt_picture) { ps->tgt_buffer.pict = XCB_NONE; + } - if (ps->tgt_picture == ps->root_picture) + if (ps->tgt_picture == ps->root_picture) { ps->tgt_picture = XCB_NONE; - else + } else { free_picture(ps->c, &ps->tgt_picture); + } free_picture(ps->c, &ps->root_picture); free_paint(ps, &ps->tgt_buffer); diff --git a/src/picom.h b/src/picom.h index 7ee289b..b5a1e8a 100644 --- a/src/picom.h +++ b/src/picom.h @@ -46,7 +46,7 @@ void cxinerama_upd_scrs(session_t *ps); void queue_redraw(session_t *ps); -void discard_ignore(session_t *ps, unsigned long sequence); +void discard_pending(session_t *ps, uint32_t sequence); void set_root_flags(session_t *ps, uint64_t flags); diff --git a/src/x.c b/src/x.c index e71f1ee..e598345 100644 --- a/src/x.c +++ b/src/x.c @@ -562,8 +562,16 @@ _x_strerror(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_c /** * Log a X11 error */ +void x_log_error(enum log_level level, unsigned long serial, uint8_t major, + uint16_t minor, uint8_t error_code) { + if (unlikely(level >= log_get_level_tls())) { + log_printf(tls_logger, level, __func__, "%s", + _x_strerror(serial, major, minor, error_code)); + } +} + void x_print_error(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_code) { - log_debug("%s", _x_strerror(serial, major, minor, error_code)); + x_log_error(LOG_LEVEL_DEBUG, serial, major, minor, error_code); } /* diff --git a/src/x.h b/src/x.h index a192886..78efea3 100644 --- a/src/x.h +++ b/src/x.h @@ -231,6 +231,8 @@ void x_clear_picture_clip_region(xcb_connection_t *, xcb_render_picture_t pict); * Log a X11 error */ void x_print_error(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_code); +void x_log_error(enum log_level level, unsigned long serial, uint8_t major, + uint16_t minor, uint8_t error_code); /* * Convert a xcb_generic_error_t to a string that describes the error From 6906e6694aa77fe7b92127e59be9d2132154bd08 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 14 Dec 2022 14:28:42 +0000 Subject: [PATCH 41/61] core: add set_cant_fail_cookie Enables picom to abort when certain requests fail. Signed-off-by: Yuxuan Shui --- src/common.h | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/common.h b/src/common.h index 21c76b7..f1e5d8a 100644 --- a/src/common.h +++ b/src/common.h @@ -481,11 +481,8 @@ static inline bool bkend_use_glx(session_t *ps) { return BKEND_GLX == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend; } -static void set_ignore(session_t *ps, uint32_t sequence) { - if (ps->o.show_all_xerrors) { - return; - } - +static void +set_reply_action(session_t *ps, uint32_t sequence, enum pending_reply_action action) { auto i = cmalloc(pending_reply_t); if (!i) { abort(); @@ -493,7 +490,7 @@ static void set_ignore(session_t *ps, uint32_t sequence) { i->sequence = sequence; i->next = 0; - i->action = PENDING_REPLY_ACTION_IGNORE; + i->action = action; *ps->pending_reply_tail = i; ps->pending_reply_tail = &i->next; } @@ -502,7 +499,15 @@ static void set_ignore(session_t *ps, uint32_t sequence) { * Ignore X errors caused by given X request. */ static inline void set_ignore_cookie(session_t *ps, xcb_void_cookie_t cookie) { - set_ignore(ps, cookie.sequence); + if (ps->o.show_all_xerrors) { + return; + } + + set_reply_action(ps, cookie.sequence, PENDING_REPLY_ACTION_IGNORE); +} + +static inline void set_cant_fail_cookie(session_t *ps, xcb_void_cookie_t cookie) { + set_reply_action(ps, cookie.sequence, PENDING_REPLY_ACTION_ABORT); } /** From 931c1b8bf62d351b37345c45641854ca95734257 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sun, 18 Dec 2022 19:23:06 +0000 Subject: [PATCH 42/61] x: fix missing _checked Signed-off-by: Yuxuan Shui --- src/x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/x.c b/src/x.c index e598345..42bee3f 100644 --- a/src/x.c +++ b/src/x.c @@ -449,9 +449,10 @@ void x_set_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict, } void x_clear_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict) { + assert(pict != XCB_NONE); xcb_render_change_picture_value_list_t v = {.clipmask = XCB_NONE}; xcb_generic_error_t *e = xcb_request_check( - c, xcb_render_change_picture(c, pict, XCB_RENDER_CP_CLIP_MASK, &v)); + c, xcb_render_change_picture_checked(c, pict, XCB_RENDER_CP_CLIP_MASK, &v)); if (e) { log_error_x_error(e, "failed to clear clip region"); free(e); From 0278a1fb0ba17d8b86f4b9199c5ed0a43b12f14f Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sun, 18 Dec 2022 19:23:49 +0000 Subject: [PATCH 43/61] backend: xrender: fix using of invalid picture when vsync is disabled Fixes #974 Signed-off-by: Yuxuan Shui --- src/backend/xrender/xrender.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index 19c2ebf..4381977 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -594,13 +594,13 @@ static void present(backend_t *base, const region_t *region) { uint16_t region_width = to_u16_checked(extent->x2 - extent->x1), region_height = to_u16_checked(extent->y2 - extent->y1); - // compose() sets clip region on the back buffer, so clear it first - x_clear_picture_clip_region(base->c, xd->back[xd->curr_back]); - // limit the region of update x_set_picture_clip_region(base->c, xd->back[2], 0, 0, region); if (xd->vsync) { + // compose() sets clip region on the back buffer, so clear it first + x_clear_picture_clip_region(base->c, xd->back[xd->curr_back]); + // Update the back buffer first, then present xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, xd->back[2], XCB_NONE, xd->back[xd->curr_back], orig_x, orig_y, 0, From ba2e24af3e98983b574e7705617bd611b658f94e Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 15 Dec 2022 10:53:31 +0000 Subject: [PATCH 44/61] core: detect screen off Use the DPMS extension to detect if screen is turned off, and unredirect if it is. This also helps working around the problem where OpenGL buffers lose data when screen is turned off, causing screen to flicker later when it turns back on if use-damage is enabled. Unfortunately the DPMS extension doesn't define an event, so we have to periodically poll the screen state. Signed-off-by: Yuxuan Shui --- .github/workflows/codeql-analysis.yml | 2 +- README.md | 3 +- src/common.h | 6 ++++ src/meson.build | 3 +- src/picom.c | 50 +++++++++++++++++++++++++++ 5 files changed, 61 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 8b65b61..224c462 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -36,7 +36,7 @@ jobs: languages: ${{ matrix.language }} # Install dependencies - - run: sudo apt update && sudo apt install libxext-dev libxcb1-dev libxcb-damage0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-render-util0-dev libxcb-render0-dev libxcb-randr0-dev libxcb-composite0-dev libxcb-image0-dev libxcb-present-dev libxcb-xinerama0-dev libxcb-glx0-dev libpixman-1-dev libdbus-1-dev libconfig-dev libgl1-mesa-dev libpcre2-dev libevdev-dev uthash-dev libev-dev libx11-xcb-dev meson ninja-build + - run: sudo apt update && sudo apt install libxext-dev libxcb1-dev libxcb-dpms0-dev libxcb-damage0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-render-util0-dev libxcb-render0-dev libxcb-randr0-dev libxcb-composite0-dev libxcb-image0-dev libxcb-present-dev libxcb-xinerama0-dev libxcb-glx0-dev libpixman-1-dev libdbus-1-dev libconfig-dev libgl1-mesa-dev libpcre2-dev libevdev-dev uthash-dev libev-dev libx11-xcb-dev meson ninja-build if: ${{ matrix.language == 'cpp' }} # Autobuild diff --git a/README.md b/README.md index f6bdfe1..a23dfd0 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Assuming you already have all the usual building tools installed (e.g. gcc, pyth * xproto * xcb * xcb-damage +* xcb-dpms * xcb-xfixes * xcb-shape * xcb-renderutil @@ -44,7 +45,7 @@ Assuming you already have all the usual building tools installed (e.g. gcc, pyth On Debian based distributions (e.g. Ubuntu), the needed packages are ``` -libxext-dev libxcb1-dev libxcb-damage0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-render-util0-dev libxcb-render0-dev libxcb-randr0-dev libxcb-composite0-dev libxcb-image0-dev libxcb-present-dev libxcb-xinerama0-dev libxcb-glx0-dev libpixman-1-dev libdbus-1-dev libconfig-dev libgl-dev libegl-dev libpcre2-dev libevdev-dev uthash-dev libev-dev libx11-xcb-dev meson +libxext-dev libxcb1-dev libxcb-damage0-dev libxcb-dpms0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-render-util0-dev libxcb-render0-dev libxcb-randr0-dev libxcb-composite0-dev libxcb-image0-dev libxcb-present-dev libxcb-xinerama0-dev libxcb-glx0-dev libpixman-1-dev libdbus-1-dev libconfig-dev libgl-dev libegl-dev libpcre2-dev libevdev-dev uthash-dev libev-dev libx11-xcb-dev meson ``` On Fedora, the needed packages are diff --git a/src/common.h b/src/common.h index f1e5d8a..9a74d4f 100644 --- a/src/common.h +++ b/src/common.h @@ -150,6 +150,8 @@ typedef struct session { // === Event handlers === /// ev_io for X connection ev_io xiow; + /// Timer for checking DPMS power level + ev_timer dpms_check_timer; /// Timeout for delayed unredirection. ev_timer unredir_timer; /// Timer for fading @@ -240,6 +242,8 @@ typedef struct session { xcb_sync_fence_t sync_fence; /// Whether we are rendering the first frame after screen is redirected bool first_frame; + /// Whether screen has been turned off + bool screen_is_off; // === Operation related === /// Flags related to the root window @@ -348,6 +352,8 @@ typedef struct session { int composite_error; /// Major opcode for X Composite extension. int composite_opcode; + /// Whether X DPMS extension exists + bool dpms_exists; /// Whether X Shape extension exists. bool shape_exists; /// Event base number for X Shape extension. diff --git a/src/meson.build b/src/meson.build index 09eb07b..b6b24b5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -16,7 +16,8 @@ cflags = [] required_xcb_packages = [ 'xcb-render', 'xcb-damage', 'xcb-randr', 'xcb-sync', 'xcb-composite', - 'xcb-shape', 'xcb-xinerama', 'xcb-xfixes', 'xcb-present', 'xcb-glx', 'xcb' + 'xcb-shape', 'xcb-xinerama', 'xcb-xfixes', 'xcb-present', 'xcb-glx', + 'xcb-dpms', 'xcb' ] required_packages = [ diff --git a/src/picom.c b/src/picom.c index c7cc504..d7e7cda 100644 --- a/src/picom.c +++ b/src/picom.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -169,6 +170,26 @@ void cxinerama_upd_scrs(session_t *ps) { free(xinerama_scrs); } +static inline bool dpms_screen_is_off(xcb_dpms_info_reply_t *info) { + // state is a bool indicating whether dpms is enabled + return info->state && (info->power_level != XCB_DPMS_DPMS_MODE_ON); +} + +void check_dpms_status(EV_P attr_unused, ev_timer *w, int revents attr_unused) { + auto ps = session_ptr(w, dpms_check_timer); + auto r = xcb_dpms_info_reply(ps->c, xcb_dpms_info(ps->c), NULL); + if (!r) { + log_fatal("Failed to query DPMS status."); + abort(); + } + auto now_screen_is_off = dpms_screen_is_off(r); + if (ps->screen_is_off != now_screen_is_off) { + ps->screen_is_off = now_screen_is_off; + queue_redraw(ps); + } + free(r); +} + /** * Find matched window. * @@ -1092,6 +1113,19 @@ paint_preprocess(session_t *ps, bool *fade_running, bool *animation_running) { // If there's no window to paint, and the screen isn't redirected, // don't redirect it. unredir_possible = true; + } else if (ps->screen_is_off) { + // Screen is off, unredirect + // We do this unconditionally disregarding "unredir_if_possible" + // because it's important for correctness, because we need to + // workaround problems X server has around screen off. + // + // Known problems: + // 1. Sometimes OpenGL front buffer can lose content, and if we + // are doing partial updates (i.e. use-damage = true), the + // result will be wrong. + // 2. For frame pacing, X server sends bogus + // PresentCompleteNotify events when screen is off. + unredir_possible = true; } if (unredir_possible) { if (ps->redirected) { @@ -1987,6 +2021,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy, xcb_prefetch_extension_data(ps->c, &xcb_present_id); xcb_prefetch_extension_data(ps->c, &xcb_sync_id); xcb_prefetch_extension_data(ps->c, &xcb_glx_id); + xcb_prefetch_extension_data(ps->c, &xcb_dpms_id); ext_info = xcb_get_extension_data(ps->c, &xcb_render_id); if (!ext_info || !ext_info->present) { @@ -2055,6 +2090,21 @@ static session_t *session_init(int argc, char **argv, Display *dpy, ps->glx_event = ext_info->first_event; } + ext_info = xcb_get_extension_data(ps->c, &xcb_dpms_id); + ps->dpms_exists = ext_info && ext_info->present; + if (ps->dpms_exists) { + auto r = xcb_dpms_info_reply(ps->c, xcb_dpms_info(ps->c), NULL); + if (!r) { + log_fatal("Failed to query DPMS info"); + goto err; + } + ps->screen_is_off = dpms_screen_is_off(r); + // Check screen status every half second + ev_timer_init(&ps->dpms_check_timer, check_dpms_status, 0, 0.5); + ev_timer_start(ps->loop, &ps->dpms_check_timer); + free(r); + } + // Parse configuration file win_option_mask_t winopt_mask[NUM_WINTYPES] = {{0}}; bool shadow_enabled = false, fading_enable = false, hasneg = false; From 0b965b2e5a0934eb711c5c62c4b5e27deb2cba35 Mon Sep 17 00:00:00 2001 From: Maxim Solovyov Date: Wed, 21 Dec 2022 23:19:51 +0300 Subject: [PATCH 45/61] x: fix leak in x_create_picture_with_pictfmt_and_pixmap --- src/x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/x.c b/src/x.c index 42bee3f..d00c9c2 100644 --- a/src/x.c +++ b/src/x.c @@ -299,6 +299,7 @@ x_create_picture_with_pictfmt_and_pixmap(xcb_connection_t *c, free(buf); if (e) { log_error_x_error(e, "failed to create picture"); + free(e); return XCB_NONE; } return tmp_picture; From efa3d9c22749bd23fa93ef0efc21185556805dd1 Mon Sep 17 00:00:00 2001 From: Maxim Solovyov Date: Thu, 22 Dec 2022 00:20:43 +0300 Subject: [PATCH 46/61] xrender: fix leak in bind_pixmap --- src/backend/xrender/xrender.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index 4381977..d897819 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -508,6 +508,7 @@ bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool if (!r) { log_error("Invalid pixmap: %#010x", pixmap); x_print_error(e->full_sequence, e->major_code, e->minor_code, e->error_code); + free(e); return NULL; } From 7f533c0b23b6a04b3a8c382413e396cfce694f27 Mon Sep 17 00:00:00 2001 From: Maxim Solovyov Date: Thu, 22 Dec 2022 01:26:51 +0300 Subject: [PATCH 47/61] xrender: fix leak in deinit and check should we actually free something fixes at least #960 --- src/backend/xrender/xrender.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index d897819..f25a921 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -576,9 +576,13 @@ static void deinit(backend_t *backend_data) { xcb_render_free_picture(xd->base.c, xd->alpha_pict[i]); } xcb_render_free_picture(xd->base.c, xd->target); - for (int i = 0; i < 2; i++) { - xcb_render_free_picture(xd->base.c, xd->back[i]); - xcb_free_pixmap(xd->base.c, xd->back_pixmap[i]); + for (int i = 0; i < 3; i++) { + if (xd->back[i] != XCB_NONE) { + xcb_render_free_picture(xd->base.c, xd->back[i]); + } + if (xd->back_pixmap[i] != XCB_NONE) { + xcb_free_pixmap(xd->base.c, xd->back_pixmap[i]); + } } if (xd->present_event) { xcb_unregister_for_special_event(xd->base.c, xd->present_event); From 6c08650f3c842f9a390e3347699e837e076b1963 Mon Sep 17 00:00:00 2001 From: Maxim Solovyov Date: Thu, 22 Dec 2022 20:40:02 +0300 Subject: [PATCH 48/61] xrender: fix leak in release_rounded_corner_cache calling wrong free function did nothing and produced ton of x errors fixes at least #892 --- src/backend/xrender/xrender.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index f25a921..113d55e 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -554,7 +554,7 @@ release_rounded_corner_cache(backend_t *base, struct xrender_rounded_rectangle_c assert(cache->refcount > 0); cache->refcount--; if (cache->refcount == 0) { - xcb_free_pixmap(base->c, cache->p); + xcb_render_free_picture(base->c, cache->p); free(cache); } } From a667886959ffdc8c7d20969975a549bc727f26d2 Mon Sep 17 00:00:00 2001 From: Maxim Solovyov Date: Fri, 23 Dec 2022 15:21:52 +0300 Subject: [PATCH 49/61] xrender: make corner-radius respect inactive-dim for the new xrender backend it's enough to clip with a rounded rectangle after dimming, not before partially fixes #867 --- src/backend/xrender/xrender.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index 113d55e..686e6de 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -265,13 +265,6 @@ compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg, coord_t dst, xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_SRC, inner->pict, XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph); - if (img->corner_radius != 0 && xrimg->rounded_rectangle != NULL) { - // Clip tmp_pict with a rounded rectangle - xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_IN_REVERSE, - xrimg->rounded_rectangle->p, XCB_NONE, - tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph); - } - if (img->color_inverted) { if (inner->has_alpha) { auto tmp_pict2 = x_create_picture_with_visual( @@ -294,6 +287,7 @@ compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg, coord_t dst, 0, 0, 0, 0, 0, 0, tmpw, tmph); } } + if (img->dim != 0) { // Dim the actually content of window xcb_rectangle_t rect = { @@ -307,6 +301,13 @@ compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg, coord_t dst, tmp_pict, dim_color, 1, &rect); } + if (img->corner_radius != 0 && xrimg->rounded_rectangle != NULL) { + // Clip tmp_pict with a rounded rectangle + xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_IN_REVERSE, + xrimg->rounded_rectangle->p, XCB_NONE, + tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph); + } + xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_OVER, tmp_pict, mask_pict, result, 0, 0, mask_dst_x, mask_dst_y, to_i16_checked(dst.x), to_i16_checked(dst.y), tmpew, From 7846b17c54ba19c7d5591eb905a5be93b3b5f4f2 Mon Sep 17 00:00:00 2001 From: Maxim Solovyov Date: Tue, 10 Jan 2023 00:15:49 +0300 Subject: [PATCH 50/61] use _checked functions with xcb_request_check --- src/event.c | 3 ++- src/picom.c | 8 ++++---- src/win.c | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/event.c b/src/event.c index 12d2477..a85d908 100644 --- a/src/event.c +++ b/src/event.c @@ -283,7 +283,8 @@ static inline void ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) { // in redirected state. if (ps->overlay && ev->window == ps->overlay && !ps->redirected) { log_debug("Overlay is mapped while we are not redirected"); - auto e = xcb_request_check(ps->c, xcb_unmap_window(ps->c, ps->overlay)); + auto e = + xcb_request_check(ps->c, xcb_unmap_window_checked(ps->c, ps->overlay)); if (e) { log_error("Failed to unmap the overlay window"); free(e); diff --git a/src/picom.c b/src/picom.c index d7e7cda..5c74703 100644 --- a/src/picom.c +++ b/src/picom.c @@ -1438,7 +1438,7 @@ static bool init_debug_window(session_t *ps) { goto err_out; } - err = xcb_request_check(ps->c, xcb_map_window(ps->c, ps->debug_window)); + err = xcb_request_check(ps->c, xcb_map_window_checked(ps->c, ps->debug_window)); if (err) { goto err_out; } @@ -2257,8 +2257,8 @@ static session_t *session_init(int argc, char **argv, Display *dpy, ps->sync_fence = XCB_NONE; if (ps->xsync_exists) { ps->sync_fence = x_new_id(ps->c); - e = xcb_request_check( - ps->c, xcb_sync_create_fence(ps->c, ps->root, ps->sync_fence, 0)); + e = xcb_request_check(ps->c, xcb_sync_create_fence_checked( + ps->c, ps->root, ps->sync_fence, 0)); if (e) { if (ps->o.xrender_sync_fence) { log_error_x_error(e, "Failed to create a XSync fence. " @@ -2469,7 +2469,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy, xcb_query_tree_reply_t *query_tree_reply = xcb_query_tree_reply(ps->c, xcb_query_tree(ps->c, ps->root), NULL); - e = xcb_request_check(ps->c, xcb_ungrab_server(ps->c)); + e = xcb_request_check(ps->c, xcb_ungrab_server_checked(ps->c)); if (e) { log_fatal_x_error(e, "Failed to ungrab server"); free(e); diff --git a/src/win.c b/src/win.c index 26c0200..ddf0835 100644 --- a/src/win.c +++ b/src/win.c @@ -1565,7 +1565,7 @@ void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client) } auto e = xcb_request_check( - ps->c, xcb_change_window_attributes( + ps->c, xcb_change_window_attributes_checked( ps->c, client, XCB_CW_EVENT_MASK, (const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_CLIENT)})); if (e) { From 349f2f37dba7b8683af66d61d409c389933948ff Mon Sep 17 00:00:00 2001 From: Maxim Solovyov Date: Mon, 9 Jan 2023 04:20:53 +0300 Subject: [PATCH 51/61] backend: egl: simplify getting a framebuffer configuration we're using the first available framebuffer configuration that meets our needs so there is no need to ask for more than one --- src/backend/gl/egl.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/backend/gl/egl.c b/src/backend/gl/egl.c index e6d4d90..d619456 100644 --- a/src/backend/gl/egl.c +++ b/src/backend/gl/egl.c @@ -166,14 +166,9 @@ static backend_t *egl_init(session_t *ps) { goto end; } - int ncfgs = 0; - if (eglGetConfigs(gd->display, NULL, 0, &ncfgs) != EGL_TRUE) { - log_error("Failed to get EGL configs."); - goto end; - } - auto visual_info = x_get_visual_info(ps->c, ps->vis); - EGLConfig *cfgs = ccalloc(ncfgs, EGLConfig); + EGLConfig config = NULL; + int nconfigs = 1; // clang-format off if (eglChooseConfig(gd->display, (EGLint[]){ @@ -186,17 +181,14 @@ static backend_t *egl_init(session_t *ps) { EGL_STENCIL_SIZE, 1, EGL_CONFIG_CAVEAT, EGL_NONE, EGL_NONE, - }, cfgs, ncfgs, &ncfgs) != EGL_TRUE) { + }, &config, nconfigs, &nconfigs) != EGL_TRUE) { log_error("Failed to choose EGL config for the root window."); goto end; } // clang-format on - EGLConfig target_cfg = cfgs[0]; - free(cfgs); - gd->target_win = eglCreatePlatformWindowSurfaceProc( - gd->display, target_cfg, (xcb_window_t[]){session_get_target_window(ps)}, NULL); + gd->display, config, (xcb_window_t[]){session_get_target_window(ps)}, NULL); if (gd->target_win == EGL_NO_SURFACE) { log_error("Failed to create EGL surface."); goto end; @@ -207,7 +199,7 @@ static backend_t *egl_init(session_t *ps) { goto end; } - gd->ctx = eglCreateContext(gd->display, target_cfg, NULL, NULL); + gd->ctx = eglCreateContext(gd->display, config, NULL, NULL); if (gd->ctx == EGL_NO_CONTEXT) { log_error("Failed to get GLX context."); goto end; From 7e975cdc5dcfe1f4fda0d42a7eadc02900400d46 Mon Sep 17 00:00:00 2001 From: Maxim Solovyov Date: Fri, 30 Dec 2022 06:16:46 +0300 Subject: [PATCH 52/61] picom: free root image properly fixes #982 --- src/picom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/picom.c b/src/picom.c index 5c74703..eb29504 100644 --- a/src/picom.c +++ b/src/picom.c @@ -1162,6 +1162,7 @@ void root_damaged(session_t *ps) { if (ps->backend_data) { if (ps->root_image) { ps->backend_data->ops->release_image(ps->backend_data, ps->root_image); + ps->root_image = NULL; } auto pixmap = x_get_root_back_pixmap(ps->c, ps->root, ps->atoms); if (pixmap != XCB_NONE) { From ab766b6386fdd643f856bff7f5faf72639c22f3f Mon Sep 17 00:00:00 2001 From: Maxim Solovyov Date: Tue, 10 Jan 2023 01:13:08 +0300 Subject: [PATCH 53/61] picom: fix xcb_request_check memory leaks --- src/picom.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/picom.c b/src/picom.c index eb29504..5bf610c 100644 --- a/src/picom.c +++ b/src/picom.c @@ -1641,6 +1641,7 @@ static void handle_pending_updates(EV_P_ struct session *ps) { auto e = xcb_request_check(ps->c, xcb_grab_server_checked(ps->c)); if (e) { log_fatal_x_error(e, "failed to grab x server"); + free(e); return quit(ps); } @@ -1676,6 +1677,7 @@ static void handle_pending_updates(EV_P_ struct session *ps) { e = xcb_request_check(ps->c, xcb_ungrab_server_checked(ps->c)); if (e) { log_fatal_x_error(e, "failed to ungrab x server"); + free(e); return quit(ps); } @@ -2010,6 +2012,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy, XCB_EVENT_MASK_PROPERTY_CHANGE})); if (e) { log_error_x_error(e, "Failed to setup root window event mask"); + free(e); } xcb_prefetch_extension_data(ps->c, &xcb_render_id); From 4be96a92c783d9d937b514f7bd74890084fdb5c0 Mon Sep 17 00:00:00 2001 From: Maxim Solovyov Date: Fri, 30 Dec 2022 07:38:28 +0300 Subject: [PATCH 54/61] backend: egl: fix creating eglpixmap according to the specification of the EGL_KHR_image_pixmap extension, if target is EGL_NATIVE_PIXMAP_KHR then ctx must be EGL_NO_CONTEXT, otherwise, the EGL_BAD_PARAMETER error is generated. source: https://registry.khronos.org/EGL/extensions/KHR/EGL_KHR_image_pixmap.txt fixes #981 --- src/backend/gl/egl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/backend/gl/egl.c b/src/backend/gl/egl.c index d619456..dd1d403 100644 --- a/src/backend/gl/egl.c +++ b/src/backend/gl/egl.c @@ -277,8 +277,9 @@ egl_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b eglpixmap = cmalloc(struct egl_pixmap); eglpixmap->pixmap = pixmap; - eglpixmap->image = eglCreateImageProc(gd->display, gd->ctx, EGL_NATIVE_PIXMAP_KHR, - (EGLClientBuffer)(uintptr_t)pixmap, NULL); + eglpixmap->image = + eglCreateImageProc(gd->display, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, + (EGLClientBuffer)(uintptr_t)pixmap, NULL); eglpixmap->owned = owned; if (eglpixmap->image == EGL_NO_IMAGE) { From d3c467f4c0d139280894ba70e83e264bddf7220a Mon Sep 17 00:00:00 2001 From: Maxim Solovyov Date: Wed, 11 Jan 2023 05:23:02 +0300 Subject: [PATCH 55/61] opengl: fix segfault in ensure_glx_context --- src/opengl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/opengl.h b/src/opengl.h index dcd8697..d0d37dd 100644 --- a/src/opengl.h +++ b/src/opengl.h @@ -159,7 +159,7 @@ static inline bool ensure_glx_context(session_t *ps) { if (!glx_has_context(ps)) glx_init(ps, false); - return ps->psglx->context; + return glx_has_context(ps); } /** From 92a32808ae0c94e29ca918ec14cd3cbe5c3f6fb0 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 17 Jan 2023 02:56:28 +0000 Subject: [PATCH 56/61] doc: list cases that trigger unredirect unredir-if-possible doesn't just happen for fullscreen windows, be more accurate. Signed-off-by: Yuxuan Shui --- man/picom.1.asciidoc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/man/picom.1.asciidoc b/man/picom.1.asciidoc index b4ff69b..fdf0198 100644 --- a/man/picom.1.asciidoc +++ b/man/picom.1.asciidoc @@ -134,7 +134,13 @@ OPTIONS Use EWMH '_NET_ACTIVE_WINDOW' to determine currently focused window, rather than listening to 'FocusIn'/'FocusOut' event. Might have more accuracy, provided that the WM supports it. *--unredir-if-possible*:: - Unredirect all windows if a full-screen opaque window is detected, to maximize performance for full-screen windows. Known to cause flickering when redirecting/unredirecting windows. + Unredirect all windows in some cases. Known to cause flickering when redirecting/unredirecting windows. Currently, unredirecting is triggered by following conditions: + * If the top level window is taking up the entire screen. In multi-monitor setup, this means ALL monitors. + * If there is no window. + * If a window is fullscreen according to its WM hints. (can be disabled with *--no-ewmh-fullscreen*). + * If a window requests to bypass the compositor ('_NET_WM_BYPASS_COMPOSITOR'). + Windows are also unredirected unconditionally when monitors are powered off, regardless if *--unredir-if-possible* is set. + *--unredir-if-possible-delay* 'MILLISECONDS':: Delay before unredirecting the window, in milliseconds. Defaults to 0. From 3b1930d2c65dc1d62823171eb27f5f8ab90c41b2 Mon Sep 17 00:00:00 2001 From: Arda Atci Date: Mon, 23 Jan 2023 02:29:30 +0300 Subject: [PATCH 57/61] fixed open window type anim, closes #12 --- src/config.c | 24 ++--- src/config_libconfig.c | 2 +- src/win.c | 234 ++++++++++++++++++++--------------------- 3 files changed, 126 insertions(+), 134 deletions(-) diff --git a/src/config.c b/src/config.c index e58cfbd..495d4e8 100644 --- a/src/config.c +++ b/src/config.c @@ -736,19 +736,19 @@ enum open_window_animation parse_open_window_animation(const char *src) { return OPEN_WINDOW_ANIMATION_SLIDE_RIGHT; } else if (strcmp(src, "slide-out") == 0) { return OPEN_WINDOW_ANIMATION_SLIDE_OUT; - } else if (strcmp(src, "slide-in") == 0) { + } else if (strcmp(src, "slide-in") == 0) { return OPEN_WINDOW_ANIMATION_SLIDE_IN; - } else if (strcmp(src, "slide-out-center") == 0) { - return OPEN_WINDOW_ANIMATION_SLIDE_OUT_CENTER; - } else if (strcmp(src, "slide-in-center") == 0) { - return OPEN_WINDOW_ANIMATION_SLIDE_IN_CENTER; - } else if (strcmp(src, "minimize") == 0 || strcmp(src, "maximize") == 0) { - return OPEN_WINDOW_ANIMATION_MINIMIZE; - } else if (strcmp(src, "squeeze") == 0) { - return OPEN_WINDOW_ANIMATION_SQUEEZE; - } else if (strcmp(src, "squeeze-bottom") == 0) { - return OPEN_WINDOW_ANIMATION_SQUEEZE_BOTTOM; - } + } else if (strcmp(src, "slide-out-center") == 0) { + return OPEN_WINDOW_ANIMATION_SLIDE_OUT_CENTER; + } else if (strcmp(src, "slide-in-center") == 0) { + return OPEN_WINDOW_ANIMATION_SLIDE_IN_CENTER; + } else if (strcmp(src, "minimize") == 0 || strcmp(src, "maximize") == 0) { + return OPEN_WINDOW_ANIMATION_MINIMIZE; + } else if (strcmp(src, "squeeze") == 0) { + return OPEN_WINDOW_ANIMATION_SQUEEZE; + } else if (strcmp(src, "squeeze-bottom") == 0) { + return OPEN_WINDOW_ANIMATION_SQUEEZE_BOTTOM; + } return OPEN_WINDOW_ANIMATION_INVALID; } diff --git a/src/config_libconfig.c b/src/config_libconfig.c index fdecbf7..54efab6 100644 --- a/src/config_libconfig.c +++ b/src/config_libconfig.c @@ -233,7 +233,7 @@ static inline void parse_wintype_config(const config_t *cfg, const char *member_ animation = OPEN_WINDOW_ANIMATION_NONE; o->animation = animation; - mask->animation = OPEN_WINDOW_ANIMATION_INVALID; + mask->animation = animation; } double fval; diff --git a/src/win.c b/src/win.c index ddf0835..e49faef 100644 --- a/src/win.c +++ b/src/win.c @@ -471,56 +471,63 @@ static void win_update_properties(session_t *ps, struct managed_win *w) { } static void init_animation(session_t *ps, struct managed_win *w) { - CLEAR_MASK(w->animation_is_tag) - static double *anim_x, *anim_y, *anim_w, *anim_h; - enum open_window_animation animation = ps->o.animation_for_open_window; + CLEAR_MASK(w->animation_is_tag) + static double *anim_x, *anim_y, *anim_w, *anim_h; + enum open_window_animation animation; + if (ps->o.wintype_option[w->window_type].animation != OPEN_WINDOW_ANIMATION_INVALID + && !w->dwm_mask) { + animation = ps->o.wintype_option[w->window_type].animation; + } + else + animation = ps->o.animation_for_open_window; if (w->window_type != WINTYPE_TOOLTIP && wid_has_prop(ps, w->client_win, ps->atoms->aWM_TRANSIENT_FOR)) { animation = ps->o.animation_for_transient_window; } - anim_x = &w->animation_center_x, anim_y = &w->animation_center_y; - anim_w = &w->animation_w, anim_h = &w->animation_h; - if (w->dwm_mask & ANIM_PREV_TAG) { - animation = ps->o.animation_for_prev_tag; + anim_x = &w->animation_center_x, anim_y = &w->animation_center_y; + anim_w = &w->animation_w, anim_h = &w->animation_h; - if (ps->o.enable_fading_prev_tag) { - w->opacity_target_old = fmax(w->opacity_target, w->opacity_target_old); - w->state = WSTATE_FADING; - w->animation_is_tag |= ANIM_FADE; - } - if (ps->o.animation_for_prev_tag >= OPEN_WINDOW_ANIMATION_ZOOM) { - w->animation_is_tag |= ANIM_FAST; - w->dwm_mask |= ANIM_SPECIAL_MINIMIZE; - goto revert; - } - w->animation_is_tag |= ANIM_SLOW; + if (w->dwm_mask & ANIM_PREV_TAG) { + animation = ps->o.animation_for_prev_tag; + + if (ps->o.enable_fading_prev_tag) { + w->opacity_target_old = fmax(w->opacity_target, w->opacity_target_old); + w->state = WSTATE_FADING; + w->animation_is_tag |= ANIM_FADE; + } + if (ps->o.animation_for_prev_tag >= OPEN_WINDOW_ANIMATION_ZOOM) { + w->animation_is_tag |= ANIM_FAST; + w->dwm_mask |= ANIM_SPECIAL_MINIMIZE; + goto revert; + } + w->animation_is_tag |= ANIM_SLOW; } else if (w->dwm_mask & ANIM_NEXT_TAG) { - animation = ps->o.animation_for_next_tag; - w->animation_is_tag |= animation >= OPEN_WINDOW_ANIMATION_ZOOM ? ANIM_FAST : ANIM_SLOW; - if (ps->o.enable_fading_next_tag) { - w->opacity = 0.0; - w->state = WSTATE_FADING; - } + animation = ps->o.animation_for_next_tag; + w->animation_is_tag |= animation >= OPEN_WINDOW_ANIMATION_ZOOM ? ANIM_FAST : ANIM_SLOW; + if (ps->o.enable_fading_next_tag) { + w->opacity = 0.0; + w->state = WSTATE_FADING; + } } else if (w->dwm_mask & ANIM_UNMAP) { - animation = ps->o.animation_for_unmap_window; -revert: - anim_x = &w->animation_dest_center_x, anim_y = &w->animation_dest_center_y; - anim_w = &w->animation_dest_w, anim_h = &w->animation_dest_h; + animation = ps->o.animation_for_unmap_window; + revert: + anim_x = &w->animation_dest_center_x, anim_y = &w->animation_dest_center_y; + anim_w = &w->animation_dest_w, anim_h = &w->animation_dest_h; } + double angle; switch (animation) { - case OPEN_WINDOW_ANIMATION_NONE: { // No animation + case OPEN_WINDOW_ANIMATION_NONE: // No animation w->animation_center_x = w->pending_g.x + w->pending_g.width * 0.5; w->animation_center_y = w->pending_g.y + w->pending_g.height * 0.5; w->animation_w = w->pending_g.width; w->animation_h = w->pending_g.height; break; - } - case OPEN_WINDOW_ANIMATION_FLYIN: { // Fly-in from a random point outside the screen + case OPEN_WINDOW_ANIMATION_FLYIN: // Fly-in from a random point outside the screen // Compute random point off screen - double angle = 2 * M_PI * ((double)rand() / RAND_MAX); + angle = 2 * M_PI * ((double)rand() / RAND_MAX); const double radius = sqrt(ps->root_width * ps->root_width + ps->root_height * ps->root_height); @@ -530,106 +537,93 @@ revert: *anim_w = 0; *anim_h = 0; break; - } - case OPEN_WINDOW_ANIMATION_SLIDE_UP: { // Slide up the image, without changing its location + case OPEN_WINDOW_ANIMATION_SLIDE_UP: // Slide up the image, without changing its location *anim_x = w->pending_g.x + w->pending_g.width * 0.5; *anim_y = w->pending_g.y + w->pending_g.height; *anim_w = w->pending_g.width; *anim_h = 0; break; - } - case OPEN_WINDOW_ANIMATION_SLIDE_DOWN: { // Slide down the image, without changing its location + case OPEN_WINDOW_ANIMATION_SLIDE_DOWN: // Slide down the image, without changing its location *anim_x = w->pending_g.x + w->pending_g.width * 0.5; *anim_y = w->pending_g.y; *anim_w = w->pending_g.width; *anim_h = 0; break; - } - case OPEN_WINDOW_ANIMATION_SLIDE_LEFT: { // Slide left the image, without changing its location + case OPEN_WINDOW_ANIMATION_SLIDE_LEFT: // Slide left the image, without changing its location *anim_x = w->pending_g.x + w->pending_g.width; *anim_y = w->pending_g.y + w->pending_g.height * 0.5; *anim_w = 0; *anim_h = w->pending_g.height; break; - } - case OPEN_WINDOW_ANIMATION_SLIDE_RIGHT: { // Slide right the image, without changing its location + case OPEN_WINDOW_ANIMATION_SLIDE_RIGHT: // Slide right the image, without changing its location *anim_x = w->pending_g.x; *anim_y = w->pending_g.y + w->pending_g.height * 0.5; *anim_w = 0; *anim_h = w->pending_g.height; break; - } - case OPEN_WINDOW_ANIMATION_SLIDE_IN: { - *anim_x = w->pending_g.x + w->pending_g.width * 0.5; + case OPEN_WINDOW_ANIMATION_SLIDE_IN: + *anim_x = w->pending_g.x + w->pending_g.width * 0.5; *anim_y = w->pending_g.y + w->pending_g.height * 0.5; *anim_w = w->pending_g.width; *anim_h = w->pending_g.height; - break; - } - case OPEN_WINDOW_ANIMATION_SLIDE_IN_CENTER: { - *anim_x = ps->selmon_center_x; + break; + case OPEN_WINDOW_ANIMATION_SLIDE_IN_CENTER: + *anim_x = ps->selmon_center_x; *anim_y = w->g.y + w->pending_g.height * 0.5; *anim_w = w->pending_g.width; *anim_h = w->pending_g.height; - break; - } - case OPEN_WINDOW_ANIMATION_SLIDE_OUT: { - w->animation_dest_center_x = w->pending_g.x + w->pending_g.width * 0.5; - w->animation_dest_center_y = w->pending_g.y; - w->animation_dest_w = w->pending_g.width; - w->animation_dest_h = w->pending_g.height; - break; - } - case OPEN_WINDOW_ANIMATION_SLIDE_OUT_CENTER: { - w->animation_dest_center_x = ps->selmon_center_x; + break; + case OPEN_WINDOW_ANIMATION_SLIDE_OUT: + w->animation_dest_center_x = w->pending_g.x + w->pending_g.width * 0.5; w->animation_dest_center_y = w->pending_g.y; w->animation_dest_w = w->pending_g.width; w->animation_dest_h = w->pending_g.height; - break; - } - case OPEN_WINDOW_ANIMATION_ZOOM: { // Zoom-in the image, without changing its location - if (w->dwm_mask & ANIM_SPECIAL_MINIMIZE) { - *anim_x = w->g.x + w->g.width * 0.5; - *anim_y = w->g.y + w->g.height * 0.5; - } else { - *anim_x = w->pending_g.x + w->pending_g.width * 0.5; - *anim_y = w->pending_g.y + w->pending_g.height * 0.5; - } + break; + case OPEN_WINDOW_ANIMATION_SLIDE_OUT_CENTER: + w->animation_dest_center_x = ps->selmon_center_x; + w->animation_dest_center_y = w->pending_g.y; + w->animation_dest_w = w->pending_g.width; + w->animation_dest_h = w->pending_g.height; + break; + case OPEN_WINDOW_ANIMATION_ZOOM: // Zoom-in the image, without changing its location + if (w->dwm_mask & ANIM_SPECIAL_MINIMIZE) { + *anim_x = w->g.x + w->g.width * 0.5; + *anim_y = w->g.y + w->g.height * 0.5; + } else { + *anim_x = w->pending_g.x + w->pending_g.width * 0.5; + *anim_y = w->pending_g.y + w->pending_g.height * 0.5; + } *anim_w = 0; *anim_h = 0; break; - } - case OPEN_WINDOW_ANIMATION_MINIMIZE: { - *anim_x = ps->selmon_center_x; - *anim_y = ps->selmon_center_y; + case OPEN_WINDOW_ANIMATION_MINIMIZE: + *anim_x = ps->selmon_center_x; + *anim_y = ps->selmon_center_y; *anim_w = 0; *anim_h = 0; - break; - } - case OPEN_WINDOW_ANIMATION_SQUEEZE: { - if (w->dwm_mask & ANIM_PREV_TAG) { - *anim_h = 0; - } else { - *anim_x = w->pending_g.x + w->pending_g.width * 0.5; - *anim_y = w->pending_g.y + w->pending_g.height * 0.5; - *anim_w = w->pending_g.width; - *anim_h = 0; - } - break; - } - case OPEN_WINDOW_ANIMATION_SQUEEZE_BOTTOM: { - if (w->dwm_mask & ANIM_PREV_TAG) { - *anim_y = w->g.y + w->g.height; - *anim_h = 0; - } else { - w->animation_center_x = w->pending_g.x + w->pending_g.width * 0.5; - w->animation_center_y = w->pending_g.y + w->pending_g.height; - w->animation_w = w->pending_g.width; - *anim_h = 0; - *anim_y = w->pending_g.y + w->pending_g.height; - } - break; - } + break; + case OPEN_WINDOW_ANIMATION_SQUEEZE: + if (w->dwm_mask & ANIM_PREV_TAG) { + *anim_h = 0; + } else { + *anim_x = w->pending_g.x + w->pending_g.width * 0.5; + *anim_y = w->pending_g.y + w->pending_g.height * 0.5; + *anim_w = w->pending_g.width; + *anim_h = 0; + } + break; + case OPEN_WINDOW_ANIMATION_SQUEEZE_BOTTOM: + if (w->dwm_mask & ANIM_PREV_TAG) { + *anim_y = w->g.y + w->g.height; + *anim_h = 0; + } else { + w->animation_center_x = w->pending_g.x + w->pending_g.width * 0.5; + w->animation_center_y = w->pending_g.y + w->pending_g.height; + w->animation_w = w->pending_g.width; + *anim_h = 0; + *anim_y = w->pending_g.y + w->pending_g.height; + } + break; case OPEN_WINDOW_ANIMATION_INVALID: assert(false); break; } } @@ -674,34 +668,33 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) { add_damage_from_win(ps, w); } - - // Determine if a window should animate if (win_should_animate(ps, w)) { - win_update_bounding_shape(ps, w); - if (w->pending_g.y < 0 && w->g.y > 0 && abs(w->pending_g.y - w->g.y) >= w->pending_g.height) - w->dwm_mask = ANIM_PREV_TAG; - else if (w->pending_g.y > 0 && w->g.y < 0 && abs(w->pending_g.y - w->g.y) >= w->pending_g.height) - w->dwm_mask = ANIM_NEXT_TAG; + win_update_bounding_shape(ps, w); + if (w->pending_g.y < 0 && w->g.y > 0 && abs(w->pending_g.y - w->g.y) >= w->pending_g.height) + w->dwm_mask = ANIM_PREV_TAG; + else if (w->pending_g.y > 0 && w->g.y < 0 && abs(w->pending_g.y - w->g.y) >= w->pending_g.height) + w->dwm_mask = ANIM_NEXT_TAG; if (!was_visible || w->dwm_mask) { + // Set window-open animation init_animation(ps, w); + if (!(w->dwm_mask & ANIM_SPECIAL_MINIMIZE)) { + w->animation_dest_center_x = w->pending_g.x + w->pending_g.width * 0.5; + w->animation_dest_center_y = w->pending_g.y + w->pending_g.height * 0.5; + w->animation_dest_w = w->pending_g.width; + w->animation_dest_h = w->pending_g.height; + w->g.x = (int16_t)round(w->animation_center_x - + w->animation_w * 0.5); + w->g.y = (int16_t)round(w->animation_center_y - + w->animation_h * 0.5); + w->g.width = (uint16_t)round(w->animation_w); + w->g.height = (uint16_t)round(w->animation_h); + } - if (!(w->dwm_mask & ANIM_SPECIAL_MINIMIZE)) { - w->animation_dest_center_x = w->pending_g.x + w->pending_g.width * 0.5; - w->animation_dest_center_y = w->pending_g.y + w->pending_g.height * 0.5; - w->animation_dest_w = w->pending_g.width; - w->animation_dest_h = w->pending_g.height; - w->g.x = (int16_t)round(w->animation_center_x - - w->animation_w * 0.5); - w->g.y = (int16_t)round(w->animation_center_y - - w->animation_h * 0.5); - w->g.width = (uint16_t)round(w->animation_w); - w->g.height = (uint16_t)round(w->animation_h); - } } else { - w->animation_is_tag = ANIM_IN_TAG; + w->animation_is_tag = ANIM_IN_TAG; w->animation_dest_center_x = w->pending_g.x + w->pending_g.width * 0.5; w->animation_dest_center_y = @@ -709,10 +702,9 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) { w->animation_dest_w = w->pending_g.width; w->animation_dest_h = w->pending_g.height; } - CLEAR_MASK(w->dwm_mask) + CLEAR_MASK(w->dwm_mask) w->g.border_width = w->pending_g.border_width; - double x_dist = w->animation_dest_center_x - w->animation_center_x; double y_dist = w->animation_dest_center_y - w->animation_center_y; double w_dist = w->animation_dest_w - w->animation_w; @@ -744,7 +736,7 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) { } } - w->animation_progress = 0.0; + w->animation_progress = 0.0; } else { w->g = w->pending_g; } From c3a7cf29ab2e090b658ca1ce39390ffe15b4e9d6 Mon Sep 17 00:00:00 2001 From: Arda Atci Date: Mon, 23 Jan 2023 02:46:38 +0300 Subject: [PATCH 58/61] AUR pkg, closes #15, thanks fxzzi --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index a23dfd0..d2abd67 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,11 @@ $ ninja -C build ### To install +#### AUR (arch) +- picom-ftlabs-git +Thanks to @Fxzzi for maintaining the package. + + ``` bash $ ninja -C build install ``` From d41a291fd653057bcba985eb89f1b8caf55b9f1d Mon Sep 17 00:00:00 2001 From: Arda Atci Date: Mon, 23 Jan 2023 05:57:32 +0300 Subject: [PATCH 59/61] fix lerping on shadows, closes #4 --- src/backend/backend.c | 12 ++++---- src/backend/backend.h | 2 +- src/backend/dummy/dummy.c | 2 +- src/backend/gl/gl_common.c | 11 ++++--- src/backend/gl/gl_common.h | 2 +- src/backend/xrender/xrender.c | 2 +- src/picom.c | 55 +++++++++++++++++------------------ 7 files changed, 44 insertions(+), 42 deletions(-) diff --git a/src/backend/backend.c b/src/backend/backend.c index f5b5479..d53577c 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -71,9 +71,9 @@ static void process_window_for_painting(session_t *ps, struct managed_win *w, coord_t dest_coord = {.x = w->g.x + w->widthb, .y = w->g.y + w->heightb}; region_t reg_visible_local; + region_t reg_bound_local; { // The bounding shape, in window local coordinates - region_t reg_bound_local; pixman_region32_init(®_bound_local); pixman_region32_copy(®_bound_local, reg_bound); pixman_region32_translate(®_bound_local, -w->g.x, -w->g.y); @@ -87,7 +87,6 @@ static void process_window_for_painting(session_t *ps, struct managed_win *w, // region, not the clip region. pixman_region32_intersect(®_visible_local, ®_visible_local, ®_bound_local); - pixman_region32_fini(®_bound_local); } auto new_img = ps->backend_data->ops->clone_image(ps->backend_data, win_image, @@ -102,9 +101,10 @@ static void process_window_for_painting(session_t *ps, struct managed_win *w, pixman_region32_fini(®_frame); ps->backend_data->ops->compose(ps->backend_data, new_img, window_coord, NULL, dest_coord, - reg_paint_in_bound, reg_visible); + reg_paint_in_bound, reg_visible, true); ps->backend_data->ops->release_image(ps->backend_data, new_img); pixman_region32_fini(®_visible_local); + pixman_region32_fini(®_bound_local); } void handle_device_reset(session_t *ps) { @@ -240,7 +240,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { if (ps->root_image) { ps->backend_data->ops->compose(ps->backend_data, ps->root_image, (coord_t){0}, NULL, (coord_t){.x = ps->root_width, .y = ps->root_height}, - ®_paint, ®_visible); + ®_paint, ®_visible, true); } else { ps->backend_data->ops->fill(ps->backend_data, (struct color){0, 0, 0, 1}, ®_paint); @@ -420,7 +420,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { } ps->backend_data->ops->compose( ps->backend_data, w->shadow_image, shadow_coord, - inverted_mask, window_coord, ®_shadow, ®_visible); + inverted_mask, window_coord, ®_shadow, ®_visible, false); if (inverted_mask) { ps->backend_data->ops->set_image_property( ps->backend_data, IMAGE_PROPERTY_INVERTED, @@ -501,7 +501,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { if (w->frame_opacity == 1 && !is_animating) { ps->backend_data->ops->compose(ps->backend_data, w->win_image, window_coord, NULL, dest_coord, - ®_paint_in_bound, ®_visible); + ®_paint_in_bound, ®_visible, true); } else { if (is_animating && w->old_win_image) { bool is_focused = win_is_focused_raw(ps, w); diff --git a/src/backend/backend.h b/src/backend/backend.h index 3d620f8..2758c68 100644 --- a/src/backend/backend.h +++ b/src/backend/backend.h @@ -181,7 +181,7 @@ struct backend_operations { */ void (*compose)(backend_t *backend_data, void *image_data, coord_t image_dst, void *mask, coord_t mask_dst, const region_t *reg_paint, - const region_t *reg_visible); + const region_t *reg_visible, bool lerp); void (*_compose)(backend_t *backend_data, void *image_data, int dst_x1, int dst_y1, int dst_x2, int dst_y2, diff --git a/src/backend/dummy/dummy.c b/src/backend/dummy/dummy.c index 7e06fac..987e54e 100644 --- a/src/backend/dummy/dummy.c +++ b/src/backend/dummy/dummy.c @@ -64,7 +64,7 @@ static void dummy_check_image(struct backend_base *base, const struct dummy_imag void dummy_compose(struct backend_base *base, void *image, coord_t dst attr_unused, void *mask attr_unused, coord_t mask_dst attr_unused, const region_t *reg_paint attr_unused, - const region_t *reg_visible attr_unused) { + const region_t *reg_visible attr_unused, bool lerp attr_unused) { auto dummy attr_unused = (struct dummy_data *)base; dummy_check_image(base, image); assert(mask == NULL || mask == &dummy->mask); diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index ea08cd2..b61aae1 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -548,7 +548,7 @@ void x_rect_to_coords(int nrects, const rect_t *rects, coord_t image_dst, // TODO(yshui) make use of reg_visible void gl_compose(backend_t *base, void *image_data, coord_t image_dst, void *mask, coord_t mask_dst, const region_t *reg_tgt, - const region_t *reg_visible attr_unused) { + const region_t *reg_visible attr_unused, bool lerp) { auto gd = (struct gl_data *)base; struct backend_image *img = image_data; auto inner = (struct gl_texture *)img->inner; @@ -574,9 +574,12 @@ void gl_compose(backend_t *base, void *image_data, coord_t image_dst, void *mask coord_t mask_offset = {.x = mask_dst.x - image_dst.x, .y = mask_dst.y - image_dst.y}; x_rect_to_coords(nrects, rects, image_dst, inner->height, inner->height, gd->height, inner->y_inverted, coord, indices); - for (unsigned int i = 2; i < 16; i+=4) { - coord[i+0] = lerp_range(0, mask_offset.x, 0, inner->width, coord[i+0]); - coord[i+1] = lerp_range(0, mask_offset.y, 0, inner->height, coord[i+1]); + + if (lerp) { + for (unsigned int i = 2; i < 16; i+=4) { + coord[i+0] = lerp_range(0, mask_offset.x, 0, inner->width, coord[i+0]); + coord[i+1] = lerp_range(0, mask_offset.y, 0, inner->height, coord[i+1]); + } } _gl_compose(base, img, gd->back_fbo, mask, mask_offset, coord, indices, nrects); diff --git a/src/backend/gl/gl_common.h b/src/backend/gl/gl_common.h index 4aad9c0..4324604 100644 --- a/src/backend/gl/gl_common.h +++ b/src/backend/gl/gl_common.h @@ -147,7 +147,7 @@ bool gl_set_image_property(backend_t *backend_data, enum image_properties prop, * @brief Render a region with texture data. */ void gl_compose(backend_t *, void *image_data, coord_t image_dst, void *mask, - coord_t mask_dst, const region_t *reg_tgt, const region_t *reg_visible); + coord_t mask_dst, const region_t *reg_tgt, const region_t *reg_visible, bool lerp); void gl_resize(struct gl_data *, int width, int height); diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index 686e6de..5d3c314 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -350,7 +350,7 @@ compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg, coord_t dst, } static void compose(backend_t *base, void *img_data, coord_t dst, void *mask, coord_t mask_dst, - const region_t *reg_paint, const region_t *reg_visible) { + const region_t *reg_paint, const region_t *reg_visible, bool lerp attr_unused) { struct _xrender_data *xd = (void *)base; return compose_impl(xd, img_data, dst, mask, mask_dst, reg_paint, reg_visible, xd->back[2]); diff --git a/src/picom.c b/src/picom.c index 5bf610c..fce7ec8 100644 --- a/src/picom.c +++ b/src/picom.c @@ -909,39 +909,38 @@ paint_preprocess(session_t *ps, bool *fade_running, bool *animation_running) { add_damage_from_win(ps, w); } - if (w->opacity != w->opacity_target) { - // Run fading - if (run_fade(ps, &w, steps)) { - *fade_running = true; - } + // Run fading + if (run_fade(ps, &w, steps)) { + *fade_running = true; + } - // Add window to damaged area if its opacity changes - // If was_painted == false, and to_paint is also false, we don't care - // If was_painted == false, but to_paint is true, damage will be added in - // the loop below - if (was_painted && w->opacity != opacity_old) { - add_damage_from_win(ps, w); - } + // Add window to damaged area if its opacity changes + // If was_painted == false, and to_paint is also false, we don't care + // If was_painted == false, but to_paint is true, damage will be added in + // the loop below + if (was_painted && w->opacity != opacity_old) { + add_damage_from_win(ps, w); + } - if (win_check_fade_finished(ps, w)) { - // the window has been destroyed because fading finished - continue; - } - if (win_has_frame(w)) { - w->frame_opacity = ps->o.frame_opacity; - } else { - w->frame_opacity = 1.0; - } + if (win_check_fade_finished(ps, w)) { + // the window has been destroyed because fading finished + continue; + } - // Update window mode - w->mode = win_calc_mode(w); + if (win_has_frame(w)) { + w->frame_opacity = ps->o.frame_opacity; + } else { + w->frame_opacity = 1.0; + } - // Destroy all reg_ignore above when frame opaque state changes on - // SOLID mode - if (was_painted && w->mode != mode_old) { - w->reg_ignore_valid = false; - } + // Update window mode + w->mode = win_calc_mode(w); + + // Destroy all reg_ignore above when frame opaque state changes on + // SOLID mode + if (was_painted && w->mode != mode_old) { + w->reg_ignore_valid = false; } } From 97d9c960e097c587d73eef3dbb3699e84aa60afb Mon Sep 17 00:00:00 2001 From: Arda Atci Date: Tue, 4 Oct 2022 00:24:05 +0300 Subject: [PATCH 60/61] animations added, picom upstreamed yshui/next --- src/config.h | 6 + src/options.c | 486 +------------------------------------------------- src/picom.c | 5 +- src/win.c | 2 + 4 files changed, 20 insertions(+), 479 deletions(-) diff --git a/src/config.h b/src/config.h index b91a5ef..9d70274 100644 --- a/src/config.h +++ b/src/config.h @@ -306,6 +306,12 @@ typedef struct options { // Enable fading for prev tag bool enable_fading_prev_tag; + + /// A list of conditions of windows to which transparent clipping + /// should not apply + c2_lptr_t *transparent_clipping_blacklist; + + bool dithered_present; } options_t; extern const char *const BACKEND_STRS[NUM_BKEND + 1]; diff --git a/src/options.c b/src/options.c index 752b5a4..3cf3afc 100644 --- a/src/options.c +++ b/src/options.c @@ -179,6 +179,15 @@ static const struct picom_option picom_options[] = { "you want to attach a debugger to picom"}, {"no-ewmh-fullscreen" , no_argument , 803, NULL , "Do not use EWMH to detect fullscreen windows. Reverts to checking if a " "window is fullscreen based only on its size and coordinates."}, + {"animations" ,no_argument , 804, NULL , "Enable/disable animations."}, + {"animation-stiffness-in-tag" , required_argument, 805, NULL , "Animation speed in current tag (float)."}, + {"animation-stiffness-tag-change", required_argument, 806, NULL , "Animation speed when tag changes (change to a new desktop)."}, + {"animation-dampening" , required_argument, 807, NULL , "Animation dampening ratio (spring dampening as an example)."}, + {"animation-window-mass" , required_argument, 808, NULL , "Animation window mass (lower mass makes animations bumpy)."}, + {"animation-clamping" , no_argument , 809, NULL , "Enable/disable animation clamping. Disabling increases performance"}, + {"animation-for-open-window" , required_argument, 810, NULL , "Set animation for opening window (Check sample.conf for options)."}, + {"animation-for-transient-window", required_argument, 811, NULL , "Set animation for transient (child) windows."}, + }; // clang-format on @@ -246,375 +255,6 @@ void print_help(const char *help, size_t indent, size_t curr_indent, size_t line * Print usage text. */ static void usage(const char *argv0, int ret) { -#define WARNING_DISABLED " (DISABLED AT COMPILE TIME)" - static const char *usage_text = - "picom (" PICOM_VERSION ")\n" - "Please report bugs to https://github.com/yshui/picom\n\n" - "usage: %s [options]\n" - "Options:\n" - "\n" - "-r radius\n" - " The blur radius for shadows. (default 12)\n" - "\n" - "-o opacity\n" - " The translucency for shadows. (default .75)\n" - "\n" - "-l left-offset\n" - " The left offset for shadows. (default -15)\n" - "\n" - "-t top-offset\n" - " The top offset for shadows. (default -15)\n" - "\n" - "-I fade-in-step\n" - " Opacity change between steps while fading in. (default 0.028)\n" - "\n" - "-O fade-out-step\n" - " Opacity change between steps while fading out. (default 0.03)\n" - "\n" - "-D fade-delta-time\n" - " The time between steps in a fade in milliseconds. (default 10)\n" - "\n" - "-m opacity\n" - " The opacity for menus. (default 1.0)\n" - "\n" - "-c\n" - " Enabled client-side shadows on windows.\n" - "\n" - "-C\n" - " Avoid drawing shadows on dock/panel windows.\n" - "\n" - "-z\n" - " Zero the part of the shadow's mask behind the window.\n" - "\n" - "-f\n" - " Fade windows in/out when opening/closing and when opacity\n" - " changes, unless --no-fading-openclose is used.\n" - "\n" - "-F\n" - " Equals to -f. Deprecated.\n" - "\n" - "--animations\n" - " Run animations for window geometry changes (movement and scaling).\n" - "\n" - "--animation-for-open-window\n" - " Which animation to run when opening a window.\n" - " Must be one of `none`, `fly-in`, `zoom`,\n" - " `slide-down`, `slide-up`, `slide-left`, `slide-right`\n" - " (default: none).\n" - "\n" - "--animation-for-transient-window\n" - " Which animation to run when opening a transient window.\n" - " Must be one of `none`, `fly-in`, `zoom`,\n" - " `slide-down`, `slide-up`, `slide-left`, `slide-right`\n" - " (default: none).\n" - "\n" - "--animation-stiffness\n" - " Stiffness (a.k.a. tension) parameter for animation (default: 200.0).\n" - "\n" - "--animation-dampening\n" - " Dampening (a.k.a. friction) parameter for animation (default: 25.0).\n" - "\n" - "--animation-window-mass\n" - " Mass parameter for animation (default: 1.0).\n" - "\n" - "--animation-clamping\n" - " Whether to clamp animations (default: true)\n" - "\n" - "--animation-exclude condition\n" - " Exclude conditions for animation.\n" - "\n" - "-i opacity\n" - " Opacity of inactive windows. (0.1 - 1.0)\n" - "\n" - "-e opacity\n" - " Opacity of window titlebars and borders. (0.1 - 1.0)\n" - "\n" - "-G\n" - " Don't draw shadows on DND windows\n" - "\n" - "-b\n" - " Daemonize process.\n" - "\n" - "--show-all-xerrors\n" - " Show all X errors (for debugging).\n" - "\n" - "--config path\n" - " Look for configuration file at the path. Use /dev/null to avoid\n" - " loading configuration file." -#ifndef CONFIG_LIBCONFIG - WARNING_DISABLED -#endif - "\n\n" - "--write-pid-path path\n" - " Write process ID to a file.\n" - "\n" - "--shadow-color color\n" - " Color of shadow, as a hex RGB string (defaults to #000000)\n" - "\n" - "--shadow-red value\n" - " Red color value of shadow (0.0 - 1.0, defaults to 0).\n" - "\n" - "--shadow-green value\n" - " Green color value of shadow (0.0 - 1.0, defaults to 0).\n" - "\n" - "--shadow-blue value\n" - " Blue color value of shadow (0.0 - 1.0, defaults to 0).\n" - "\n" - "--inactive-opacity-override\n" - " Inactive opacity set by -i overrides value of _NET_WM_WINDOW_OPACITY.\n" - "\n" - "--inactive-dim value\n" - " Dim inactive windows. (0.0 - 1.0, defaults to 0)\n" - "\n" - "--active-opacity opacity\n" - " Default opacity for active windows. (0.0 - 1.0)\n" - "\n" - "--corner-radius value\n" - " Sets the radius of rounded window corners. When > 0, the compositor\n" - " will round the corners of windows. (defaults to 0).\n" - "\n" - "--rounded-corners-exclude condition\n" - " Exclude conditions for rounded corners.\n" - "\n" - "--mark-wmwin-focused\n" - " Try to detect WM windows and mark them as active.\n" - "\n" - "--shadow-exclude condition\n" - " Exclude conditions for shadows.\n" - "\n" - "--fade-exclude condition\n" - " Exclude conditions for fading.\n" - "\n" - "--mark-ovredir-focused\n" - " Mark windows that have no WM frame as active.\n" - "\n" - "--no-fading-openclose\n" - " Do not fade on window open/close.\n" - "\n" - "--no-fading-destroyed-argb\n" - " Do not fade destroyed ARGB windows with WM frame. Workaround of bugs\n" - " in Openbox, Fluxbox, etc.\n" - "\n" - "--shadow-ignore-shaped\n" - " Do not paint shadows on shaped windows. (Deprecated, use\n" - " --shadow-exclude \'bounding_shaped\' or\n" - " --shadow-exclude \'bounding_shaped && !rounded_corners\' instead.)\n" - "\n" - "--detect-rounded-corners\n" - " Try to detect windows with rounded corners and don't consider\n" - " them shaped windows. Affects --shadow-ignore-shaped,\n" - " --unredir-if-possible, and possibly others. You need to turn this\n" - " on manually if you want to match against rounded_corners in\n" - " conditions.\n" - "\n" - "--detect-client-opacity\n" - " Detect _NET_WM_WINDOW_OPACITY on client windows, useful for window\n" - " managers not passing _NET_WM_WINDOW_OPACITY of client windows to frame\n" - " windows.\n" - "\n" - "--vsync\n" - " Enable VSync\n" - "\n" - "--use-ewmh-active-win\n" - " Use _NET_WM_ACTIVE_WINDOW on the root window to determine which\n" - " window is focused instead of using FocusIn/Out events.\n" - "\n" - "--unredir-if-possible\n" - " Unredirect all windows if a full-screen opaque window is\n" - " detected, to maximize performance for full-screen windows.\n" - "\n" - "--unredir-if-possible-delay ms\n" - " Delay before unredirecting the window, in milliseconds.\n" - " Defaults to 0.\n" - "\n" - "--unredir-if-possible-exclude condition\n" - " Conditions of windows that shouldn't be considered full-screen\n" - " for unredirecting screen.\n" - "\n" - "--focus-exclude condition\n" - " Specify a list of conditions of windows that should always be\n" - " considered focused.\n" - "\n" - "--inactive-dim-fixed\n" - " Use fixed inactive dim value.\n" - "\n" - "--max-brightness\n" - " Dims windows which average brightness is above this threshold.\n" - " Requires --no-use-damage.\n" - " Default: 1.0 or no dimming.\n" - "\n" - "--detect-transient\n" - " Use WM_TRANSIENT_FOR to group windows, and consider windows in\n" - " the same group focused at the same time.\n" - "\n" - "--detect-client-leader\n" - " Use WM_CLIENT_LEADER to group windows, and consider windows in\n" - " the same group focused at the same time. This usually means windows\n" - " from the same application will be considered focused or unfocused at\n" - " the same time. WM_TRANSIENT_FOR has higher priority if\n" - " --detect-transient is enabled, too.\n" - "\n" - "--blur-method\n" - " The algorithm used for background bluring. Available choices are:\n" - " 'none' to disable, 'gaussian', 'box' or 'kernel' for custom\n" - " convolution blur with --blur-kern.\n" - " Note: 'gaussian' and 'box' is not supported by --legacy-backends.\n" - "\n" - "--blur-size\n" - " The radius of the blur kernel for 'box' and 'gaussian' blur method.\n" - "\n" - "--blur-deviation\n" - " The standard deviation for the 'gaussian' blur method.\n" - "\n" - "--blur-strength\n" - " The strength level of the 'dual_kawase' blur method.\n" - "\n" - "--blur-background\n" - " Blur background of semi-transparent / ARGB windows. Bad in\n" - " performance. The switch name may change without prior\n" - " notifications.\n" - "\n" - "--blur-background-frame\n" - " Blur background of windows when the window frame is not opaque.\n" - " Implies --blur-background. Bad in performance. The switch name\n" - " may change.\n" - "\n" - "--blur-background-fixed\n" - " Use fixed blur strength instead of adjusting according to window\n" - " opacity.\n" - "\n" - "--blur-kern matrix\n" - " Specify the blur convolution kernel, with the following format:\n" - " WIDTH,HEIGHT,ELE1,ELE2,ELE3,ELE4,ELE5...\n" - " The element in the center must not be included, it will be forever\n" - " 1.0 or changing based on opacity, depending on whether you have\n" - " --blur-background-fixed.\n" - " A 7x7 Gaussian blur kernel looks like:\n" - " --blur-kern " - "'7,7,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0." - "000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000849,0." - "029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.001723,0.059106,0." - "493069,0.493069,0.059106,0.001723,0.000849,0.029143,0.243117,0.493069,0." - "243117,0.029143,0.000849,0.000102,0.003494,0.029143,0.059106,0.029143,0." - "003494,0.000102,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0." - "000003'\n" - " Up to 4 blur kernels may be specified, separated with semicolon, for\n" - " multi-pass blur.\n" - " May also be one the predefined kernels: 3x3box (default), 5x5box,\n" - " 7x7box, 3x3gaussian, 5x5gaussian, 7x7gaussian, 9x9gaussian,\n" - " 11x11gaussian.\n" - "\n" - "--blur-background-exclude condition\n" - " Exclude conditions for background blur.\n" - "\n" - "--resize-damage integer\n" - " Resize damaged region by a specific number of pixels. A positive\n" - " value enlarges it while a negative one shrinks it. Useful for\n" - " fixing the line corruption issues of blur. May or may not\n" - " work with --glx-no-stencil. Shrinking doesn't function correctly.\n" - "\n" - "--invert-color-include condition\n" - " Specify a list of conditions of windows that should be painted with\n" - " inverted color. Resource-hogging, and is not well tested.\n" - "\n" - "--opacity-rule opacity:condition\n" - " Specify a list of opacity rules, in the format \"PERCENT:PATTERN\",\n" - " like \'50:name *= \"Firefox\"'. picom-trans is recommended over\n" - " this. Note we do not distinguish 100%% and unset, and we don't make\n" - " any guarantee about possible conflicts with other programs that set\n" - " _NET_WM_WINDOW_OPACITY on frame or client windows.\n" - "\n" - "--shadow-exclude-reg geometry\n" - " Specify a X geometry that describes the region in which shadow\n" - " should not be painted in, such as a dock window region.\n" - " Use --shadow-exclude-reg \'x10+0-0\', for example, if the 10 pixels\n" - " on the bottom of the screen should not have shadows painted on.\n" - "\n" - "--clip-shadow-above condition\n" - " Specify a list of conditions of windows to not paint a shadow over,\n" - " such as a dock window.\n" - "\n" - "--xinerama-shadow-crop\n" - " Crop shadow of a window fully on a particular Xinerama screen to the\n" - " screen.\n" - "\n" - "--backend backend\n" - " Choose backend. Possible choices are xrender, glx, and\n" - " xr_glx_hybrid." -#ifndef CONFIG_OPENGL - " (GLX BACKENDS DISABLED AT COMPILE TIME)" -#endif - "\n\n" - "--glx-no-stencil\n" - " GLX backend: Avoid using stencil buffer. Might cause issues\n" - " when rendering transparent content. My tests show a 15%% performance\n" - " boost.\n" - "\n" - "--glx-no-rebind-pixmap\n" - " GLX backend: Avoid rebinding pixmap on window damage. Probably\n" - " could improve performance on rapid window content changes, but is\n" - " known to break things on some drivers (LLVMpipe, xf86-video-intel,\n" - " etc.).\n" - "\n" - "--no-use-damage\n" - " Disable the use of damage information. This cause the whole screen to\n" - " be redrawn everytime, instead of the part of the screen that has\n" - " actually changed. Potentially degrades the performance, but might fix\n" - " some artifacts.\n" - "\n" - "--xrender-sync-fence\n" - " Additionally use X Sync fence to sync clients' draw calls. Needed\n" - " on nvidia-drivers with GLX backend for some users.\n" - "\n" - "--force-win-blend\n" - " Force all windows to be painted with blending. Useful if you have a\n" - " --glx-fshader-win that could turn opaque pixels transparent.\n" - "\n" - "--dbus\n" - " Enable remote control via D-Bus. See the D-BUS API section in the\n" - " man page for more details." -#ifndef CONFIG_DBUS - WARNING_DISABLED -#endif - "\n\n" - "--benchmark cycles\n" - " Benchmark mode. Repeatedly paint until reaching the specified cycles.\n" - "\n" - "--benchmark-wid window-id\n" - " Specify window ID to repaint in benchmark mode. If omitted or is 0,\n" - " the whole screen is repainted.\n" - "\n" - "--monitor-repaint\n" - " Highlight the updated area of the screen. For debugging the xrender\n" - " backend only.\n" - "\n" - "--debug-mode\n" - " Render into a separate window, and don't take over the screen. Useful\n" - " when you want to attach a debugger to picom\n" - "\n" - "--no-ewmh-fullscreen\n" - " Do not use EWMH to detect fullscreen windows. Reverts to checking\n" - " if a window is fullscreen based only on its size and coordinates.\n" - "\n" - "--transparent-clipping\n" - " Make transparent windows clip other windows like non-transparent windows\n" - " do, instead of blending on top of them\n" - "\n" - "--transparent-clipping-exclude condition\n" - " Specify a list of conditions of windows that should never have\n" - " transparent clipping applied. Useful for screenshot tools, where you\n" - " need to be able to see through transparent parts of the window.\n" - "\n" - "--window-shader-fg shader\n" - " Specify GLSL fragment shader path for rendering window contents. Does\n" - " not work when `--legacy-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`. `SHADER_PATH` can be \"default\", in which\n" - " case the default shader will be used. Does not work when\n" - " `--legacy-backends` is enabled.\n"; FILE *f = (ret ? stderr : stdout); fprintf(f, "picom (%s)\n", PICOM_VERSION); fprintf(f, "Standalone X11 compositor\n"); @@ -671,114 +311,6 @@ static void usage(const char *argv0, int ret) { } static const char *shortopts = "D:I:O:r:o:m:l:t:i:e:hscnfFCazGb"; -static const struct option longopts[] = { - {"help", no_argument, NULL, 'h'}, - {"config", required_argument, NULL, 256}, - {"shadow-radius", required_argument, NULL, 'r'}, - {"shadow-opacity", required_argument, NULL, 'o'}, - {"shadow-offset-x", required_argument, NULL, 'l'}, - {"shadow-offset-y", required_argument, NULL, 't'}, - {"fade-in-step", required_argument, NULL, 'I'}, - {"fade-out-step", required_argument, NULL, 'O'}, - {"fade-delta", required_argument, NULL, 'D'}, - {"menu-opacity", required_argument, NULL, 'm'}, - {"shadow", no_argument, NULL, 'c'}, - {"clear-shadow", no_argument, NULL, 'z'}, - {"fading", no_argument, NULL, 'f'}, - {"inactive-opacity", required_argument, NULL, 'i'}, - {"frame-opacity", required_argument, NULL, 'e'}, - {"daemon", no_argument, NULL, 'b'}, - {"shadow-red", required_argument, NULL, 257}, - {"shadow-green", required_argument, NULL, 258}, - {"shadow-blue", required_argument, NULL, 259}, - {"inactive-opacity-override", no_argument, NULL, 260}, - {"inactive-dim", required_argument, NULL, 261}, - {"mark-wmwin-focused", no_argument, NULL, 262}, - {"shadow-exclude", required_argument, NULL, 263}, - {"mark-ovredir-focused", no_argument, NULL, 264}, - {"no-fading-openclose", no_argument, NULL, 265}, - {"shadow-ignore-shaped", no_argument, NULL, 266}, - {"detect-rounded-corners", no_argument, NULL, 267}, - {"detect-client-opacity", no_argument, NULL, 268}, - {"refresh-rate", required_argument, NULL, 269}, - {"vsync", optional_argument, NULL, 270}, - {"sw-opti", no_argument, NULL, 274}, - {"vsync-aggressive", no_argument, NULL, 275}, - {"use-ewmh-active-win", no_argument, NULL, 276}, - {"respect-prop-shadow", no_argument, NULL, 277}, - {"unredir-if-possible", no_argument, NULL, 278}, - {"focus-exclude", required_argument, NULL, 279}, - {"inactive-dim-fixed", no_argument, NULL, 280}, - {"detect-transient", no_argument, NULL, 281}, - {"detect-client-leader", no_argument, NULL, 282}, - {"blur-background", no_argument, NULL, 283}, - {"blur-background-frame", no_argument, NULL, 284}, - {"blur-background-fixed", no_argument, NULL, 285}, - {"dbus", no_argument, NULL, 286}, - {"logpath", required_argument, NULL, 287}, - {"invert-color-include", required_argument, NULL, 288}, - {"opengl", no_argument, NULL, 289}, - {"backend", required_argument, NULL, 290}, - {"glx-no-stencil", no_argument, NULL, 291}, - {"benchmark", required_argument, NULL, 293}, - {"benchmark-wid", required_argument, NULL, 294}, - {"blur-background-exclude", required_argument, NULL, 296}, - {"active-opacity", required_argument, NULL, 297}, - {"glx-no-rebind-pixmap", no_argument, NULL, 298}, - {"glx-swap-method", required_argument, NULL, 299}, - {"fade-exclude", required_argument, NULL, 300}, - {"blur-kern", required_argument, NULL, 301}, - {"resize-damage", required_argument, NULL, 302}, - {"glx-use-gpushader4", no_argument, NULL, 303}, - {"opacity-rule", required_argument, NULL, 304}, - {"shadow-exclude-reg", required_argument, NULL, 305}, - {"paint-exclude", required_argument, NULL, 306}, - {"xinerama-shadow-crop", no_argument, NULL, 307}, - {"unredir-if-possible-exclude", required_argument, NULL, 308}, - {"unredir-if-possible-delay", required_argument, NULL, 309}, - {"write-pid-path", required_argument, NULL, 310}, - {"vsync-use-glfinish", no_argument, NULL, 311}, - {"xrender-sync-fence", no_argument, NULL, 313}, - {"show-all-xerrors", no_argument, NULL, 314}, - {"no-fading-destroyed-argb", no_argument, NULL, 315}, - {"force-win-blend", no_argument, NULL, 316}, - {"glx-fshader-win", required_argument, NULL, 317}, - {"version", no_argument, NULL, 318}, - {"no-x-selection", no_argument, NULL, 319}, - {"log-level", required_argument, NULL, 321}, - {"log-file", required_argument, NULL, 322}, - {"use-damage", no_argument, NULL, 323}, - {"no-use-damage", no_argument, NULL, 324}, - {"no-vsync", no_argument, NULL, 325}, - {"max-brightness", required_argument, NULL, 326}, - {"transparent-clipping", no_argument, NULL, 327}, - {"blur-method", required_argument, NULL, 328}, - {"blur-size", required_argument, NULL, 329}, - {"blur-deviation", required_argument, NULL, 330}, - {"blur-strength", required_argument, NULL, 331}, - {"shadow-color", required_argument, NULL, 332}, - {"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}, - {"transparent-clipping-exclude", required_argument, NULL, 338}, - {"legacy-backends", no_argument, NULL, 733}, - {"monitor-repaint", no_argument, NULL, 800}, - {"diagnostics", no_argument, NULL, 801}, - {"debug-mode", no_argument, NULL, 802}, - {"no-ewmh-fullscreen", no_argument, NULL, 803}, - {"animations", no_argument, NULL, 804}, - {"animation-stiffness-in-tag", required_argument, NULL, 805}, - {"animation-stiffness-tag-change", required_argument, NULL, 806}, - {"animation-dampening", required_argument, NULL, 807}, - {"animation-window-mass", required_argument, NULL, 808}, - {"animation-clamping", no_argument, NULL, 809}, - {"animation-for-open-window", required_argument, NULL, 810}, - {"animation-for-transient-window", required_argument, NULL, 811}, - // Must terminate with a NULL entry - {NULL, 0, NULL, 0}, -}; /// Get config options that are needed to parse the rest of the options /// Return true if we should quit diff --git a/src/picom.c b/src/picom.c index fce7ec8..7357c69 100644 --- a/src/picom.c +++ b/src/picom.c @@ -1917,8 +1917,8 @@ static session_t *session_init(int argc, char **argv, Display *dpy, .alpha_picts = NULL, .fade_time = 0L, .animation_time = 0L, - .ignore_head = NULL, - .ignore_tail = NULL, + .pending_reply_head = NULL, + .pending_reply_tail = NULL, .quit = false, .expose_rects = NULL, @@ -2715,6 +2715,7 @@ static void session_destroy(session_t *ps) { ev_timer_stop(ps->loop, &ps->unredir_timer); ev_timer_stop(ps->loop, &ps->fade_timer); ev_timer_stop(ps->loop, &ps->animation_timer); + ev_timer_stop(ps->loop, &ps->dpms_check_timer); ev_idle_stop(ps->loop, &ps->draw_idle); ev_prepare_stop(ps->loop, &ps->event_check); ev_signal_stop(ps->loop, &ps->usr1_signal); diff --git a/src/win.c b/src/win.c index e49faef..a710543 100644 --- a/src/win.c +++ b/src/win.c @@ -668,6 +668,8 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) { add_damage_from_win(ps, w); } + + // Determine if a window should animate if (win_should_animate(ps, w)) { win_update_bounding_shape(ps, w); From f5e83515070263e87bda2aa02852d2236265b711 Mon Sep 17 00:00:00 2001 From: Arda Atci Date: Sat, 4 Feb 2023 07:09:20 +0300 Subject: [PATCH 61/61] shadow fix --- src/backend/backend.c | 2 +- src/picom.c | 8 ++++---- src/win.c | 12 +++++++----- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/backend/backend.c b/src/backend/backend.c index d53577c..d373f82 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -497,7 +497,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { } // Draw window on target - bool is_animating = 0 <= w->animation_progress && w->animation_progress < 1.0; + bool is_animating = 0 < w->animation_progress && w->animation_progress < 1.0; if (w->frame_opacity == 1 && !is_animating) { ps->backend_data->ops->compose(ps->backend_data, w->win_image, window_coord, NULL, dest_coord, diff --git a/src/picom.c b/src/picom.c index 7357c69..2e813fb 100644 --- a/src/picom.c +++ b/src/picom.c @@ -739,7 +739,7 @@ paint_preprocess(session_t *ps, bool *fade_running, bool *animation_running) { // IMPORTANT: These window animation steps must happen before any other // [pre]processing. This is because it changes the window's geometry. if (ps->o.animations && - !isnan(w->animation_progress) && w->animation_progress != 1.0 && + !isnan(w->animation_progress) && w->animation_progress <= 0.999999999 && ps->o.wintype_option[w->window_type].animation != 0 && win_is_mapped_in_x(w)) { @@ -893,14 +893,14 @@ paint_preprocess(session_t *ps, bool *fade_running, bool *animation_running) { // We can't check for 1 here as sometimes 1 = 0.999999999999999 // in case of floating numbers if (w->animation_progress >= 0.999999999) { + win_process_update_flags(ps, w); w->animation_progress = 1; w->animation_velocity_x = 0.0; w->animation_velocity_y = 0.0; w->animation_velocity_w = 0.0; w->animation_velocity_h = 0.0; - w->opacity = win_calc_opacity_target(ps, w); + w->opacity = win_calc_opacity_target(ps, w); } - *animation_running = true; } @@ -2412,7 +2412,6 @@ static session_t *session_init(int argc, char **argv, Display *dpy, ev_idle_init(&ps->draw_idle, draw_callback); ev_init(&ps->fade_timer, fade_timer_callback); - ev_init(&ps->animation_timer, animation_timer_callback); // Set up SIGUSR1 signal handler to reset program ev_signal_init(&ps->usr1_signal, reset_enable, SIGUSR1); @@ -2512,6 +2511,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy, } free_winprop(&prop); } + ev_init(&ps->animation_timer, animation_timer_callback); if (fork && stderr_logger) { // Remove the stderr logger if we will fork diff --git a/src/win.c b/src/win.c index a710543..bf4299f 100644 --- a/src/win.c +++ b/src/win.c @@ -674,9 +674,9 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) { if (win_should_animate(ps, w)) { win_update_bounding_shape(ps, w); if (w->pending_g.y < 0 && w->g.y > 0 && abs(w->pending_g.y - w->g.y) >= w->pending_g.height) - w->dwm_mask = ANIM_PREV_TAG; + w->dwm_mask = ANIM_PREV_TAG; else if (w->pending_g.y > 0 && w->g.y < 0 && abs(w->pending_g.y - w->g.y) >= w->pending_g.height) - w->dwm_mask = ANIM_NEXT_TAG; + w->dwm_mask = ANIM_NEXT_TAG; if (!was_visible || w->dwm_mask) { @@ -1467,8 +1467,10 @@ void win_on_factor_change(session_t *ps, struct managed_win *w) { // focused state of the window win_update_focused(ps, w); - win_determine_shadow(ps, w); - win_determine_clip_shadow_above(ps, w); + if (w->animation_progress > 0.9999 || w->animation_progress < 0.0001) { + win_determine_shadow(ps, w); + win_determine_clip_shadow_above(ps, w); + } win_determine_invert_color(ps, w); win_determine_blur_background(ps, w); win_determine_rounded_corners(ps, w); @@ -1779,7 +1781,7 @@ struct win *fill_win(session_t *ps, struct win *w) { .animation_velocity_y = 0.0, // updated by window geometry changes .animation_velocity_w = 0.0, // updated by window geometry changes .animation_velocity_h = 0.0, // updated by window geometry changes - .animation_progress = 1.0, // updated by window geometry changes + .animation_progress = 0.0, // updated by window geometry changes .animation_inv_og_distance = NAN, // updated by window geometry changes .reg_ignore_valid = false, // set to true when damaged .flags = WIN_FLAGS_IMAGES_NONE, // updated by property/attributes/etc