Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Animation part 6 extra: geometry trigger #1310

Merged
merged 9 commits into from
Aug 12, 2024
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
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 @@ -183,6 +183,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
28 changes: 28 additions & 0 deletions 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,6 +739,8 @@ 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 before the animation is triggered. This is only meaningfully different from the normal window geometry variables for 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.
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 @@
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 @@
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";

Check warning on line 128 in src/backend/backend.c

View check run for this annotation

Codecov / codecov/patch

src/backend/backend.c#L128

Added line #L128 was not covered by tests
case BACKEND_COMMAND_SOURCE_SHADOW: return "shadow";
case BACKEND_COMMAND_SOURCE_BACKGROUND: return "background";
}
Expand Down
15 changes: 10 additions & 5 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 @@ -83,7 +82,11 @@ enum animation_trigger {
ANIMATION_TRIGGER_OPEN,
/// When a window is closed
ANIMATION_TRIGGER_CLOSE,
ANIMATION_TRIGGER_LAST = ANIMATION_TRIGGER_CLOSE,
/// 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 @@ -93,6 +96,7 @@ static const char *animation_trigger_names[] attr_unused = {
[ANIMATION_TRIGGER_DECREASE_OPACITY] = "decrease-opacity",
[ANIMATION_TRIGGER_OPEN] = "open",
[ANIMATION_TRIGGER_CLOSE] = "close",
[ANIMATION_TRIGGER_GEOMETRY] = "geometry",
};

struct script;
Expand Down Expand Up @@ -192,7 +196,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 @@ -214,7 +218,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 Down Expand Up @@ -432,7 +436,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 @@ -521,5 +525,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 :
6 changes: 3 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
46 changes: 40 additions & 6 deletions src/dbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -555,9 +555,7 @@
return DBUS_HANDLER_RESULT_HANDLED;
}

/**
* Process a win_get D-Bus request.
*/
/// Process a property Get D-Bus request.
static DBusHandlerResult
cdbus_process_window_property_get(session_t *ps, DBusMessage *msg, cdbus_window_t wid,
DBusMessage *reply, DBusError *e) {
Expand Down Expand Up @@ -1125,9 +1123,7 @@
}
///@}

/**
* Process an D-Bus Introspect request, for /windows.
*/
/// Process an D-Bus Introspect request, for /windows.
static DBusHandlerResult
cdbus_process_windows_root_introspect(session_t *ps, DBusMessage *reply) {
static const char *str_introspect =
Expand Down Expand Up @@ -1209,6 +1205,11 @@
" <property type='b' name='Mapped' access='read'/>\n"
" <property type='s' name='Name' access='read'/>\n"
" <property type='as' name='Type' access='read'/>\n"
" <method name='BlockUnblockAnimation'>\n"
" <arg type='s' name='trigger' direction='in'/>\n"
" <arg type='b' name='block' direction='in'/>\n"
" <arg type='u' name='count' direction='out'/>\n"
" </method>\n"
" </interface>\n"
"</node>\n";
// clang-format on
Expand Down Expand Up @@ -1424,6 +1425,39 @@
"Unexpected member \"%s\" of dbus properties interface.", member);
dbus_set_error_const(&err, DBUS_ERROR_UNKNOWN_METHOD, NULL);
}
} else if (strcmp(interface, PICOM_WINDOW_INTERFACE) == 0 &&
strcmp(member, "BlockUnblockAnimation") == 0) {
bool block = false;
const char *trigger_str = NULL;
if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &trigger_str) ||
!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &block)) {
dbus_set_error_const(&err, DBUS_ERROR_INVALID_ARGS, NULL);
goto finished;

Check warning on line 1435 in src/dbus.c

View check run for this annotation

Codecov / codecov/patch

src/dbus.c#L1428-L1435

Added lines #L1428 - L1435 were not covered by tests
}
auto trigger = parse_animation_trigger(trigger_str);
if (trigger == ANIMATION_TRIGGER_INVALID) {
dbus_set_error(&err, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S,

Check warning on line 1439 in src/dbus.c

View check run for this annotation

Codecov / codecov/patch

src/dbus.c#L1437-L1439

Added lines #L1437 - L1439 were not covered by tests
trigger_str);
goto finished;

Check warning on line 1441 in src/dbus.c

View check run for this annotation

Codecov / codecov/patch

src/dbus.c#L1441

Added line #L1441 was not covered by tests
}
auto cursor = wm_find(ps->wm, wid);
if (cursor == NULL) {
dbus_set_error(&err, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid);
goto finished;

Check warning on line 1446 in src/dbus.c

View check run for this annotation

Codecov / codecov/patch

src/dbus.c#L1443-L1446

Added lines #L1443 - L1446 were not covered by tests
}
auto w = wm_ref_deref(cursor);
unsigned count = 0;
if (w != NULL) {
if (block) {
w->animation_block[trigger] += 1;
} else if (w->animation_block[trigger] > 0) {
w->animation_block[trigger] -= 1;

Check warning on line 1454 in src/dbus.c

View check run for this annotation

Codecov / codecov/patch

src/dbus.c#L1448-L1454

Added lines #L1448 - L1454 were not covered by tests
}
count = w->animation_block[trigger];

Check warning on line 1456 in src/dbus.c

View check run for this annotation

Codecov / codecov/patch

src/dbus.c#L1456

Added line #L1456 was not covered by tests
}
if (reply != NULL && !cdbus_append_uint32(reply, count)) {
ret = DBUS_HANDLER_RESULT_NEED_MEMORY;

Check warning on line 1459 in src/dbus.c

View check run for this annotation

Codecov / codecov/patch

src/dbus.c#L1458-L1459

Added lines #L1458 - L1459 were not covered by tests
}
} else {
log_debug("Illegal message of type \"%s\", path \"%s\" "
"interface \"%s\", member \"%s\"",
Expand Down
2 changes: 1 addition & 1 deletion src/inspect.c
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@
}

char **animation_triggers = dynarr_new(char *, 0);
for (int i = 0; i <= ANIMATION_TRIGGER_LAST; i++) {
for (int i = 0; i < ANIMATION_TRIGGER_COUNT; i++) {

Check warning on line 221 in src/inspect.c

View check run for this annotation

Codecov / codecov/patch

src/inspect.c#L221

Added line #L221 was not covered by tests
if (wopts.animations[i].script != NULL) {
char *name = NULL;
casprintf(&name, "\"%s\"", animation_trigger_names[i]);
Expand Down
15 changes: 10 additions & 5 deletions src/picom.c
Original file line number Diff line number Diff line change
Expand Up @@ -1649,13 +1649,9 @@
// Process window flags. This needs to happen before `refresh_images`
// because this might set the pixmap stale window flag.
refresh_windows(ps);
ps->pending_updates = false;
}

// Process window flags (stale images)
refresh_images(ps);

ps->pending_updates = false;

wm_stack_foreach_safe(ps->wm, cursor, tmp) {
auto w = wm_ref_deref(cursor);
BUG_ON(w != NULL && w->tree_ref != cursor);
Expand All @@ -1665,13 +1661,22 @@
free(w->running_animation_instance);
w->running_animation_instance = NULL;
w->in_openclose = false;
if (w->saved_win_image != NULL) {
ps->backend_data->ops.release_image(ps->backend_data,

Check warning on line 1665 in src/picom.c

View check run for this annotation

Codecov / codecov/patch

src/picom.c#L1665

Added line #L1665 was not covered by tests
w->saved_win_image);
w->saved_win_image = NULL;

Check warning on line 1667 in src/picom.c

View check run for this annotation

Codecov / codecov/patch

src/picom.c#L1667

Added line #L1667 was not covered by tests
}
if (w->state == WSTATE_UNMAPPED) {
unmap_win_finish(ps, w);
} else if (w->state == WSTATE_DESTROYED) {
win_destroy_finish(ps, w);
}
}
}

// Process window flags (stale images)
refresh_images(ps);
assert(ps->pending_updates == false);
}

/**
Expand Down
Loading