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

Adding transition (cleanup) #766

Closed
wants to merge 4 commits into from
Closed
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
39 changes: 39 additions & 0 deletions picom.sample.conf
Original file line number Diff line number Diff line change
@@ -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 #
#################################
Expand Down
5 changes: 5 additions & 0 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 30 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include "types.h"
#include "win_defs.h"

#include "timing_functions.h"

typedef struct session session_t;

/// @brief Possible backends
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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];
Expand Down
106 changes: 106 additions & 0 deletions src/config_libconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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
Expand Down
27 changes: 27 additions & 0 deletions src/event.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand All @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand Down
47 changes: 47 additions & 0 deletions src/picom.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Arian8j2 marked this conversation as resolved.
Show resolved Hide resolved
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.
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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
{
Expand Down
Loading