diff --git a/.circleci/config.yml b/.circleci/config.yml index d20c03945e..949ed97a6c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -55,6 +55,9 @@ jobs: - run: name: test config file parsing command: xvfb-run -s "-screen 0 640x480x24" build/src/picom --config tests/configs/parsing_test.conf --no-vsync --diagnostics + - run: + name: test config file parsing in a different locale + command: LC_NUMERIC=de_DE.UTF-8 xvfb-run -s "-screen 0 640x480x24" build/src/picom --config tests/configs/parsing_test.conf --no-vsync --diagnostics - run: name: run testsuite command: tests/run_tests.sh build/src/picom diff --git a/src/backend/backend_common.c b/src/backend/backend_common.c index 9ce7b05437..7863dde69c 100644 --- a/src/backend/backend_common.c +++ b/src/backend/backend_common.c @@ -249,7 +249,8 @@ bool build_shadow(struct x_connection *c, double opacity, const int width, batch_height = to_u16_checked(shadow_image->height - row); } - uint32_t offset = row * shadow_image->stride / sizeof(*shadow_image->data); + auto offset = + (size_t)row * shadow_image->stride / sizeof(*shadow_image->data); xcb_put_image(c->c, (uint8_t)shadow_image->format, shadow_pixmap, gc, shadow_image->width, batch_height, 0, to_i16_checked(row), 0, shadow_image->depth, shadow_image->stride * batch_height, diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index ffe936c414..ca1883edb1 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -494,9 +494,6 @@ xrender_copy_area(struct backend_base *base, ivec2 origin, image_handle target_h static bool xrender_blur(struct backend_base *base, ivec2 origin, image_handle target_handle, const struct backend_blur_args *args) { auto bctx = (struct xrender_blur_context *)args->blur_context; - auto source_mask = args->source_image - ? (struct xrender_image_data_inner *)args->source_mask->image - : NULL; auto source = (struct xrender_image_data_inner *)args->source_image; auto target = (struct xrender_image_data_inner *)target_handle; if (bctx->method == BLUR_METHOD_NONE) { @@ -547,22 +544,18 @@ static bool xrender_blur(struct backend_base *base, ivec2 origin, xcb_render_picture_t src_pict = source->pict; auto mask_pict = xd->alpha_pict[(int)(args->opacity * MAX_ALPHA)]; bool mask_allocated = false; - auto mask_pict_origin = args->source_mask->origin; - if (source_mask != NULL) { + ivec2 mask_pict_origin = {}; + if (args->source_mask != NULL) { // Translate the target mask region to the mask's coordinate auto mask_extent = *pixman_region32_extents(args->target_mask); region_translate_rect( mask_extent, ivec2_neg(ivec2_add(origin, args->source_mask->origin))); + mask_pict_origin = args->source_mask->origin; mask_pict = xrender_process_mask(xd, args->source_mask, mask_extent, args->opacity != 1.0 ? mask_pict : XCB_NONE, &mask_pict_origin, &mask_allocated); mask_pict_origin.x -= extent_resized->x1; mask_pict_origin.y -= extent_resized->y1; - } else { - // Sampling the 1x1 alpha pict out-of-bound while the X server is under - // heavy load, which it will be if blur is enabled, produces unpredictable - // results... This is a workaround for that. - mask_pict_origin = (ivec2){0, 0}; } x_set_picture_clip_region(c, src_pict, 0, 0, ®_op_resized); x_set_picture_clip_region(c, target->pict, 0, 0, args->target_mask); diff --git a/src/config_libconfig.c b/src/config_libconfig.c index 659650e49a..339d86e209 100644 --- a/src/config_libconfig.c +++ b/src/config_libconfig.c @@ -407,21 +407,22 @@ static struct script **parse_animations(struct win_script *animations, #define FADING_TEMPLATE_1 \ "opacity = { " \ - " timing = \"%fms linear\"; " \ + " timing = \"%sms linear\"; " \ " start = \"window-raw-opacity-before\"; " \ " end = \"window-raw-opacity\"; " \ "};" \ "shadow-opacity = \"opacity\";" #define FADING_TEMPLATE_2 \ "blur-opacity = { " \ - " timing = \"%fms linear\"; " \ - " start = %f; end = %f; " \ + " timing = \"%sms linear\"; " \ + " start = %d; end = %d; " \ "};" static struct script *compile_win_script_from_string(const char *input, int *output_indices) { config_t tmp_config; config_setting_t *setting; config_init(&tmp_config); + config_set_auto_convert(&tmp_config, true); config_read_string(&tmp_config, input); setting = config_root_setting(&tmp_config); @@ -437,7 +438,7 @@ static struct script *compile_win_script_from_string(const char *input, int *out void generate_fading_config(struct options *opt) { // We create stand-in animations for fade-in/fade-out if they haven't be // overwritten - char *str = NULL; + scoped_charp str = NULL; size_t len = 0; enum animation_trigger trigger[2]; struct script *scripts[4]; @@ -446,72 +447,88 @@ void generate_fading_config(struct options *opt) { int output_indices[NUM_OF_WIN_SCRIPT_OUTPUTS]; double duration = 1.0 / opt->fade_in_step * opt->fade_delta; - // Fading in from nothing, i.e. `open` and `show` - asnprintf(&str, &len, FADING_TEMPLATE_1 FADING_TEMPLATE_2, duration, duration, - 0.F, 1.F); + if (!safe_isinf(duration) && !safe_isnan(duration) && duration > 0) { + scoped_charp duration_str = NULL; + dtostr(duration, &duration_str); - auto fade_in1 = compile_win_script_from_string(str, output_indices); - if (opt->animations[ANIMATION_TRIGGER_OPEN].script == NULL && !opt->no_fading_openclose) { - trigger[number_of_triggers++] = ANIMATION_TRIGGER_OPEN; - } - if (opt->animations[ANIMATION_TRIGGER_SHOW].script == NULL) { - trigger[number_of_triggers++] = ANIMATION_TRIGGER_SHOW; - } - if (set_animation(opt->animations, trigger, number_of_triggers, fade_in1, - output_indices, 0, 0)) { - scripts[number_of_scripts++] = fade_in1; - } else { - script_free(fade_in1); - } + // Fading in from nothing, i.e. `open` and `show` + asnprintf(&str, &len, FADING_TEMPLATE_1 FADING_TEMPLATE_2, duration_str, + duration_str, 0, 1); - // Fading for opacity change, for these, the blur opacity doesn't change. - asnprintf(&str, &len, FADING_TEMPLATE_1, duration); - auto fade_in2 = compile_win_script_from_string(str, output_indices); - number_of_triggers = 0; - if (opt->animations[ANIMATION_TRIGGER_INCREASE_OPACITY].script == NULL) { - trigger[number_of_triggers++] = ANIMATION_TRIGGER_INCREASE_OPACITY; - } - if (set_animation(opt->animations, trigger, number_of_triggers, fade_in2, - output_indices, 0, 0)) { - scripts[number_of_scripts++] = fade_in2; + auto fade_in1 = compile_win_script_from_string(str, output_indices); + if (opt->animations[ANIMATION_TRIGGER_OPEN].script == NULL && + !opt->no_fading_openclose) { + trigger[number_of_triggers++] = ANIMATION_TRIGGER_OPEN; + } + if (opt->animations[ANIMATION_TRIGGER_SHOW].script == NULL) { + trigger[number_of_triggers++] = ANIMATION_TRIGGER_SHOW; + } + if (set_animation(opt->animations, trigger, number_of_triggers, fade_in1, + output_indices, 0, 0)) { + scripts[number_of_scripts++] = fade_in1; + } else { + script_free(fade_in1); + } + + // Fading for opacity change, for these, the blur opacity doesn't change. + asnprintf(&str, &len, FADING_TEMPLATE_1, duration_str); + auto fade_in2 = compile_win_script_from_string(str, output_indices); + number_of_triggers = 0; + if (opt->animations[ANIMATION_TRIGGER_INCREASE_OPACITY].script == NULL) { + trigger[number_of_triggers++] = ANIMATION_TRIGGER_INCREASE_OPACITY; + } + if (set_animation(opt->animations, trigger, number_of_triggers, fade_in2, + output_indices, 0, 0)) { + scripts[number_of_scripts++] = fade_in2; + } else { + script_free(fade_in2); + } } else { - script_free(fade_in2); + log_error("Invalid fade-in setting (step: %f, delta: %d), ignoring.", + opt->fade_in_step, opt->fade_delta); } duration = 1.0 / opt->fade_out_step * opt->fade_delta; - // Fading out to nothing, i.e. `hide` and `close` - asnprintf(&str, &len, FADING_TEMPLATE_1 FADING_TEMPLATE_2, duration, duration, - 1.F, 0.F); - auto fade_out1 = compile_win_script_from_string(str, output_indices); - number_of_triggers = 0; - if (opt->animations[ANIMATION_TRIGGER_CLOSE].script == NULL && - !opt->no_fading_openclose) { - trigger[number_of_triggers++] = ANIMATION_TRIGGER_CLOSE; - } - if (opt->animations[ANIMATION_TRIGGER_HIDE].script == NULL) { - trigger[number_of_triggers++] = ANIMATION_TRIGGER_HIDE; - } - if (set_animation(opt->animations, trigger, number_of_triggers, fade_out1, - output_indices, 0, 0)) { - scripts[number_of_scripts++] = fade_out1; - } else { - script_free(fade_out1); - } + if (!safe_isinf(duration) && !safe_isnan(duration) && duration > 0) { + scoped_charp duration_str = NULL; + dtostr(duration, &duration_str); + + // Fading out to nothing, i.e. `hide` and `close` + asnprintf(&str, &len, FADING_TEMPLATE_1 FADING_TEMPLATE_2, duration_str, + duration_str, 1, 0); + auto fade_out1 = compile_win_script_from_string(str, output_indices); + number_of_triggers = 0; + if (opt->animations[ANIMATION_TRIGGER_CLOSE].script == NULL && + !opt->no_fading_openclose) { + trigger[number_of_triggers++] = ANIMATION_TRIGGER_CLOSE; + } + if (opt->animations[ANIMATION_TRIGGER_HIDE].script == NULL) { + trigger[number_of_triggers++] = ANIMATION_TRIGGER_HIDE; + } + if (set_animation(opt->animations, trigger, number_of_triggers, fade_out1, + output_indices, 0, 0)) { + scripts[number_of_scripts++] = fade_out1; + } else { + script_free(fade_out1); + } - // Fading for opacity change - asnprintf(&str, &len, FADING_TEMPLATE_1, duration); - auto fade_out2 = compile_win_script_from_string(str, output_indices); - number_of_triggers = 0; - if (opt->animations[ANIMATION_TRIGGER_DECREASE_OPACITY].script == NULL) { - trigger[number_of_triggers++] = ANIMATION_TRIGGER_DECREASE_OPACITY; - } - if (set_animation(opt->animations, trigger, number_of_triggers, fade_out2, - output_indices, 0, 0)) { - scripts[number_of_scripts++] = fade_out2; + // Fading for opacity change + asnprintf(&str, &len, FADING_TEMPLATE_1, duration_str); + auto fade_out2 = compile_win_script_from_string(str, output_indices); + number_of_triggers = 0; + if (opt->animations[ANIMATION_TRIGGER_DECREASE_OPACITY].script == NULL) { + trigger[number_of_triggers++] = ANIMATION_TRIGGER_DECREASE_OPACITY; + } + if (set_animation(opt->animations, trigger, number_of_triggers, fade_out2, + output_indices, 0, 0)) { + scripts[number_of_scripts++] = fade_out2; + } else { + script_free(fade_out2); + } } else { - script_free(fade_out2); + log_error("Invalid fade-out setting (step: %f, delta: %d), ignoring.", + opt->fade_out_step, opt->fade_delta); } - free(str); log_debug("Generated %d scripts for fading.", number_of_scripts); if (number_of_scripts) { diff --git a/src/string_utils.h b/src/string_utils.h index 970f227ea1..88f7d47747 100644 --- a/src/string_utils.h +++ b/src/string_utils.h @@ -2,11 +2,12 @@ // Copyright (c) Yuxuan Shui #pragma once #include +#include #include #include #include -#include "compiler.h" +#include "utils.h" #define mstrncmp(s1, s2) strncmp((s1), (s2), strlen(s1)) @@ -38,6 +39,24 @@ static inline int uitostr(unsigned int n, char *buf) { return ret; } +/// Convert a double into a string. Avoid using *printf functions to print floating points +/// directly because they are locale dependent. +static inline void dtostr(double n, char **buf) { + BUG_ON(safe_isnan(n)); + BUG_ON(safe_isinf(n)); + if (fabs(n) > 1e9) { + // The number is so big that it's not meaningful to keep decimal places. + asprintf(buf, "%.0f", n); + return; + } + + if (n > 0) { + asprintf(buf, "%.0f.%03d", floor(n), (int)(fmod(n, 1) * 1000)); + } else { + asprintf(buf, "-%.0f.%03d", floor(-n), (int)(fmod(-n, 1) * 1000)); + } +} + static inline const char *skip_space_const(const char *src) { if (!src) { return NULL; @@ -70,4 +89,5 @@ static inline bool starts_with(const char *str, const char *needle, bool ignore_ /// Similar to `asprintf`, but it reuses the allocated memory pointed to by `*strp`, and /// reallocates it if it's not big enough. -int asnprintf(char **strp, size_t *capacity, const char *fmt, ...); +int asnprintf(char **strp, size_t *capacity, const char *fmt, ...) + __attribute__((format(printf, 3, 4))); diff --git a/src/win.c b/src/win.c index 06e95fe6d6..611121276b 100644 --- a/src/win.c +++ b/src/win.c @@ -2245,11 +2245,10 @@ bool win_process_animation_and_state_change(struct session *ps, struct managed_w bool will_never_render = !w->ever_damaged && w->state != WSTATE_MAPPED; if (!ps->redirected || will_never_render) { // This window won't be rendered, so we don't need to run the animations. - assert(w->running_animation == NULL); bool state_changed = w->previous.state != w->state; w->previous.state = w->state; w->opacity = win_calc_opacity_target(ps, w); - return state_changed; + return state_changed || (w->running_animation != NULL); } auto win_ctx = win_script_context_prepare(ps, w); diff --git a/tests/run_one_test.sh b/tests/run_one_test.sh index 2b745710c9..98314df308 100755 --- a/tests/run_one_test.sh +++ b/tests/run_one_test.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -xe if [ -z $DISPLAY ]; then exec xvfb-run -s "+extension composite" -a $0 $1 $2 $3 @@ -6,14 +6,25 @@ fi echo "Running test $2" -# TODO keep the log file, and parse it to see if test is successful -($1 --dbus --backend dummy --log-level=debug --log-file=$PWD/log --config=$2) & -main_pid=$! -$3 +picom_exe=$1 +config=$2 +test_script=$3 -kill -INT $main_pid || true -cat log -rm log -wait $main_pid +function test_with_backend() { + backend=$1 + # TODO keep the log file, and parse it to see if test is successful + ($picom_exe --dbus --backend $backend --log-level=debug --log-file=$PWD/log --config=$config) & + main_pid=$! + $test_script + kill -INT $main_pid || true + cat log + rm log + wait $main_pid +} + +test_with_backend dummy +test_with_backend xrender +test_with_backend glx +# test_with_backend egl