Skip to content

Commit

Permalink
Merge pull request #1310 from yshui/animation-geometry
Browse files Browse the repository at this point in the history
Animation part 6 extra: geometry trigger
  • Loading branch information
yshui authored Aug 12, 2024
2 parents 3df599c + 34ecca5 commit 5f24750
Show file tree
Hide file tree
Showing 19 changed files with 704 additions and 79 deletions.
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 @@ 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
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 @@ cdbus_process_list_win(session_t *ps, DBusMessage *msg attr_unused, DBusMessage
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 @@ static DBusHandlerResult cdbus_process_introspect(DBusMessage *reply) {
}
///@}

/**
* 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 @@ static bool cdbus_process_window_introspect(DBusMessage *reply) {
" <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 @@ cdbus_process_windows(DBusConnection *conn, DBusMessage *msg, void *ud) {
"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;
}
auto trigger = parse_animation_trigger(trigger_str);
if (trigger == ANIMATION_TRIGGER_INVALID) {
dbus_set_error(&err, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S,
trigger_str);
goto finished;
}
auto cursor = wm_find(ps->wm, wid);
if (cursor == NULL) {
dbus_set_error(&err, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid);
goto finished;
}
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;
}
count = w->animation_block[trigger];
}
if (reply != NULL && !cdbus_append_uint32(reply, count)) {
ret = DBUS_HANDLER_RESULT_NEED_MEMORY;
}
} 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 @@ void inspect_dump_window_maybe_options(struct window_maybe_options wopts) {
}

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++) {
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 @@ static void handle_pending_updates(struct session *ps, double delta_t) {
// 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 @@ static void handle_pending_updates(struct session *ps, double delta_t) {
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,
w->saved_win_image);
w->saved_win_image = NULL;
}
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

0 comments on commit 5f24750

Please sign in to comment.