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 #752

Closed
wants to merge 5 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,4 @@ doxygen/
*.orig
/tests/log
/tests/testcases/__pycache__/
.vscode
25 changes: 25 additions & 0 deletions picom.sample.conf
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
################################
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is not enough explanation here for:

  • what does transition do? i.e. what is being transitioned?
  • what is transition-offset?
  • what is the meaning of each transition-direction?

# Transition #
#################################

# How many second transition will take to complete
transition-duration = .75;


transition-offset = 50;

# Directions:
# 'left', 'bottom', 'right', 'top', 'smart-x', 'smart-y' and for no transition use 'none'
transition-direction = "smart-x";

# See https://easings.net/ for seeing info about each timing function
# Naming case is differ from that site tho, For example
# 'easeInOutExpo' will be 'ease-in-out-expo' and ...
transition-timing-function = "ease-out-cubic";

# Similar to opacity-rule but this is for transition direction
# transition-rule = [
# "right:class_g = 'firefox'",
# "none:window_type = 'dropdown_menu'",
# ];

#################################
# Shadows #
#################################
Expand Down
5 changes: 5 additions & 0 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "types.h"
#include "utils.h"
#include "win.h"
#include "timing_functions.h"

#include "config.h"

Expand Down Expand Up @@ -581,6 +582,10 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,

.track_leader = false,

.transition_direction = TRANSITIONDIR_NONE,
.transition_rules = NULL,
.transition_duration = 0,

.rounded_corners_blacklist = NULL
};
// clang-format on
Expand Down
24 changes: 24 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ enum blur_method {
BLUR_METHOD_INVALID,
};

enum transition_direction {
TRANSITIONDIR_LEFT = 0,
TRANSITIONDIR_BOTTOM,
TRANSITIONDIR_RIGHT,
TRANSITIONDIR_TOP,
TRANSITIONDIR_SMART_X,
TRANSITIONDIR_SMART_Y,
TRANSITIONDIR_NONE
};

typedef double (*timing_function)(double);

typedef struct _c2_lptr c2_lptr_t;

/// Structure representing all options.
Expand Down Expand Up @@ -250,6 +262,18 @@ typedef struct options {
// Make transparent windows clip other windows, instead of blending on top of
// them
bool transparent_clipping;

// Transition
int transition_offset;

enum transition_direction transition_direction;

c2_lptr_t *transition_rules;

timing_function transition_timing_function;

double transition_duration;

} options_t;

extern const char *const BACKEND_STRS[NUM_BKEND + 1];
Expand Down
110 changes: 110 additions & 0 deletions src/config_libconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "string_utils.h"
#include "utils.h"
#include "win.h"
#include "timing_functions.h"

#pragma GCC diagnostic error "-Wunused-parameter"

Expand Down Expand Up @@ -250,6 +251,97 @@ parse_cfg_condlst_opct(options_t *opt, const config_t *pcfg, const char *name) {
}
}

enum transition_direction parse_transition_direction(const char* direction) {
static const char* names[] = {
"left",
"bottom",
"right",
"top",
"smart-x",
"smart-y",
"none"
};

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 TRANSITIONDIR_NONE;
}

timing_function parse_timing_function(const char* timing_name) {
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[] = {
easeInSine, easeOutSine, easeInOutSine,
easeInCubic, easeOutCubic, easeInOutCubic,
easeInQuint, easeOutQuint, easeInOutQuint,
easeInCirc, easeOutCirc, easeInOutCirc,
easeInElastic, easeOutElastic, easeInOutElastic,
easeInQuad, easeOutQuad, easeInOutQuad,
easeInQuart, easeOutQuart, easeInOutQuart,
easeInEtpo, easeOutEtpo, easeInOutEtpo,
easeInBack, easeOutBack, easeInOutBack,
easeOutBounce, easeInBounce, easeInOutBounce
};

char buffer[64];
for(int i=0; i < (int) (sizeof(names) / sizeof(char*)); i++) {
for(int p=0; p < 3; p++) {
snprintf(buffer, sizeof(buffer), "ease-%s-%s", prefixes[p], names[i]);

if(strcmp(buffer, timing_name) == 0) {
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[256];
size_t 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++;
}
}

enum transition_direction direction = parse_transition_direction(rule);
c2_parse(&opt->transition_rules, &elem[elem_index + 1], (void*) direction);
}
}
}

static inline void parse_wintype_config(const config_t *cfg, const char *member_name,
win_option_t *o, win_option_mask_t *mask) {
char *str = mstrjoin("wintypes.", member_name);
Expand Down Expand Up @@ -669,6 +761,24 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
opt->write_pid_path = strdup(sval);
}

config_lookup_int(&cfg, "transition-offset", &opt->transition_offset);
config_lookup_float(&cfg, "transition-duration", &opt->transition_duration);

if(config_lookup_string(&cfg, "transition-direction", &sval)) {
enum transition_direction dir = parse_transition_direction(sval);
opt->transition_direction = dir;
}

if(config_lookup_string(&cfg, "transition-timing-function", &sval)) {
timing_function res = parse_timing_function(sval);

if(res == NULL)
res = easeOutCubic;

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
23 changes: 23 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 uint32_t distance(int x1, int x2, int y1, int y2) {
return (uint32_t) (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,13 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
// visible/mapped
ps->pending_updates = true;

static const uint32_t 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 +246,18 @@ 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 != TRANSITIONDIR_NONE) {
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
42 changes: 42 additions & 0 deletions src/picom.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
#include "list.h"
#include "options.h"
#include "uthash_extra.h"
#include "timing_functions.h"

/// Get session_t pointer from a pointer to a member of session_t
#define session_ptr(ptr, member) \
Expand Down Expand Up @@ -690,6 +691,45 @@ 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
if(w->transition_time >= 0.0f && w->transition_time <= 1.0f && w->transition_direction != TRANSITIONDIR_NONE) {
// TODO: use refresh rate instead of using static 60 fps calculation
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see this todo completed before merging

w->transition_time += (1.0f / 60.0f) / ps->o.transition_duration;
double transition = ps->o.transition_timing_function(w->transition_time);
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 > TRANSITIONDIR_TOP) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite understand the logic here, maybe because it is not explained what the transition directions mean.

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 cause
window geometry may change between transitions
*/
direction = w->transition_direction - (bigger_than_half || wide_enough ? 4 : 2);
}

int8_t xy = direction % 2;
int16_t xy_target = *( ((int16_t*) &w->target_geometry) + xy );
int16_t* xy_source = ((int16_t*) &w->g) + xy;

int start_location = xy_target +
(direction % 3 == 0? -1: 1) * 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 @@ -1925,6 +1965,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
c2_list_postprocess(ps, ps->o.blur_background_blacklist) &&
c2_list_postprocess(ps, ps->o.invert_color_list) &&
c2_list_postprocess(ps, ps->o.opacity_rules) &&
c2_list_postprocess(ps, ps->o.transition_rules) &&
c2_list_postprocess(ps, ps->o.rounded_corners_blacklist) &&
c2_list_postprocess(ps, ps->o.focus_blacklist))) {
log_error("Post-processing of conditionals failed, some of your rules "
Expand Down Expand Up @@ -2302,6 +2343,7 @@ static void session_destroy(session_t *ps) {
free_wincondlst(&ps->o.invert_color_list);
free_wincondlst(&ps->o.blur_background_blacklist);
free_wincondlst(&ps->o.opacity_rules);
free_wincondlst(&ps->o.transition_rules);
free_wincondlst(&ps->o.paint_blacklist);
free_wincondlst(&ps->o.unredir_if_possible_blacklist);
free_wincondlst(&ps->o.rounded_corners_blacklist);
Expand Down
Loading