speedie-overlay/x11-misc/picom-animations/files/transitions.diff
2022-11-11 15:48:06 +01:00

683 lines
20 KiB
Diff

diff --git a/picom.sample.conf b/picom.sample.conf
index 777cf0d0..b7b10626 100644
--- a/picom.sample.conf
+++ b/picom.sample.conf
@@ -1,3 +1,42 @@
+#################################
+# Transitions #
+#################################
+
+# When windows get moved or resized it transitions window position
+transition = true;
+
+# How many pixels move window to make the first position in transition (defaults to 20)
+transition-offset = 20;
+
+# Direction of transition (top, right, bottom, left) e.g: "right" direction will make
+# all windows come from right to left
+
+# (smart-x, smart-y) are smart direction that will check if there are
+# multiple windows that splits the screen and will change their directions,
+# in "smart-x" it changes direction of left window to "right" and direction of
+# right window to "left", if screen is not splited and a window is taking
+# a lot of screen it will change that window direction to "left".
+# "smart-y" is also exactly like "smart-x" but instead of translating directions to
+# "right" and "left", it translate to "top" and "bottom"
+transition-direction = "smart-x";
+
+# Function that calculates new position of window (defaults to "ease-out-cubic")
+# see https://easings.net for list of all functions
+# naming conventions are different to that site tho, e.g "easeInSine" is listed
+# on site but here that translated to "ease-in-sine"
+transition-timing-function = "ease-out-cubic";
+
+# Time between frames in transition. (0.01 - 1.0, defaults to 0.028)
+transition-step = 0.028;
+
+# Similar to opacity rules but determites transition direction e.g:
+# "right: name *= 'Firefox'" will make firefox transition direction to right
+# Specify a list of transition rules, in the format `DIRECTION:PATTERN`
+
+# for disabling transition on specific patterns use "none" keyword as a direction
+# e.g: use "none: window_type = 'popup_menu'" for disabling transitions on popup menus
+transition-rule = [];
+
#################################
# Shadows #
#################################
diff --git a/src/config.c b/src/config.c
index 90324778..ae400c1b 100644
--- a/src/config.c
+++ b/src/config.c
@@ -579,6 +579,11 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
.track_leader = false,
+ .transition_offset = 20,
+ .transition_direction = 0,
+ .transition_step = 0.028,
+ .transition_timing_function = ease_out_cubic,
+
.rounded_corners_blacklist = NULL
};
// clang-format on
diff --git a/src/config.h b/src/config.h
index 03ef74b3..4cefb4c1 100644
--- a/src/config.h
+++ b/src/config.h
@@ -26,6 +26,8 @@
#include "types.h"
#include "win_defs.h"
+#include "timing_functions.h"
+
typedef struct session session_t;
/// @brief Possible backends
@@ -68,6 +70,18 @@ enum blur_method {
BLUR_METHOD_INVALID,
};
+enum transition_direction {
+ TRANSITION_DIR_NONE = 0,
+ TRANSITION_DIR_LEFT,
+ TRANSITION_DIR_BOTTOM,
+ TRANSITION_DIR_RIGHT,
+ TRANSITION_DIR_TOP,
+ TRANSITION_DIR_SMART_X,
+ TRANSITION_DIR_SMART_Y,
+};
+
+typedef double (*timing_function)(double);
+
typedef struct _c2_lptr c2_lptr_t;
/// Structure representing all options.
@@ -246,6 +260,22 @@ typedef struct options {
// Make transparent windows clip other windows, instead of blending on top of
// them
bool transparent_clipping;
+
+ // === Transition ===
+ // How many pixels move window to make the first position in transition
+ int transition_offset;
+
+ // Direction of transition
+ enum transition_direction transition_direction;
+
+ // Rules to change window transition
+ c2_lptr_t *transition_rules;
+
+ // Function that calculate new position
+ timing_function transition_timing_function;
+
+ // Time between frames in transition
+ double transition_step;
} options_t;
extern const char *const BACKEND_STRS[NUM_BKEND + 1];
diff --git a/src/config_libconfig.c b/src/config_libconfig.c
index e9818ebc..60e72a86 100644
--- a/src/config_libconfig.c
+++ b/src/config_libconfig.c
@@ -295,6 +295,90 @@ static inline void parse_wintype_config(const config_t *cfg, const char *member_
}
}
+enum transition_direction parse_transition_direction(const char *direction) {
+ static const char *names[] = {"none", "left", "bottom", "right",
+ "top", "smart-x", "smart-y"};
+
+ for (unsigned int i = 0; i < sizeof(names) / sizeof(char *); i++) {
+ if (strcmp(direction, names[i]) == 0) {
+ return i;
+ }
+ }
+
+ log_error("'%s' is not a valid transition direction.", direction);
+ return TRANSITION_DIR_NONE;
+}
+
+timing_function parse_timing_function(const char *timing_name) {
+ // clang-format off
+ static const char *names[] = {
+ "sine", "cubic", "quint", "circ", "elastic",
+ "quad", "quart", "etpo", "back", "bounce"
+ };
+
+ static const char *prefixes[] = {"in", "out", "in-out"};
+
+ static timing_function functions[] = {
+ ease_in_sine , ease_out_sine , ease_in_out_sine ,
+ ease_in_cubic , ease_out_cubic , ease_in_out_cubic ,
+ ease_in_quint , ease_out_quint , ease_in_out_quint ,
+ ease_in_circ , ease_out_circ , ease_in_out_circ ,
+ ease_in_elastic, ease_out_elastic, ease_in_out_elastic,
+ ease_in_quad , ease_out_quad , ease_in_out_quad ,
+ ease_in_quart , ease_out_quart , ease_in_out_quart ,
+ ease_in_etpo , ease_out_etpo , ease_in_out_etpo ,
+ ease_in_back , ease_out_back , ease_in_out_back ,
+ ease_in_bounce , ease_out_bounce , ease_in_out_bounce ,
+ };
+ // clang-format on
+
+ char buffer[64];
+ for (unsigned int i = 0; i < sizeof(names) / sizeof(char *); i++) {
+ for (unsigned int p = 0; p < 3; p++) {
+ snprintf(buffer, sizeof(buffer), "ease-%s-%s", prefixes[p], names[i]);
+
+ if (strcmp(buffer, timing_name) == 0) {
+ unsigned int function_index = (i * 3) + p;
+ return functions[function_index];
+ }
+ }
+ }
+
+ log_error("'%s' is not a valid transition timing function.", timing_name);
+ return NULL;
+}
+
+static inline void
+parse_cfg_condlst_trns(options_t *opt, const config_t *pcfg, const char *name) {
+ config_setting_t *setting = config_lookup(pcfg, name);
+ if (setting) {
+ int length = config_setting_length(setting);
+
+ for (int i = 0; i < length; i++) {
+ const char *elem = config_setting_get_string_elem(setting, i);
+
+ char rule[512];
+ unsigned long elem_index = 0;
+
+ for (int rule_index = 0; elem_index < strlen(elem); elem_index++) {
+ char character = elem[elem_index];
+ if (character == ':') {
+ rule[rule_index] = '\0';
+ break;
+ }
+
+ if (!isspace(character)) {
+ rule[rule_index] = character;
+ rule_index++;
+ }
+ }
+
+ int *direction = (int *)parse_transition_direction(rule);
+ c2_parse(&opt->transition_rules, &elem[elem_index + 1], direction);
+ }
+ }
+}
+
/**
* Parse a configuration file from default location.
*
@@ -642,6 +726,28 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
opt->write_pid_path = strdup(sval);
}
+ // Transition
+ if (lcfg_lookup_bool(&cfg, "transition", &bval)) {
+ if (bval) {
+ config_lookup_int(&cfg, "transition-offset", &opt->transition_offset);
+ config_lookup_float(&cfg, "transition-step", &opt->transition_step);
+
+ if (config_lookup_string(&cfg, "transition-direction", &sval)) {
+ opt->transition_direction = parse_transition_direction(sval);
+ }
+
+ if (config_lookup_string(&cfg, "transition-timing-function", &sval)) {
+ timing_function res = parse_timing_function(sval);
+
+ if (res != NULL) {
+ opt->transition_timing_function = res;
+ }
+ }
+
+ parse_cfg_condlst_trns(opt, &cfg, "transition-rule");
+ }
+ }
+
// Wintype settings
// XXX ! Refactor all the wintype_* arrays into a struct
diff --git a/src/event.c b/src/event.c
index e6052f1d..f9ca356a 100644
--- a/src/event.c
+++ b/src/event.c
@@ -186,6 +186,10 @@ static inline void ev_create_notify(session_t *ps, xcb_create_notify_event_t *ev
}
}
+static inline unsigned int distance(int x1, int x2, int y1, int y2) {
+ return (unsigned int)(abs(x2 - x1) + abs(y2 - y1));
+}
+
/// Handle configure event of a regular window
static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
auto w = find_win(ps, ce->window);
@@ -217,6 +221,15 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
// visible/mapped
ps->pending_updates = true;
+ static const unsigned int small_diff = 100;
+ bool small_move =
+ position_changed &&
+ distance(mw->pending_g.x, ce->x, mw->pending_g.y, ce->y) < small_diff;
+
+ bool small_resize =
+ size_changed && distance(mw->pending_g.width, ce->width,
+ mw->pending_g.height, ce->height) < small_diff;
+
// At least one of the following if's is true
if (position_changed) {
log_trace("Window position changed, %dx%d -> %dx%d", mw->g.x,
@@ -235,6 +248,20 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
win_set_flags(mw, WIN_FLAGS_SIZE_STALE);
}
+ if (mw->transition_direction != TRANSITION_DIR_NONE) {
+ // Dont't transition windows that wanna go out of screen
+ if (ce->x >= 0 && ce->x <= ps->root_width) {
+ mw->transition_time = 0.0f;
+ mw->target_geometry = mw->pending_g;
+ } else {
+ mw->transition_time = -1.0f;
+ }
+ }
+
+ if (mw->transition_time != -1.0f && (small_move || small_resize)) {
+ mw->transition_time = -1.0f;
+ }
+
// Recalculate which screen this window is on
win_update_screen(ps->xinerama_nscrs, ps->xinerama_scr_regs, mw);
}
diff --git a/src/meson.build b/src/meson.build
index 0a882f93..341df783 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -9,7 +9,7 @@ base_deps = [
srcs = [ files('picom.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'vsync.c', 'utils.c',
'diagnostic.c', 'string_utils.c', 'render.c', 'kernel.c', 'log.c',
- 'options.c', 'event.c', 'cache.c', 'atom.c', 'file_watch.c') ]
+ 'options.c', 'event.c', 'cache.c', 'atom.c', 'file_watch.c', 'timing_functions.c') ]
picom_inc = include_directories('.')
cflags = []
diff --git a/src/picom.c b/src/picom.c
index 81fb334d..2c9a42ad 100644
--- a/src/picom.c
+++ b/src/picom.c
@@ -680,6 +680,51 @@ static struct managed_win *paint_preprocess(session_t *ps, bool *fade_running) {
if (was_painted && w->mode != mode_old) {
w->reg_ignore_valid = false;
}
+
+ // Transition
+ bool valid_trns_time = w->transition_time >= 0.0f && w->transition_time <= 1.0f;
+ if (w->transition_direction && valid_trns_time) {
+ double transition =
+ ps->o.transition_timing_function(w->transition_time);
+
+ w->transition_time += ps->o.transition_step;
+ if (w->transition_time > 1.0f)
+ transition = 1.0f;
+
+ add_damage_from_win(ps, w);
+ unsigned int direction = w->transition_direction;
+
+ // Smart direction
+ if (direction == TRANSITION_DIR_SMART_X ||
+ direction == TRANSITION_DIR_SMART_Y) {
+
+ bool wide_enough = w->g.width > 80 * ps->root_width / 100;
+ bool bigger_than_half =
+ w->target_geometry.x > ps->root_width / 2;
+
+ /*
+ Not changing transition_direction because
+ smart calculation have to be calculated each time
+ */
+ direction = w->transition_direction -
+ ((bigger_than_half || wide_enough) ? 4 : 2);
+ }
+
+ // Determite we are working on x or y of window
+ int8_t xy = !(direction % 2);
+
+ int16_t xy_target = *(((int16_t *)&w->target_geometry) + xy);
+ int16_t *xy_source = ((int16_t *)&w->g) + xy;
+
+ int8_t sign = (direction - 1) % 3 ? 1 : -1;
+ int start_location = xy_target + sign * ps->o.transition_offset;
+
+ *xy_source = (int16_t)round(
+ transition * (xy_target - start_location) + start_location);
+
+ w->mode = WMODE_TRANS;
+ *fade_running = true;
+ }
}
// Opacity will not change, from now on.
@@ -1804,6 +1849,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
c2_list_postprocess(ps, ps->o.invert_color_list) &&
c2_list_postprocess(ps, ps->o.opacity_rules) &&
c2_list_postprocess(ps, ps->o.rounded_corners_blacklist) &&
+ c2_list_postprocess(ps, ps->o.transition_rules) &&
c2_list_postprocess(ps, ps->o.focus_blacklist))) {
log_error("Post-processing of conditionals failed, some of your rules "
"might not work");
@@ -2174,6 +2220,7 @@ static void session_destroy(session_t *ps) {
free_wincondlst(&ps->o.paint_blacklist);
free_wincondlst(&ps->o.unredir_if_possible_blacklist);
free_wincondlst(&ps->o.rounded_corners_blacklist);
+ free_wincondlst(&ps->o.transition_rules);
// Free tracked atom list
{
diff --git a/src/timing_functions.c b/src/timing_functions.c
new file mode 100644
index 00000000..a6424bd2
--- /dev/null
+++ b/src/timing_functions.c
@@ -0,0 +1,178 @@
+#include "timing_functions.h"
+#include <math.h>
+
+// clang-format off
+double ease_in_sine(double t) {
+ return 1 - cos((t * M_PI) / 2);
+}
+
+double ease_out_sine(double t) {
+ return sin((t * M_PI) / 2);
+}
+
+double ease_in_out_sine(double t) {
+ return -(cos(M_PI * t) - 1) / 2;
+}
+
+double ease_in_cubic(double t) {
+ return t * t * t;
+}
+
+double ease_out_cubic(double t) {
+ return 1 - pow(1 - t, 3);
+}
+
+double ease_in_out_cubic(double t) {
+ return t < 0.5 ? 4 * t * t * t : 1 - pow(-2 * t + 2, 3) / 2;
+}
+
+double ease_in_quint(double t) {
+ return t * t * t * t * t;
+}
+
+double ease_out_quint(double t) {
+ return 1 - pow(1 - t, 5);
+}
+
+double ease_in_out_quint(double t) {
+ return t < 0.5 ? 16 * t * t * t * t * t : 1 - pow(-2 * t + 2, 5) / 2;
+}
+
+double ease_in_circ(double t) {
+ return 1 - sqrt(1 - pow(t, 2));
+}
+
+double ease_out_circ(double t) {
+ return sqrt(1 - pow(t - 1, 2));
+}
+
+double ease_in_out_circ(double t) {
+ return t < 0.5
+ ? (1 - sqrt(1 - pow(2 * t, 2))) / 2
+ : (sqrt(1 - pow(-2 * t + 2, 2)) + 1) / 2;
+}
+
+double ease_in_elastic(double t) {
+ double c4 = (2 * M_PI) / 3;
+
+ return t == 0
+ ? 0
+ : t == 1
+ ? 1
+ : -pow(2, 10 * t - 10) * sin((t * 10 - 10.75) * c4);
+}
+
+double ease_out_elastic(double t) {
+ double c4 = (2 * M_PI) / 3;
+
+ return t == 0
+ ? 0
+ : t == 1
+ ? 1
+ : pow(2, -10 * t) * sin((t * 10 - 0.75) * c4) + 1;
+}
+
+double ease_in_out_elastic(double t) {
+ double c5 = (2 * M_PI) / 4.5;
+
+ return t == 0
+ ? 0
+ : t == 1
+ ? 1
+ : t < 0.5
+ ? -(pow(2, 20 * t - 10) * sin((20 * t - 11.125) * c5)) / 2
+ : (pow(2, -20 * t + 10) * sin((20 * t - 11.125) * c5)) / 2 + 1;
+}
+
+double ease_in_quad(double t) {
+ return t * t;
+}
+
+double ease_out_quad(double t) {
+ return 1 - (1 - t) * (1 - t);
+}
+
+double ease_in_out_quad(double t) {
+ return t < 0.5 ? 2 * t * t : 1 - pow(-2 * t + 2, 2) / 2;
+}
+
+double ease_in_quart(double t) {
+ return t * t * t * t;
+}
+
+double ease_out_quart(double t) {
+ return 1 - pow(1 - t, 4);
+}
+
+double ease_in_out_quart(double t) {
+ return t < 0.5 ? 8 * t * t * t * t : 1 - pow(-2 * t + 2, 4) / 2;
+}
+
+double ease_in_etpo(double t) {
+ return t == 0 ? 0 : pow(2, 10 * t - 10);
+}
+
+double ease_out_etpo(double t) {
+ return t == 1 ? 1 : 1 - pow(2, -10 * t);
+}
+
+double ease_in_out_etpo(double t) {
+ return t == 0
+ ? 0
+ : t == 1
+ ? 1
+ : t < 0.5 ? pow(2, 20 * t - 10) / 2
+ : (2 - pow(2, -20 * t + 10)) / 2;
+}
+
+double ease_in_back(double t) {
+ double c1 = 1.70158;
+ double c3 = c1 + 1;
+
+ return c3 * t * t * t - c1 * t * t;
+}
+
+double ease_out_back(double t) {
+ double c1 = 1.70158;
+ double c3 = c1 + 1;
+
+ return 1 + c3 * pow(t - 1, 3) + c1 * pow(t - 1, 2);
+}
+
+double ease_in_out_back(double t) {
+ double c1 = 1.70158;
+ double c2 = c1 * 1.525;
+
+ return t < 0.5
+ ? (pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2
+ : (pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2;
+}
+
+double ease_in_bounce(double t) {
+ return 1 - ease_out_bounce(1 - t);
+}
+
+double ease_out_bounce(double t) {
+ double n1 = 7.5625;
+ double d1 = 2.75;
+
+ if (t < 1 / d1) {
+ return n1 * t * t;
+ } else if (t < 2 / d1) {
+ t -= 1.5 / d1;
+ return n1 * t * t + 0.75;
+ } else if (t < 2.5 / d1) {
+ t -= 2.25 / d1;
+ return n1 * t * t + 0.9375;
+ } else {
+ t -= 2.625 / d1;
+ return n1 * t * t + 0.984375;
+ }
+}
+
+double ease_in_out_bounce(double t) {
+ return t < 0.5
+ ? (1 - ease_out_bounce(1 - 2 * t)) / 2
+ : (1 + ease_out_bounce(2 * t - 1)) / 2;
+}
+// clang-format on
\ No newline at end of file
diff --git a/src/timing_functions.h b/src/timing_functions.h
new file mode 100644
index 00000000..8e6776e3
--- /dev/null
+++ b/src/timing_functions.h
@@ -0,0 +1,46 @@
+/*
+ all functions and formulas gathered from
+ https://easings.net/
+*/
+
+#pragma once
+
+double ease_in_sine(double t);
+double ease_out_sine(double t);
+double ease_in_out_sine(double t);
+
+double ease_in_cubic(double t);
+double ease_out_cubic(double t);
+double ease_in_out_cubic(double t);
+
+double ease_in_quint(double t);
+double ease_out_quint(double t);
+double ease_in_out_quint(double t);
+
+double ease_in_circ(double t);
+double ease_out_circ(double t);
+double ease_in_out_circ(double t);
+
+double ease_in_elastic(double t);
+double ease_out_elastic(double t);
+double ease_in_out_elastic(double t);
+
+double ease_in_quad(double t);
+double ease_out_quad(double t);
+double ease_in_out_quad(double t);
+
+double ease_in_quart(double t);
+double ease_out_quart(double t);
+double ease_in_out_quart(double t);
+
+double ease_in_etpo(double t);
+double ease_out_etpo(double t);
+double ease_in_out_etpo(double t);
+
+double ease_in_back(double t);
+double ease_out_back(double t);
+double ease_in_out_back(double t);
+
+double ease_out_bounce(double t);
+double ease_in_bounce(double t);
+double ease_in_out_bounce(double t);
\ No newline at end of file
diff --git a/src/win.c b/src/win.c
index 7bdb05f5..5171284f 100644
--- a/src/win.c
+++ b/src/win.c
@@ -1118,6 +1118,14 @@ void win_update_opacity_rule(session_t *ps, struct managed_win *w) {
w->opacity_is_set = is_set;
}
+void win_update_transition_rule(session_t *ps, struct managed_win *w) {
+ void *val;
+ if (c2_match(ps, w, ps->o.transition_rules, &val)) {
+ // uses multiple casters to trick compiler to not give warnings
+ w->transition_direction = (unsigned int)(long)val;
+ }
+}
+
/**
* Function to be called on window data changes.
*
@@ -1129,6 +1137,7 @@ void win_on_factor_change(session_t *ps, struct managed_win *w) {
// state of the window
win_update_focused(ps, w);
+ win_update_transition_rule(ps, w);
win_determine_shadow(ps, w);
win_determine_clip_shadow_above(ps, w);
win_determine_invert_color(ps, w);
@@ -1497,6 +1506,8 @@ struct win *fill_win(session_t *ps, struct win *w) {
.shadow_paint = PAINT_INIT,
.corner_radius = 0,
+
+ .transition_time = -1.0f,
};
assert(!w->destroyed);
@@ -1568,6 +1579,8 @@ struct win *fill_win(session_t *ps, struct win *w) {
.border_width = g->border_width,
};
+ new->transition_direction = ps->o.transition_direction;
+
free(g);
// Create Damage for window (if not Input Only)
diff --git a/src/win.h b/src/win.h
index 6c6ae323..2a5779c0 100644
--- a/src/win.h
+++ b/src/win.h
@@ -270,6 +270,15 @@ struct managed_win {
/// Whether to blur window background.
bool blur_background;
+ /// Transition time that used as input in timing function
+ double transition_time;
+
+ /// Save new geometry for calculating transition
+ struct win_geometry target_geometry;
+
+ /// Transition direction of window
+ enum transition_direction transition_direction;
+
#ifdef CONFIG_OPENGL
/// Textures and FBO background blur use.
glx_blur_cache_t glx_blur_cache;