Skip to content

Commit

Permalink
Update to head
Browse files Browse the repository at this point in the history
  • Loading branch information
pijulius committed Aug 13, 2024
2 parents fc0938b + f4fcedc commit e5aaa1a
Show file tree
Hide file tree
Showing 28 changed files with 886 additions and 206 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Unreleased
# v12-rc1 (2024-Aug-12)

## New features

* Universal window rules (#1284). One option to rule them all! Added new configuration option `rules` to replace all existing rule options, and to provide more flexibility on top of that. See [picom(1)](https://picom.app/#_window_rules) for more details.
* Animations! Yes, now picom officially supports animations. For examples, and information on how to configure it, please go to our [documentation site](https://picom.app/#_animations). There are some video clips in #1253 as well. (#1220 #1253 #1303 #1305 #1308 #1310)
* Universal window rules (#1284). One option to rule them all! Added new configuration option `rules` to replace all existing rule options, and to provide more flexibility on top of that. See [picom(1)](https://picom.app/#_window_rules) for more details. This can be used to configure per-window animations.
* `@include` directives in config file now also search in `$XDG_CONFIG_HOME/picom/include` and `$XDG_CONFIG_DIRS/picom/include`, in addition to relative to the config file's parent directory.
* Allow `corner-radius-rules` to override `corner-radius = 0`. Previously setting corner radius to 0 globally disables rounded corners. (#1170)
* New `picom-inspect` tool, which lets you test out your picom rules. `man picom-inspect(1)` for more details. Sample output:
Expand Down Expand Up @@ -50,6 +51,7 @@

* Fix ghosting artifacts that sometimes occur when window manager is restarted (#1081)
* Fix a bug where rounded corner is disabled after making a window fullscreen and back (#1216)
* Fix ugly looking rounded corners (#1261)

## Build changes

Expand Down
Binary file added assets/geometry-change.mp4
Binary file not shown.
41 changes: 40 additions & 1 deletion data/animation_presets.conf
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,43 @@ fly-in = {
(2, "direction", [-1, 1, 0, 0]),
(3, "direction", [0, 0, 1, 1]),
);
}
};
geometry-change = {
scale-x = {
curve = "cubic-bezier(0.07, 0.65, 0, 1)";
duration = "placeholder0";
start = "window-width-before / window-width";
end = 1;
};
scale-y = {
curve = "cubic-bezier(0.07, 0.65, 0, 1)";
duration = "placeholder0";
start = "window-height-before / window-height";
end = 1;
};
shadow-scale-x = "scale-x";
shadow-scale-y = "scale-y";
offset-x = {
curve = "cubic-bezier(0.07, 0.65, 0, 1)";
duration = "placeholder0";
start = "window-x-before - window-x";
end = 0;
};
offset-y = {
curve = "cubic-bezier(0.07, 0.65, 0, 1)";
duration = "placeholder0";
start = "window-y-before - window-y";
end = 0;
};
saved-image-blend = {
duration = "placeholder0";
start = 1;
end = 0;
};
shadow-offset-x = "offset-x";
shadow-offset-y = "offset-y";
*knobs = {
duration = 0.4;
};
*placeholders = ((0, "duration"));
};
1 change: 1 addition & 0 deletions include/picom/backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ enum backend_command_op {
/// will later be filled in by the renderer using this symbolic reference.
enum backend_command_source {
BACKEND_COMMAND_SOURCE_WINDOW,
BACKEND_COMMAND_SOURCE_WINDOW_SAVED,
BACKEND_COMMAND_SOURCE_SHADOW,
BACKEND_COMMAND_SOURCE_BACKGROUND,
};
Expand Down
32 changes: 31 additions & 1 deletion man/picom.1.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,10 @@ animations = ({

_decrease-opacity_:: When the opacity of a window is decreased.

[[trigger-geometry]]_geometry_:: When the geometry of a window is changed. (EXPERIMENTAL)
+
WARNING: The _geometry_ trigger is experimental. Using this means you accept the caveat that geometry animations will also trigger when you manually resize or move a window, like when you drag the window around with your mouse.

_suppressions_:::
Which other animations should be suppressed when this animation is running. Normally, if another trigger is activated while an animation is already running, the animation in progress will be interrupted and the new animation will start. If you want to prevent this, you can set the `suppressions` option to a list of triggers that should be suppressed. This is optional, the default value for this is an empty list.

Expand Down Expand Up @@ -594,6 +598,24 @@ endif::[]

_duration_:: Duration of the animation in seconds.
--
+
_geometry-change_:::
+
Animate the geometry (i.e. size and position) change of the window.
+
WARNING: This makes use of both the <<trigger-geometry>> trigger, and the <<saved-image-blend>> output variable. Both of these features are experimental and may not work as expected.
+
--
ifdef::env-web[]
video::assets/geometry-change.mp4[width=400]
endif::[]
--
+
--
*Options*:::

_duration_:: Duration of the animation in seconds.
--

=== Advanced

Expand Down Expand Up @@ -693,6 +715,10 @@ Currently, these output variables are supported: :::

_crop-x_, _crop-y_, _crop-width_, _crop-height_:: These four values combined defines a rectangle on the screen. The window and its shadow will be cropped to this rectangle. If not defined, the window and shadow will not be cropped.

[[saved-image-blend]]_saved-image-blend_:: When the window's geometry changes, its content will often change drastically, creating a jarring discontinuity. This output variable allows you to blend the window's content before and after the geometry change, the before and after images will be stretched appropriately to match the animation. This way you can smoothly animated geometry changes. This is a number between 0 and 1. 0 means the saved image is not used, whereas 1 means you will only see the saved image. (EXPERIMENTAL)
+
WARNING: The _saved-image-blend_ variable is experimental. It might work incorrectly, cause visual artifacts, or slow down your system. You are welcome to open an issue on GitHub if you encounter any problems to help us improve it, though resolution is not guaranteed.

All coordinates are in pixels, and are in the coordinate system of the screen. Sizes are also in pixels.

IMPORTANT: If an output variable name is not defined in your animation script, it will take the default value for whichever state the window is in. Specifically, if you don't define an _opacity_ variable in the animation script for the "close" or "hide" trigger, a closed window will, by default, have 0 opacity. So you will just see it disappear instantly. Oftentimes, you will want to set _opacity_ to 1 to make the window visible for the duration of the animation.
Expand All @@ -713,9 +739,13 @@ Currently, these context variables are defined: :::

_window-width_, _window-height_:: The size of the window.

_window-x-before_, _window-y-before_, _window-width-before_, _window-height-before_:: The size and coordinates of the window from the previous frame. This is only meaningfully different from the normal window geometry variables inside animations triggered by the _geometry_ trigger.

_window-monitor-x_, _window-monitor-y_, _window-monitor-width_, _window-monitor-height_:: Defines the rectangle which reflects the monitor the window is on. If the window is not fully contained in any monitor, the rectangle will reflect the entire virtual screen.

_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 before and after the animation is triggered. They are useful if you want to smoothly transition the window's opacity.
_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.

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.
--

=== Share your animations
Expand Down
5 changes: 5 additions & 0 deletions src/backend/backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "config.h"
#include "log.h"
#include "region.h"
#include "renderer/layout.h"
#include "wm/win.h"
#include "x.h"

Expand Down Expand Up @@ -93,6 +94,9 @@ bool backend_execute(struct backend_base *backend, image_handle target, unsigned
if (!pixman_region32_not_empty(cmd->blit.target_mask)) {
continue;
}
if (cmd->blit.opacity < 1. / MAX_ALPHA) {
continue;
}
succeeded =
backend->ops.blit(backend, cmd->origin, target, &cmd->blit);
break;
Expand Down Expand Up @@ -121,6 +125,7 @@ bool backend_execute(struct backend_base *backend, image_handle target, unsigned
static inline const char *render_command_source_name(enum backend_command_source source) {
switch (source) {
case BACKEND_COMMAND_SOURCE_WINDOW: return "window";
case BACKEND_COMMAND_SOURCE_WINDOW_SAVED: return "window_saved";
case BACKEND_COMMAND_SOURCE_SHADOW: return "shadow";
case BACKEND_COMMAND_SOURCE_BACKGROUND: return "background";
}
Expand Down
65 changes: 41 additions & 24 deletions src/backend/gl/shaders.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,20 +84,23 @@ const char masking_glsl[] = GLSL(330,
layout(location = UNIFORM_MASK_INVERTED_LOC)
uniform bool mask_inverted;
in vec2 texcoord;
float mask_rectangle_sdf(vec2 point, vec2 half_size) {
vec2 d = abs(point) - half_size;
return length(max(d, 0.0));
vec2 mask_rectangle_sdf(vec2 point, vec2 half_size) {
vec2 d = max(abs(point) - half_size, 0.0);
float l = length(d);
// Add a small number to avoid 0/0.
return vec2(l, l / (max(d.x, d.y) + 1e-8));
}
float mask_factor() {
vec2 mask_size = textureSize(mask_tex, 0);
vec2 maskcoord = texcoord - mask_offset;
vec4 mask = texture2D(mask_tex, maskcoord / mask_size);
if (mask_corner_radius != 0) {
vec2 inner_size = mask_size - vec2(mask_corner_radius) * 2.0f - 1;
float dist = mask_rectangle_sdf(maskcoord - mask_size / 2.0f,
inner_size / 2.0f) - mask_corner_radius;
vec2 inner_size = mask_size - vec2(mask_corner_radius) * 2.0f;
vec2 sdf = mask_rectangle_sdf(maskcoord - mask_size / 2.0f,
inner_size / 2.0f);
float dist = sdf.x - mask_corner_radius + sdf.y / 2.0f;
if (dist > 0.0f) {
mask.r *= (1.0f - clamp(dist, 0.0f, 1.0f));
mask.r *= (1.0f - clamp(dist, 0.0f, sdf.y) / (sdf.y + 1e-8));
}
}
if (mask_inverted) {
Expand Down Expand Up @@ -138,9 +141,13 @@ const char blit_shader_glsl[] = GLSL(330,
uniform int frame_opacity_fscm;
// Signed distance field for rectangle center at (0, 0), with size of
// half_size * 2
float rectangle_sdf(vec2 point, vec2 half_size) {
vec2 d = abs(point) - half_size;
return length(max(d, 0.0));
// Returns 2 number: the distance, and the approximate chord length inside
// the pixel around `point`.
vec2 rectangle_sdf(vec2 point, vec2 half_size) {
vec2 d = max(abs(point) - half_size, 0.0);
float l = length(d);
// Add a small number to avoid 0/0.
return vec2(l, l / (max(d.x, d.y) + 1e-8));
}

vec4 default_post_processing(vec4 c) {
Expand Down Expand Up @@ -175,20 +182,30 @@ const char blit_shader_glsl[] = GLSL(330,
border_color.rgb = border_color.rgb * (max_brightness / brightness);
}

// Rim color is the color of the outer rim of the window, if there is no
// border, it's the color of the window itself, otherwise it's the border.
// Using mix() to avoid a branch here.
vec4 rim_color = mix(c, border_color, clamp(border_width, 0.0f, 1.0f));

vec2 outer_size = effective_size;
vec2 inner_size = outer_size - vec2(corner_radius) * 2.0f - 1;
float rect_distance = rectangle_sdf(texcoord - outer_size / 2.0f,
inner_size / 2.0f) - corner_radius;
if (rect_distance > 0.0f) {
c = (1.0f - clamp(rect_distance, 0.0f, 1.0f)) * rim_color;
} else {
float factor = clamp(rect_distance + border_width, 0.0f, 1.0f);
c = (1.0f - factor) * c + factor * border_color;
if (corner_radius != 0) {
// Rim color is the color of the outer rim of the window, if there is no
// border, it's the color of the window itself, otherwise it's the border.
// Using mix() to avoid a branch here.
vec4 rim_color = mix(c, border_color, clamp(border_width, 0.0f, 1.0f));

vec2 outer_size = effective_size;
vec2 inner_size = outer_size - vec2(corner_radius) * 2.0f;
vec2 sdf = rectangle_sdf(texcoord - outer_size / 2.0f,
inner_size / 2.0f);
// For anti-aliasing, we estimate how much of the pixel is covered by the rounded
// rectangle. This differs depends on at what angle the circle sweeps through the
// pixel. e.g. if it goes from corner to corner, then the coverage goes from 0 to
// 1 when the distance goes from -sqrt(2)/2 to +sqrt(2)/2; if it goes from egde to
// edge, then the coverage goes from 0 to 1 when the distance goes from -0.5 to 0.5.
// The chord length returned by `rectangle_sdf` is an approximation of this.
float rect_distance = sdf.x - corner_radius + sdf.y / 2.0f;
// Add a small number to sdf.y to avoid 0/0
if (rect_distance > 0.0f) {
c = (1.0f - clamp(rect_distance, 0.0f, sdf.y) / (sdf.y + 1e-8)) * rim_color;
} else {
float factor = clamp(rect_distance + border_width, 0.0f, sdf.y) / (sdf.y + 1e-8);
c = (1.0f - factor) * c + factor * border_color;
}
}

return c;
Expand Down
2 changes: 0 additions & 2 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -639,8 +639,6 @@ bool load_plugin(const char *name, const char *include_dir) {
return handle != NULL;
}

struct shader_info null_shader = {0};

bool parse_config(options_t *opt, const char *config_file) {
// clang-format off
*opt = (struct options){
Expand Down
23 changes: 13 additions & 10 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ enum vblank_scheduler_type {
};

enum animation_trigger {
ANIMATION_TRIGGER_INVALID = -1,
/// When a hidden window is shown
ANIMATION_TRIGGER_SHOW = 0,
/// When a window is hidden
Expand All @@ -87,7 +86,11 @@ enum animation_trigger {
ANIMATION_TRIGGER_WORKSPACE_IN_INVERSE,
ANIMATION_TRIGGER_WORKSPACE_OUT,
ANIMATION_TRIGGER_WORKSPACE_OUT_INVERSE,
ANIMATION_TRIGGER_LAST = ANIMATION_TRIGGER_WORKSPACE_OUT_INVERSE,
/// When a window's geometry changes
ANIMATION_TRIGGER_GEOMETRY,

ANIMATION_TRIGGER_INVALID,
ANIMATION_TRIGGER_COUNT = ANIMATION_TRIGGER_INVALID,
};

static const char *animation_trigger_names[] attr_unused = {
Expand All @@ -101,6 +104,7 @@ static const char *animation_trigger_names[] attr_unused = {
[ANIMATION_TRIGGER_WORKSPACE_IN_INVERSE] = "workspace-in-inverse",
[ANIMATION_TRIGGER_WORKSPACE_OUT] = "workspace-out",
[ANIMATION_TRIGGER_WORKSPACE_OUT_INVERSE] = "workspace-out-inverse",
[ANIMATION_TRIGGER_GEOMETRY] = "geometry",
};

struct script;
Expand Down Expand Up @@ -177,8 +181,8 @@ struct window_maybe_options {
/// Window dim level, NaN means not set.
double dim;

/// Name of the custom fragment shader for this window. NULL means not set.
const struct shader_info *shader;
/// The name of the custom fragment shader for this window. NULL means not set.
const char *shader;

/// Whether transparent clipping is excluded by the rules.
enum tristate transparent_clipping;
Expand All @@ -200,7 +204,7 @@ struct window_maybe_options {
enum tristate full_shadow;

/// Window specific animations
struct win_script animations[ANIMATION_TRIGGER_LAST + 1];
struct win_script animations[ANIMATION_TRIGGER_COUNT];
};

// Make sure `window_options` has no implicit padding.
Expand All @@ -210,7 +214,7 @@ struct window_maybe_options {
struct window_options {
double opacity;
double dim;
const struct shader_info *shader;
const char *shader;
unsigned int corner_radius;
enum window_unredir_option unredir;
bool transparent_clipping;
Expand All @@ -222,7 +226,7 @@ struct window_options {
bool paint;
bool full_shadow;

struct win_script animations[ANIMATION_TRIGGER_LAST + 1];
struct win_script animations[ANIMATION_TRIGGER_COUNT];
};
#pragma GCC diagnostic pop

Expand All @@ -233,8 +237,6 @@ win_options_no_damage(const struct window_options *a, const struct window_option
return memcmp(a, b, offsetof(struct window_options, animations)) == 0;
}

extern struct shader_info null_shader;

/// Structure representing all options.
typedef struct options {
// === Config ===
Expand Down Expand Up @@ -446,7 +448,7 @@ typedef struct options {

bool dithered_present;
// === Animation ===
struct win_script animations[ANIMATION_TRIGGER_LAST + 1];
struct win_script animations[ANIMATION_TRIGGER_COUNT];
/// Array of all the scripts used in `animations`. This is a dynarr.
struct script **all_scripts;

Expand Down Expand Up @@ -535,5 +537,6 @@ static inline void log_warn_both_style_of_rules(const char *option_name) {
"precedence, and \"%s\" will have no effect.",
option_name, option_name);
}
enum animation_trigger parse_animation_trigger(const char *trigger);

// vim: set noet sw=8 ts=8 :
16 changes: 13 additions & 3 deletions src/config_libconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,8 @@ static inline void parse_wintype_config(const config_t *cfg, const char *member_
}
}

static enum animation_trigger parse_animation_trigger(const char *trigger) {
for (int i = 0; i <= ANIMATION_TRIGGER_LAST; i++) {
enum animation_trigger parse_animation_trigger(const char *trigger) {
for (unsigned i = 0; i < ANIMATION_TRIGGER_COUNT; i++) {
if (strcasecmp(trigger, animation_trigger_names[i]) == 0) {
return i;
}
Expand Down Expand Up @@ -297,7 +297,7 @@ static bool parse_animation_one(struct win_script *animations,
}
auto number_of_triggers =
config_setting_get_string(triggers) == NULL ? config_setting_length(triggers) : 1;
if (number_of_triggers > ANIMATION_TRIGGER_LAST) {
if (number_of_triggers > ANIMATION_TRIGGER_COUNT) {
log_error("Too many triggers in animation defined at line %d",
config_setting_source_line(triggers));
return false;
Expand Down Expand Up @@ -627,6 +627,8 @@ parse_rule(struct list_node *rules, config_setting_t *setting, struct script ***
if (animations) {
parse_animations(wopts->animations, animations, out_scripts);
}

config_setting_lookup_string(setting, "shader", &wopts->shader);
return rule;
}

Expand Down Expand Up @@ -764,6 +766,14 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) {
config_setting_t *rules = config_lookup(&cfg, "rules");
if (rules) {
parse_rules(&opt->rules, rules, &opt->all_scripts);
c2_condition_list_foreach(&opt->rules, i) {
auto data = (struct window_maybe_options *)c2_condition_get_data(i);
if (data->shader == NULL) {
continue;
}
data->shader = locate_auxiliary_file(
"shaders", data->shader, config_get_include_dir(&cfg));
}
}

// --dbus
Expand Down
Loading

0 comments on commit e5aaa1a

Please sign in to comment.