Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions man/picom.1.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,9 @@ Within each sub-item, these keys are available: ::
opacity:::
Opacity of the matching window. (0.0 - 1.0). If not explicitly set by a rule, the opacity value from the window properties (e.g. pass:[_]NET_WM_WINDOW_OPACITY) will be used.

blur-opacity:::
Opacity of the background blur. (0.0 - 1.0). If not explicitly set by a rule, this will be 1.0. Ignored by `blur-background` is not enabled.

dim:::
Dim level of the matching window. Larger value means more dimming. (0.0 - 1.0)

Expand Down Expand Up @@ -514,9 +517,9 @@ animations = ({

_hide_:: When a window is minimized or iconified.

_increase-opacity_:: When the opacity of a window is increased.
_increase-opacity_:: When the opacity or the blur opacity of a window is increased. If the opacity and the blur opacity changed in different directions, the opacity change takes precedence.

_decrease-opacity_:: When the opacity of a window is decreased.
_decrease-opacity_:: When the opacity or the blur opacity of a window is decreased. If the opacity and the blur opacity changed in different directions, the opacity change takes precedence.

_size_, _position_:: When the size or position of a window is changed. If both changed, the position trigger has priority. (EXPERIMENTAL)

Expand Down Expand Up @@ -754,6 +757,8 @@ Currently, these context variables are defined: :::

_window-raw-opacity-before_, _window-raw-opacity_:: Animation triggers are usually accompanied by a change in the window's opacity. For example, when a window is opened, its opacity changes from 0 to 1. These two variables reflect the opacity of the window for the previous and current frame. They are useful if you want to smoothly transition the window's opacity.

_window-blur-opacity-before_, _window-blur-opacity_:: The blur opacity of the window for the previous and current frame.

_window-shadow-red_, _window-shadow-green_, _window-shadow-blue_, _window-shadow-red-before_, _window-shadow-green-before_, _window-shadow-blue-before_:: Color of the shadow before and after the shadow color change. (See notes below about _window-*-before_ variables.

IMPORTANT: All of the _window-*-before_ variables are updated every frame, and reflects the state of the window in the previous frame. Which means they will only be meaningful for a single frame, when an animation has just been triggered. Which means you should only use them to define the _start_, _end_, _duration_, or _delay_ values of a timing function, since these values are only evaluated once when the animation starts.
Expand Down
5 changes: 5 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ struct window_maybe_options {
/// Window opacity, NaN means not set.
double opacity;

/// Opacity of the blurred background, NaN means not set, ignored if
/// blur_background is disabled.
double blur_opacity;

/// Window dim level, NaN means not set.
double dim;

Expand Down Expand Up @@ -212,6 +216,7 @@ struct window_maybe_options {
struct window_options {
struct color shadow_color;
double opacity;
double blur_opacity;
double dim;
const char *shader;
unsigned int corner_radius;
Expand Down
26 changes: 18 additions & 8 deletions src/config_libconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ static void parse_animations(struct win_script *animations, config_setting_t *se
#define FADING_TEMPLATE_2 \
"blur-opacity = { " \
" duration = %s; " \
" start = %d; end = %d; " \
" start = %s; end = %s; " \
"};"

static bool compile_win_script_from_string(struct win_script *result, const char *input) {
Expand Down Expand Up @@ -511,9 +511,11 @@ void generate_fading_config(struct options *opt) {
// Fading in from nothing, i.e. `open` and `show`. These will fade blur
// opacity with the window opacity. Unless `blur-background-fixed` is
// used, in which case blur-opacity stays at 1.
int start = opt->blur_background_fixed ? 1 : 0;
auto start = opt->blur_background_fixed
? "\"window-blur-opacity\""
: "\"window-blur-opacity-before\"";
asnprintf(&str, &len, FADING_TEMPLATE_1 FADING_TEMPLATE_2, duration_str,
duration_str, start, 1);
duration_str, start, "\"window-blur-opacity\"");

struct win_script fade_in1 = {.is_generated = true};
BUG_ON(!compile_win_script_from_string(&fade_in1, str));
Expand All @@ -530,8 +532,10 @@ void generate_fading_config(struct options *opt) {
script_free(fade_in1.script);
}

// Fading for opacity change, for these, the blur opacity doesn't change.
asnprintf(&str, &len, FADING_TEMPLATE_1, duration_str);
// Fading for opacity change.
asnprintf(&str, &len, FADING_TEMPLATE_1 FADING_TEMPLATE_2, duration_str,
duration_str, "\"window-blur-opacity-before\"",
"\"window-blur-opacity\"");
struct win_script fade_in2 = {.is_generated = true};
BUG_ON(!compile_win_script_from_string(&fade_in2, str));
triggers = 0;
Expand All @@ -555,9 +559,10 @@ void generate_fading_config(struct options *opt) {
dtostr(duration, &duration_str);

// Fading out to nothing, i.e. `hide` and `close`. Same as above.
int end = opt->blur_background_fixed ? 1 : 0;
auto end = opt->blur_background_fixed ? "\"window-blur-opacity-before\""
: "\"window-blur-opacity\"";
asnprintf(&str, &len, FADING_TEMPLATE_1 FADING_TEMPLATE_2, duration_str,
duration_str, 1, end);
duration_str, "\"window-blur-opacity-before\"", end);
struct win_script fade_out1 = {.is_generated = true};
BUG_ON(!compile_win_script_from_string(&fade_out1, str));
if (opt->animations[ANIMATION_TRIGGER_CLOSE].script == NULL &&
Expand All @@ -574,7 +579,9 @@ void generate_fading_config(struct options *opt) {
}

// Fading for opacity change
asnprintf(&str, &len, FADING_TEMPLATE_1, duration_str);
asnprintf(&str, &len, FADING_TEMPLATE_1 FADING_TEMPLATE_2, duration_str,
duration_str, "\"window-blur-opacity-before\"",
"\"window-blur-opacity\"");
struct win_script fade_out2 = {.is_generated = true};
BUG_ON(!compile_win_script_from_string(&fade_out2, str));
triggers = 0;
Expand Down Expand Up @@ -681,6 +688,9 @@ static bool parse_rule(struct list_node *rules, config_setting_t *setting,
if (config_setting_lookup_float(setting, "opacity", &fval)) {
wopts->opacity = normalize_d(fval);
}
if (config_setting_lookup_float(setting, "blur-opacity", &fval)) {
wopts->blur_opacity = normalize_d(fval);
}
if (config_setting_lookup_float(setting, "dim", &fval)) {
wopts->dim = normalize_d(fval);
}
Expand Down
1 change: 1 addition & 0 deletions src/picom.c
Original file line number Diff line number Diff line change
Expand Up @@ -1884,6 +1884,7 @@ static struct window_options win_options_from_config(const struct options *opts)
.clip_shadow_above = false,
.unredir = WINDOW_UNREDIR_WHEN_POSSIBLE_ELSE_TERMINATE,
.opacity = 1,
.blur_opacity = 1,
};
memcpy(ret.animations, opts->animations, sizeof(ret.animations));
return ret;
Expand Down
13 changes: 6 additions & 7 deletions src/transition/script.c
Original file line number Diff line number Diff line change
Expand Up @@ -1178,13 +1178,12 @@ void script_instance_resume_from(struct script_instance *old, struct script_inst
}
}

enum script_evaluation_result
script_instance_evaluate(struct script_instance *instance, void *context) {
enum script_evaluation_result script_instance_evaluate(struct script_instance *instance,
void *context, bool do_branch_once) {
auto script = instance->script;
auto stack = (double *)&instance->memory[script->n_slots];
auto stack = &instance->memory[script->n_slots];
unsigned top = 0;
double l, r;
bool do_branch_once = instance->memory[script->elapsed_slot] == 0;
for (auto i = script->instrs;; i++) {
switch (i->type) {
case INST_IMM: stack[top++] = i->imm; break;
Expand Down Expand Up @@ -1303,7 +1302,7 @@ TEST_CASE(scripts_1) {
TEST_NOTEQUAL(c, NULL);

struct script_instance *instance = script_instance_new(script);
auto result = script_instance_evaluate(instance, NULL);
auto result = script_instance_evaluate(instance, NULL, true);
TEST_EQUAL(result, SCRIPT_EVAL_OK);
TEST_EQUAL(instance->memory[script->elapsed_slot + 1], 10.5);
TEST_EQUAL(instance->memory[outputs[0].slot], 10);
Expand All @@ -1314,12 +1313,12 @@ TEST_CASE(scripts_1) {
TEST_TRUE(!script_instance_is_finished(instance));

instance->memory[instance->script->elapsed_slot] += 5.5;
result = script_instance_evaluate(instance, NULL);
result = script_instance_evaluate(instance, NULL, false);
TEST_EQUAL(result, SCRIPT_EVAL_OK);
TEST_EQUAL(instance->memory[outputs[4].slot], 214);

instance->memory[instance->script->elapsed_slot] += 5.5;
result = script_instance_evaluate(instance, NULL);
result = script_instance_evaluate(instance, NULL, false);
TEST_EQUAL(result, SCRIPT_EVAL_OK);
TEST_EQUAL(instance->memory[outputs[0].slot], 10);
TEST_EQUAL(instance->memory[outputs[1].slot], 20);
Expand Down
6 changes: 4 additions & 2 deletions src/transition/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@ typedef struct config_setting_t config_setting_t;
struct script *
script_compile(config_setting_t *setting, struct script_parse_config cfg, char **out_err);
void script_free(struct script *script);
enum script_evaluation_result
script_instance_evaluate(struct script_instance *instance, void *context);
/// Evaluate a script instance with context. `do_branch_once` indicates whether
/// `BRANCH_ONCE` instructions should branch.
enum script_evaluation_result script_instance_evaluate(struct script_instance *instance,
void *context, bool do_branch_once);
/// Resume the script instance from another script instance that's currently running.
/// The script doesn't have to be the same. For resumable (explained later) transitions,
/// if matching variables exist in the `old` script, their starting point will be
Expand Down
23 changes: 18 additions & 5 deletions src/wm/win.c
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,11 @@ static double win_calc_opacity_target(session_t *ps, const struct win *w, bool f
return opacity;
}

static inline double win_get_blur_opacity(const struct win *w) {
auto wopts = win_options(w);
return w->state == WSTATE_MAPPED ? wopts.blur_opacity : 0.0;
}

/// Finish the unmapping of a window (e.g. after fading has finished).
/// Doesn't free `w`
void unmap_win_finish(session_t *ps, struct win *w) {
Expand Down Expand Up @@ -1592,7 +1597,8 @@ void unmap_win_start(struct win *w) {
w->opacity = 0.0F;
}

struct win_script_context win_script_context_prepare(struct session *ps, struct win *w) {
static inline struct win_script_context
win_script_context_prepare(struct session *ps, struct win *w) {
auto monitor_index = win_find_monitor(&ps->monitors, w);
auto monitor =
monitor_index >= 0
Expand All @@ -1610,6 +1616,8 @@ struct win_script_context win_script_context_prepare(struct session *ps, struct
.width_before = w->previous.g.width + w->previous.g.border_width * 2,
.height_before = w->previous.g.height + w->previous.g.border_width * 2,
.opacity_before = w->previous.opacity,
.blur_opacity = win_get_blur_opacity(w),
.blur_opacity_before = w->previous.blur_opacity,
.monitor_x = monitor.x1,
.monitor_y = monitor.y1,
.monitor_width = monitor.x2 - monitor.x1,
Expand All @@ -1628,7 +1636,7 @@ double win_animatable_get(const struct win *w, enum win_script_output output) {

auto wopts = win_options(w);
switch (output) {
case WIN_SCRIPT_BLUR_OPACITY: return w->state == WSTATE_MAPPED ? 1.0 : 0.0;
case WIN_SCRIPT_BLUR_OPACITY: return win_get_blur_opacity(w);
case WIN_SCRIPT_OPACITY:
case WIN_SCRIPT_SHADOW_OPACITY: return w->opacity;
case WIN_SCRIPT_CROP_X:
Expand Down Expand Up @@ -1669,8 +1677,8 @@ static bool win_advance_animation(struct win *w, double delta_t,
auto elapsed_slot =
script_elapsed_slot(w->running_animation_instance->script);
w->running_animation_instance->memory[elapsed_slot] += delta_t;
auto result =
script_instance_evaluate(w->running_animation_instance, (void *)win_ctx);
auto result = script_instance_evaluate(w->running_animation_instance,
(void *)win_ctx, false);
if (result != SCRIPT_EVAL_OK) {
log_error("Failed to run animation script: %d", result);
return true;
Expand Down Expand Up @@ -1704,6 +1712,7 @@ bool win_process_animation_and_state_change(struct session *ps, struct win *w, d
w->previous.opacity = w->opacity;
w->previous.g = w->g;
w->previous.shadow_color = w->options.shadow_color;
w->previous.blur_opacity = win_get_blur_opacity(w);

if (!ps->redirected || will_never_render) {
// This window won't be rendered, so we don't need to run the animations.
Expand Down Expand Up @@ -1780,6 +1789,10 @@ bool win_process_animation_and_state_change(struct session *ps, struct win *w, d
trigger = win_ctx.opacity > win_ctx.opacity_before
? ANIMATION_TRIGGER_INCREASE_OPACITY
: ANIMATION_TRIGGER_DECREASE_OPACITY;
} else if (win_ctx.blur_opacity_before != win_ctx.blur_opacity) {
trigger = win_ctx.blur_opacity > win_ctx.blur_opacity_before
? ANIMATION_TRIGGER_INCREASE_OPACITY
: ANIMATION_TRIGGER_DECREASE_OPACITY;
} else if (!color_eq(win_ctx.shadow_color_before, win_ctx.shadow_color)) {
assert(w->state == WSTATE_MAPPED);
trigger = ANIMATION_TRIGGER_COLOR;
Expand Down Expand Up @@ -1908,7 +1921,7 @@ bool win_process_animation_and_state_change(struct session *ps, struct win *w, d
}
w->running_animation_instance = new_animation;
w->running_animation = wopts.animations[trigger];
script_instance_evaluate(w->running_animation_instance, &win_ctx);
script_instance_evaluate(w->running_animation_instance, &win_ctx, true);
return script_instance_is_finished(w->running_animation_instance);
}

Expand Down
26 changes: 19 additions & 7 deletions src/wm/win.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ struct win_geometry {
struct win_state_change {
winstate_t state;
double opacity;
double blur_opacity;
struct win_geometry g;
struct color shadow_color;
};
Expand Down Expand Up @@ -234,6 +235,7 @@ struct win_script_context {
double x, y, width, height;
double x_before, y_before, width_before, height_before;
double opacity_before, opacity;
double blur_opacity_before, blur_opacity;
double monitor_x, monitor_y;
double monitor_width, monitor_height;
struct color shadow_color, shadow_color_before;
Expand All @@ -254,6 +256,8 @@ static const struct script_context_info win_script_context_info[] = {
{"window-height-before", X(height_before)},
{"window-raw-opacity-before", X(opacity_before)},
{"window-raw-opacity", X(opacity)},
{"window-blur-opacity-before", X(blur_opacity_before)},
{"window-blur-opacity", X(blur_opacity)},
{"window-monitor-x", X(monitor_x)},
{"window-monitor-y", X(monitor_y)},
{"window-monitor-width", X(monitor_width)},
Expand Down Expand Up @@ -300,6 +304,7 @@ static const struct window_maybe_options WIN_MAYBE_OPTIONS_DEFAULT = {
.paint = TRI_UNKNOWN,
.dim = NAN,
.opacity = NAN,
.blur_opacity = NAN,
.shader = NULL,
.corner_radius = -1,
.unredir = WINDOW_UNREDIR_INVALID,
Expand All @@ -312,10 +317,15 @@ static inline void win_script_fold(const struct win_script *upper,
}
}

/// Return `a` if it's not NaN, otherwise return `def`.
static inline double __attribute__((always_inline, const)) number_or_d(double a, double def) {
return safe_isnan(a) ? def : a;
}

/// Combine two window options. The `upper` value has higher priority, the `lower` value
/// will only be used if the corresponding value in `upper` is not set (e.g. it is
/// TRI_UNKNOWN for tristate values, NaN for opacity, -1 for corner_radius).
static inline struct window_maybe_options __attribute__((always_inline))
static inline struct window_maybe_options __attribute__((always_inline, const))
win_maybe_options_fold(struct window_maybe_options upper, struct window_maybe_options lower) {
struct window_maybe_options ret = {
.unredir = upper.unredir == WINDOW_UNREDIR_INVALID ? lower.unredir : upper.unredir,
Expand All @@ -328,8 +338,9 @@ win_maybe_options_fold(struct window_maybe_options upper, struct window_maybe_op
.paint = tri_or(upper.paint, lower.paint),
.transparent_clipping =
tri_or(upper.transparent_clipping, lower.transparent_clipping),
.opacity = !safe_isnan(upper.opacity) ? upper.opacity : lower.opacity,
.dim = !safe_isnan(upper.dim) ? upper.dim : lower.dim,
.opacity = number_or_d(upper.opacity, lower.opacity),
.blur_opacity = number_or_d(upper.blur_opacity, lower.blur_opacity),
.dim = number_or_d(upper.dim, lower.dim),
.shader = upper.shader ? upper.shader : lower.shader,
.corner_radius = upper.corner_radius >= 0 ? upper.corner_radius : lower.corner_radius,
.is_shadow_color_set = upper.is_shadow_color_set || lower.is_shadow_color_set,
Expand All @@ -341,7 +352,7 @@ win_maybe_options_fold(struct window_maybe_options upper, struct window_maybe_op

/// Unwrap a `window_maybe_options` to a `window_options`, using the default value for
/// values that are not set in the `window_maybe_options`.
static inline struct window_options __attribute__((always_inline))
static inline struct window_options __attribute__((always_inline, const))
win_maybe_options_or(struct window_maybe_options maybe, struct window_options def) {
assert(def.unredir != WINDOW_UNREDIR_INVALID);
struct window_options ret = {
Expand All @@ -357,16 +368,17 @@ win_maybe_options_or(struct window_maybe_options maybe, struct window_options de
.paint = tri_or_bool(maybe.paint, def.paint),
.transparent_clipping =
tri_or_bool(maybe.transparent_clipping, def.transparent_clipping),
.opacity = !safe_isnan(maybe.opacity) ? maybe.opacity : def.opacity,
.dim = !safe_isnan(maybe.dim) ? maybe.dim : def.dim,
.opacity = number_or_d(maybe.opacity, def.opacity),
.blur_opacity = number_or_d(maybe.blur_opacity, def.blur_opacity),
.dim = number_or_d(maybe.dim, def.dim),
.shader = maybe.shader ? maybe.shader : def.shader,
.shadow_color = maybe.is_shadow_color_set ? maybe.shadow_color : def.shadow_color,
};
win_script_fold(maybe.animations, def.animations, ret.animations);
return ret;
}

static inline struct window_options __attribute__((always_inline))
static inline struct window_options __attribute__((always_inline, const))
win_options(const struct win *w) {
return win_maybe_options_or(
win_maybe_options_fold(w->options_override, w->options), *w->options_default);
Expand Down
Loading