Skip to content

Commit 5f24750

Browse files
authored
Merge pull request #1310 from yshui/animation-geometry
Animation part 6 extra: geometry trigger
2 parents 3df599c + 34ecca5 commit 5f24750

19 files changed

+704
-79
lines changed

assets/geometry-change.mp4

122 KB
Binary file not shown.

data/animation_presets.conf

+40-1
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,43 @@ fly-in = {
162162
(2, "direction", [-1, 1, 0, 0]),
163163
(3, "direction", [0, 0, 1, 1]),
164164
);
165-
}
165+
};
166+
geometry-change = {
167+
scale-x = {
168+
curve = "cubic-bezier(0.07, 0.65, 0, 1)";
169+
duration = "placeholder0";
170+
start = "window-width-before / window-width";
171+
end = 1;
172+
};
173+
scale-y = {
174+
curve = "cubic-bezier(0.07, 0.65, 0, 1)";
175+
duration = "placeholder0";
176+
start = "window-height-before / window-height";
177+
end = 1;
178+
};
179+
shadow-scale-x = "scale-x";
180+
shadow-scale-y = "scale-y";
181+
offset-x = {
182+
curve = "cubic-bezier(0.07, 0.65, 0, 1)";
183+
duration = "placeholder0";
184+
start = "window-x-before - window-x";
185+
end = 0;
186+
};
187+
offset-y = {
188+
curve = "cubic-bezier(0.07, 0.65, 0, 1)";
189+
duration = "placeholder0";
190+
start = "window-y-before - window-y";
191+
end = 0;
192+
};
193+
saved-image-blend = {
194+
duration = "placeholder0";
195+
start = 1;
196+
end = 0;
197+
};
198+
shadow-offset-x = "offset-x";
199+
shadow-offset-y = "offset-y";
200+
*knobs = {
201+
duration = 0.4;
202+
};
203+
*placeholders = ((0, "duration"));
204+
};

include/picom/backend.h

+1
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ enum backend_command_op {
183183
/// will later be filled in by the renderer using this symbolic reference.
184184
enum backend_command_source {
185185
BACKEND_COMMAND_SOURCE_WINDOW,
186+
BACKEND_COMMAND_SOURCE_WINDOW_SAVED,
186187
BACKEND_COMMAND_SOURCE_SHADOW,
187188
BACKEND_COMMAND_SOURCE_BACKGROUND,
188189
};

man/picom.1.adoc

+28
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,10 @@ animations = ({
515515

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

518+
[[trigger-geometry]]_geometry_:: When the geometry of a window is changed. (EXPERIMENTAL)
519+
+
520+
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.
521+
518522
_suppressions_:::
519523
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.
520524

@@ -594,6 +598,24 @@ endif::[]
594598

595599
_duration_:: Duration of the animation in seconds.
596600
--
601+
+
602+
_geometry-change_:::
603+
+
604+
Animate the geometry (i.e. size and position) change of the window.
605+
+
606+
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.
607+
+
608+
--
609+
ifdef::env-web[]
610+
video::assets/geometry-change.mp4[width=400]
611+
endif::[]
612+
--
613+
+
614+
--
615+
*Options*:::
616+
617+
_duration_:: Duration of the animation in seconds.
618+
--
597619

598620
=== Advanced
599621

@@ -693,6 +715,10 @@ Currently, these output variables are supported: :::
693715

694716
_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.
695717

718+
[[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)
719+
+
720+
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.
721+
696722
All coordinates are in pixels, and are in the coordinate system of the screen. Sizes are also in pixels.
697723

698724
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.
@@ -713,6 +739,8 @@ Currently, these context variables are defined: :::
713739

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

742+
_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.
743+
716744
_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.
717745

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

src/backend/backend.c

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "config.h"
1212
#include "log.h"
1313
#include "region.h"
14+
#include "renderer/layout.h"
1415
#include "wm/win.h"
1516
#include "x.h"
1617

@@ -93,6 +94,9 @@ bool backend_execute(struct backend_base *backend, image_handle target, unsigned
9394
if (!pixman_region32_not_empty(cmd->blit.target_mask)) {
9495
continue;
9596
}
97+
if (cmd->blit.opacity < 1. / MAX_ALPHA) {
98+
continue;
99+
}
96100
succeeded =
97101
backend->ops.blit(backend, cmd->origin, target, &cmd->blit);
98102
break;
@@ -121,6 +125,7 @@ bool backend_execute(struct backend_base *backend, image_handle target, unsigned
121125
static inline const char *render_command_source_name(enum backend_command_source source) {
122126
switch (source) {
123127
case BACKEND_COMMAND_SOURCE_WINDOW: return "window";
128+
case BACKEND_COMMAND_SOURCE_WINDOW_SAVED: return "window_saved";
124129
case BACKEND_COMMAND_SOURCE_SHADOW: return "shadow";
125130
case BACKEND_COMMAND_SOURCE_BACKGROUND: return "background";
126131
}

src/config.h

+10-5
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ enum vblank_scheduler_type {
7070
};
7171

7272
enum animation_trigger {
73-
ANIMATION_TRIGGER_INVALID = -1,
7473
/// When a hidden window is shown
7574
ANIMATION_TRIGGER_SHOW = 0,
7675
/// When a window is hidden
@@ -83,7 +82,11 @@ enum animation_trigger {
8382
ANIMATION_TRIGGER_OPEN,
8483
/// When a window is closed
8584
ANIMATION_TRIGGER_CLOSE,
86-
ANIMATION_TRIGGER_LAST = ANIMATION_TRIGGER_CLOSE,
85+
/// When a window's geometry changes
86+
ANIMATION_TRIGGER_GEOMETRY,
87+
88+
ANIMATION_TRIGGER_INVALID,
89+
ANIMATION_TRIGGER_COUNT = ANIMATION_TRIGGER_INVALID,
8790
};
8891

8992
static const char *animation_trigger_names[] attr_unused = {
@@ -93,6 +96,7 @@ static const char *animation_trigger_names[] attr_unused = {
9396
[ANIMATION_TRIGGER_DECREASE_OPACITY] = "decrease-opacity",
9497
[ANIMATION_TRIGGER_OPEN] = "open",
9598
[ANIMATION_TRIGGER_CLOSE] = "close",
99+
[ANIMATION_TRIGGER_GEOMETRY] = "geometry",
96100
};
97101

98102
struct script;
@@ -192,7 +196,7 @@ struct window_maybe_options {
192196
enum tristate full_shadow;
193197

194198
/// Window specific animations
195-
struct win_script animations[ANIMATION_TRIGGER_LAST + 1];
199+
struct win_script animations[ANIMATION_TRIGGER_COUNT];
196200
};
197201

198202
// Make sure `window_options` has no implicit padding.
@@ -214,7 +218,7 @@ struct window_options {
214218
bool paint;
215219
bool full_shadow;
216220

217-
struct win_script animations[ANIMATION_TRIGGER_LAST + 1];
221+
struct win_script animations[ANIMATION_TRIGGER_COUNT];
218222
};
219223
#pragma GCC diagnostic pop
220224

@@ -432,7 +436,7 @@ typedef struct options {
432436

433437
bool dithered_present;
434438
// === Animation ===
435-
struct win_script animations[ANIMATION_TRIGGER_LAST + 1];
439+
struct win_script animations[ANIMATION_TRIGGER_COUNT];
436440
/// Array of all the scripts used in `animations`. This is a dynarr.
437441
struct script **all_scripts;
438442

@@ -521,5 +525,6 @@ static inline void log_warn_both_style_of_rules(const char *option_name) {
521525
"precedence, and \"%s\" will have no effect.",
522526
option_name, option_name);
523527
}
528+
enum animation_trigger parse_animation_trigger(const char *trigger);
524529

525530
// vim: set noet sw=8 ts=8 :

src/config_libconfig.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,8 @@ static inline void parse_wintype_config(const config_t *cfg, const char *member_
224224
}
225225
}
226226

227-
static enum animation_trigger parse_animation_trigger(const char *trigger) {
228-
for (int i = 0; i <= ANIMATION_TRIGGER_LAST; i++) {
227+
enum animation_trigger parse_animation_trigger(const char *trigger) {
228+
for (unsigned i = 0; i < ANIMATION_TRIGGER_COUNT; i++) {
229229
if (strcasecmp(trigger, animation_trigger_names[i]) == 0) {
230230
return i;
231231
}
@@ -297,7 +297,7 @@ static bool parse_animation_one(struct win_script *animations,
297297
}
298298
auto number_of_triggers =
299299
config_setting_get_string(triggers) == NULL ? config_setting_length(triggers) : 1;
300-
if (number_of_triggers > ANIMATION_TRIGGER_LAST) {
300+
if (number_of_triggers > ANIMATION_TRIGGER_COUNT) {
301301
log_error("Too many triggers in animation defined at line %d",
302302
config_setting_source_line(triggers));
303303
return false;

src/dbus.c

+40-6
Original file line numberDiff line numberDiff line change
@@ -555,9 +555,7 @@ cdbus_process_list_win(session_t *ps, DBusMessage *msg attr_unused, DBusMessage
555555
return DBUS_HANDLER_RESULT_HANDLED;
556556
}
557557

558-
/**
559-
* Process a win_get D-Bus request.
560-
*/
558+
/// Process a property Get D-Bus request.
561559
static DBusHandlerResult
562560
cdbus_process_window_property_get(session_t *ps, DBusMessage *msg, cdbus_window_t wid,
563561
DBusMessage *reply, DBusError *e) {
@@ -1125,9 +1123,7 @@ static DBusHandlerResult cdbus_process_introspect(DBusMessage *reply) {
11251123
}
11261124
///@}
11271125

1128-
/**
1129-
* Process an D-Bus Introspect request, for /windows.
1130-
*/
1126+
/// Process an D-Bus Introspect request, for /windows.
11311127
static DBusHandlerResult
11321128
cdbus_process_windows_root_introspect(session_t *ps, DBusMessage *reply) {
11331129
static const char *str_introspect =
@@ -1209,6 +1205,11 @@ static bool cdbus_process_window_introspect(DBusMessage *reply) {
12091205
" <property type='b' name='Mapped' access='read'/>\n"
12101206
" <property type='s' name='Name' access='read'/>\n"
12111207
" <property type='as' name='Type' access='read'/>\n"
1208+
" <method name='BlockUnblockAnimation'>\n"
1209+
" <arg type='s' name='trigger' direction='in'/>\n"
1210+
" <arg type='b' name='block' direction='in'/>\n"
1211+
" <arg type='u' name='count' direction='out'/>\n"
1212+
" </method>\n"
12121213
" </interface>\n"
12131214
"</node>\n";
12141215
// clang-format on
@@ -1424,6 +1425,39 @@ cdbus_process_windows(DBusConnection *conn, DBusMessage *msg, void *ud) {
14241425
"Unexpected member \"%s\" of dbus properties interface.", member);
14251426
dbus_set_error_const(&err, DBUS_ERROR_UNKNOWN_METHOD, NULL);
14261427
}
1428+
} else if (strcmp(interface, PICOM_WINDOW_INTERFACE) == 0 &&
1429+
strcmp(member, "BlockUnblockAnimation") == 0) {
1430+
bool block = false;
1431+
const char *trigger_str = NULL;
1432+
if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &trigger_str) ||
1433+
!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &block)) {
1434+
dbus_set_error_const(&err, DBUS_ERROR_INVALID_ARGS, NULL);
1435+
goto finished;
1436+
}
1437+
auto trigger = parse_animation_trigger(trigger_str);
1438+
if (trigger == ANIMATION_TRIGGER_INVALID) {
1439+
dbus_set_error(&err, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S,
1440+
trigger_str);
1441+
goto finished;
1442+
}
1443+
auto cursor = wm_find(ps->wm, wid);
1444+
if (cursor == NULL) {
1445+
dbus_set_error(&err, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid);
1446+
goto finished;
1447+
}
1448+
auto w = wm_ref_deref(cursor);
1449+
unsigned count = 0;
1450+
if (w != NULL) {
1451+
if (block) {
1452+
w->animation_block[trigger] += 1;
1453+
} else if (w->animation_block[trigger] > 0) {
1454+
w->animation_block[trigger] -= 1;
1455+
}
1456+
count = w->animation_block[trigger];
1457+
}
1458+
if (reply != NULL && !cdbus_append_uint32(reply, count)) {
1459+
ret = DBUS_HANDLER_RESULT_NEED_MEMORY;
1460+
}
14271461
} else {
14281462
log_debug("Illegal message of type \"%s\", path \"%s\" "
14291463
"interface \"%s\", member \"%s\"",

src/inspect.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ void inspect_dump_window_maybe_options(struct window_maybe_options wopts) {
218218
}
219219

220220
char **animation_triggers = dynarr_new(char *, 0);
221-
for (int i = 0; i <= ANIMATION_TRIGGER_LAST; i++) {
221+
for (int i = 0; i < ANIMATION_TRIGGER_COUNT; i++) {
222222
if (wopts.animations[i].script != NULL) {
223223
char *name = NULL;
224224
casprintf(&name, "\"%s\"", animation_trigger_names[i]);

src/picom.c

+10-5
Original file line numberDiff line numberDiff line change
@@ -1649,13 +1649,9 @@ static void handle_pending_updates(struct session *ps, double delta_t) {
16491649
// Process window flags. This needs to happen before `refresh_images`
16501650
// because this might set the pixmap stale window flag.
16511651
refresh_windows(ps);
1652+
ps->pending_updates = false;
16521653
}
16531654

1654-
// Process window flags (stale images)
1655-
refresh_images(ps);
1656-
1657-
ps->pending_updates = false;
1658-
16591655
wm_stack_foreach_safe(ps->wm, cursor, tmp) {
16601656
auto w = wm_ref_deref(cursor);
16611657
BUG_ON(w != NULL && w->tree_ref != cursor);
@@ -1665,13 +1661,22 @@ static void handle_pending_updates(struct session *ps, double delta_t) {
16651661
free(w->running_animation_instance);
16661662
w->running_animation_instance = NULL;
16671663
w->in_openclose = false;
1664+
if (w->saved_win_image != NULL) {
1665+
ps->backend_data->ops.release_image(ps->backend_data,
1666+
w->saved_win_image);
1667+
w->saved_win_image = NULL;
1668+
}
16681669
if (w->state == WSTATE_UNMAPPED) {
16691670
unmap_win_finish(ps, w);
16701671
} else if (w->state == WSTATE_DESTROYED) {
16711672
win_destroy_finish(ps, w);
16721673
}
16731674
}
16741675
}
1676+
1677+
// Process window flags (stale images)
1678+
refresh_images(ps);
1679+
assert(ps->pending_updates == false);
16751680
}
16761681

16771682
/**

0 commit comments

Comments
 (0)