From 51fdb8bcaafc4e1261a644873358031c37e09f83 Mon Sep 17 00:00:00 2001 From: blackcapcoder Date: Sat, 7 Oct 2017 15:22:37 +0200 Subject: [PATCH] Implement animations Squashed all the stuff: init Update README.md options performance Animation no longer jumps if started in the middle of an animation Doubled default transition length Minor bugfix, preformance Track x and y separately to aviod jumping in mid-animation repositioning Added video to readme docs docs fixed graphical glitch with non-transparent windows now animates window scaling too (but only if the window grows) added options for size transitions and actually respect the new options.. and actually respect the new options.. added center-spawn option added center-spawn-screen option fixed center spawn added no-scale-down goofed remember old windows remember old windows docs fixed shadow fixed shadow fixed shadow fixed shadow lost no-scale-down due to revert lost spawn-center due to revert Fixed #2 fix missing window borders --- src/common.h | 5 ++ src/config.c | 9 ++++ src/config.h | 18 +++++++ src/config_libconfig.c | 23 +++++++++ src/event.c | 103 +++++++++++++++++++++++++++++++++++++++-- src/picom.c | 53 ++++++++++++++++++++- src/render.c | 9 ++-- src/win.c | 14 ++++++ src/win.h | 7 +++ 9 files changed, 234 insertions(+), 7 deletions(-) diff --git a/src/common.h b/src/common.h index 542c31f..9498ebe 100644 --- a/src/common.h +++ b/src/common.h @@ -527,3 +527,8 @@ static inline void wintype_arr_enable(bool arr[]) { arr[i] = true; } } + +/** + * Get current system clock in milliseconds. + */ +int64_t get_time_ms(void); diff --git a/src/config.c b/src/config.c index a88bc02..6824865 100644 --- a/src/config.c +++ b/src/config.c @@ -497,6 +497,15 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable, *opt = (struct options){ .backend = BKEND_XRENDER, .glx_no_stencil = false, + .transition_length = 300, + .transition_pow_x = 1.5, + .transition_pow_y = 1.5, + .transition_pow_w = 1.5, + .transition_pow_h = 1.5, + .size_transition = true, + .no_scale_down = false, + .spawn_center_screen = false, + .spawn_center = true, .mark_wmwin_focused = false, .mark_ovredir_focused = false, .detect_rounded_corners = false, diff --git a/src/config.h b/src/config.h index cd32add..3e54116 100644 --- a/src/config.h +++ b/src/config.h @@ -87,6 +87,24 @@ typedef struct options { bool glx_no_stencil; /// Whether to avoid rebinding pixmap on window damage. bool glx_no_rebind_pixmap; + /// Length of window transitions + int transition_length; + /// For smoothing on the x-coordinate of window animations + float transition_pow_x; + /// For smoothing on the y-coordinate of window animations + float transition_pow_y; + /// For smoothing on the width of window animations + float transition_pow_w; + /// For smoothing on the height of window animations + float transition_pow_h; + /// Wether to animate on window size change + bool size_transition; + /// Wether to scale new windows in from the center of the screen + bool spawn_center_screen; + /// Wether to scale new windows in from their center + bool spawn_center; + /// Does not animate downscaling + bool no_scale_down; /// Custom fragment shader for painting windows, as a string. char *glx_fshader_win_str; /// Whether to detect rounded corners. diff --git a/src/config_libconfig.c b/src/config_libconfig.c index 659ff86..f5b1a41 100644 --- a/src/config_libconfig.c +++ b/src/config_libconfig.c @@ -353,6 +353,29 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad // -O (fade_out_step) if (config_lookup_float(&cfg, "fade-out-step", &dval)) opt->fade_out_step = normalize_d(dval); + // --transition-length + if (config_lookup_int(&cfg, "transition-length", &ival)) + opt->transition_length = ival; + // --transition-pow-x + if (config_lookup_float(&cfg, "transition-pow-x", &dval)) + opt->transition_pow_x = dval; + // --transition-pow-y + if (config_lookup_float(&cfg, "transition-pow-y", &dval)) + opt->transition_pow_y = dval; + // --transition-pow-w + if (config_lookup_float(&cfg, "transition-pow-w", &dval)) + opt->transition_pow_w = dval; + // --transition-pow-h + if (config_lookup_float(&cfg, "transition-pow-h", &dval)) + opt->transition_pow_h = dval; + // --size-transition + lcfg_lookup_bool(&cfg, "size-transition", &opt->size_transition); + // --spawn-center-screen + lcfg_lookup_bool(&cfg, "spawn-center-screen", &opt->spawn_center_screen); + // --spawn-center + lcfg_lookup_bool(&cfg, "spawn-center", &opt->spawn_center); + // --no-scale-down + lcfg_lookup_bool(&cfg, "no-scale-down", &opt->no_scale_down); // -r (shadow_radius) config_lookup_int(&cfg, "shadow-radius", &opt->shadow_radius); // -o (shadow_opacity) diff --git a/src/event.c b/src/event.c index 3be46e4..141b668 100644 --- a/src/event.c +++ b/src/event.c @@ -202,6 +202,106 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) { auto mw = (struct managed_win *)w; + float t = get_time_ms(); + if (mw->oldX == -10000 && mw->oldY == -10000 && mw->oldW == 0 && mw->oldH == 0) { + if (!mw->isOld) { + /* mw->isOld = true; */ + + if (ps->o.spawn_center_screen) { + mw->oldX = ps->root_width/2; + mw->oldY = ps->root_height/2; + mw->oldW = 1; + mw->oldH = 1; + } else if (ps->o.spawn_center) { + mw->oldX = ce->x + ce->width/2; + mw->oldY = ce->y + ce->height/2; + mw->oldW = 1; + mw->oldH = 1; + } else { + mw->oldX = ce->x; + mw->oldY = ce->y; + mw->oldW = ce->width; + mw->oldH = ce->height; + } + } else { + mw->oldX = ce->x; + mw->oldY = ce->y; + mw->oldW = ce->width; + mw->oldH = ce->height; + } + + mw->newX = ce->x; + mw->newY = ce->y; + mw->newW = ce->width; + mw->newH = ce->height; + mw->moveTimeX = t; + mw->moveTimeY = t; + mw->moveTimeW = t; + mw->moveTimeH = t; + } else { + if (mw->newX == mw->g.x && mw->newY == mw->g.y) { + mw->oldX = mw->g.x; + mw->oldY = mw->g.y; + mw->oldW = mw->g.width; + mw->oldH = mw->g.height; + mw->moveTimeX = t; + mw->moveTimeY = t; + mw->moveTimeW = t; + mw->moveTimeH = t; + } + if (mw->newX != ce->x || mw->newY != ce->y || mw->newW != ce->width || mw->newH != ce->height) { + float moveDx = ((float) t - mw->moveTimeX) / ps->o.transition_length; + float moveDy = ((float) t - mw->moveTimeY) / ps->o.transition_length; + float moveDw = ((float) t - mw->moveTimeW) / ps->o.transition_length; + float moveDh = ((float) t - mw->moveTimeH) / ps->o.transition_length; + + if (mw->moveTimeX != 0.0 && moveDx < 1.0 && mw->oldX != mw->newX) { + float oldMoveDx = pow((float) (mw->newX - mw->g.x) / (float) (mw->newX - ce->x), 1 / ps->o.transition_pow_x); + float fakeT = (t - oldMoveDx * (float) ps->o.transition_length); + /* printf("X: %f,%f\n", fakeT, t); */ + mw->moveTimeX = isnanf(fakeT)? t : fakeT; + } else { + mw->moveTimeX = t; + } + if (mw->moveTimeY != 0.0 && moveDy < 1.0 && mw->oldY != mw->newY) { + float oldMoveDy = pow((float) (mw->newY - mw->g.y) / (float) (mw->newY - ce->y), 1 / ps->o.transition_pow_y); + float fakeT = (t - oldMoveDy * (float) ps->o.transition_length); + /* printf("Y: %f,%f\n", fakeT, t); */ + mw->moveTimeY = isnanf(fakeT)? t : fakeT; + } else { + mw->moveTimeY = t; + } + if (mw->moveTimeW != 0.0 && moveDw < 1.0 && mw->oldW != mw->newW) { + float oldMoveDw = pow((float) (mw->newW - mw->g.width) / (float) (mw->newW - ce->width), 1 / ps->o.transition_pow_w); + float fakeT = (t - oldMoveDw * (float) ps->o.transition_length); + /* printf("Y: %f,%f\n", fakeT, t); */ + mw->moveTimeW = isnanf(fakeT)? t : fakeT; + } else { + mw->moveTimeW = t; + } + if (mw->moveTimeH != 0.0 && moveDh < 1.0 && mw->oldH != mw->newH) { + float oldMoveDh = pow((float) (mw->newH - mw->g.height) / (float) (mw->newH - ce->height), 1 / ps->o.transition_pow_h); + float fakeT = (t - oldMoveDh * (float) ps->o.transition_length); + /* printf("Y: %f,%f\n", fakeT, t); */ + mw->moveTimeH = isnanf(fakeT)? t : fakeT; + } else { + mw->moveTimeH = t; + } + + mw->oldX = mw->newX; + mw->oldY = mw->newY; + mw->oldW = mw->newW; + mw->oldH = mw->newH; + mw->newX = ce->x; + mw->newY = ce->y; + mw->newW = ce->width; + mw->newH = ce->height; + + if (ps->o.no_scale_down && mw->newW < mw->oldW) { mw->oldW = mw->newW; } + if (ps->o.no_scale_down && mw->newH < mw->oldH) { mw->oldH = mw->newH; } + } + } + if (mw->state == WSTATE_UNMAPPED || mw->state == WSTATE_UNMAPPING || mw->state == WSTATE_DESTROYING) { // Only restack the window to make sure we can handle future restack @@ -218,9 +318,6 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) { factor_change = true; } - mw->g.x = ce->x; - mw->g.y = ce->y; - if (mw->g.width != ce->width || mw->g.height != ce->height || mw->g.border_width != ce->border_width) { log_trace("Window size changed, %dx%d -> %dx%d", mw->g.width, diff --git a/src/picom.c b/src/picom.c index cff2707..a775ab9 100644 --- a/src/picom.c +++ b/src/picom.c @@ -126,7 +126,7 @@ static inline void free_xinerama_info(session_t *ps) { /** * Get current system clock in milliseconds. */ -static inline int64_t get_time_ms(void) { +int64_t get_time_ms(void) { struct timespec tp; clock_gettime(CLOCK_MONOTONIC, &tp); return (int64_t)tp.tv_sec * 1000 + (int64_t)tp.tv_nsec / 1000000; @@ -676,6 +676,57 @@ static struct managed_win *paint_preprocess(session_t *ps, bool *fade_running) { } } + win_stack_foreach_managed(w, &ps->window_stack) { + bool posChanged = (w->oldX != -10000 && w->oldY != -10000 && w->oldW != 0 && w->oldH != 0) + && (w->g.x != w->newX || w->g.y != w->newY || w->g.width != w->newW || w->g.height != w->newH); + + if (posChanged) { + float t = get_time_ms(); + float moveDx = (t - w->moveTimeX) / ps->o.transition_length; + float moveDy = (t - w->moveTimeY) / ps->o.transition_length; + float moveDw = (t - w->moveTimeW) / ps->o.transition_length; + float moveDh = (t - w->moveTimeH) / ps->o.transition_length; + if (moveDx >= 1.0) moveDx = 1.0; + if (moveDy >= 1.0) moveDy = 1.0; + if (moveDw >= 1.0) moveDw = 1.0; + if (moveDh >= 1.0) moveDh = 1.0; + + float q = pow (moveDx, ps->o.transition_pow_x); + float k = pow (moveDy, ps->o.transition_pow_y); + float g = pow (moveDw, ps->o.transition_pow_w); + float z = pow (moveDh, ps->o.transition_pow_h); + + float x = (float) w->oldX * (1-q) + (float) w->newX * q; + float y = (float) w->oldY * (1-k) + (float) w->newY * k; + float W = (float) w->oldW * (1-g) + (float) w->newW * g; + float h = (float) w->oldH * (1-z) + (float) w->newH * z; + + add_damage_from_win(ps, w); + w->g.x = (int) x; + w->g.y = (int) y; + if (ps->o.size_transition) { + w->g.width = (int) W; + w->g.height = (int) h; + } + + /* w->to_paint = true; */ + w->mode = WMODE_TRANS; + *fade_running = true; + } + // TODO + //if ((w->shadow && posChanged) || (ps->o.size_transition && w->pixmap_damaged)) { + // rc_region_unref(&w->extents); + // rc_region_unref(&w->border_size); + // w->extents = win_extents(ps, w); + // calc_win_size(ps, w); + + // if (ps->shape_exists && ps->o.shadow_ignore_shaped + // && ps->o.detect_rounded_corners && w->bounding_shaped) + // win_update_shape(ps, w); + //} + /* add_damage_win(ps, w); */ + } + // Opacity will not change, from now on. rc_region_t *last_reg_ignore = rc_region_new(); diff --git a/src/render.c b/src/render.c index 9859b0f..ac4b232 100644 --- a/src/render.c +++ b/src/render.c @@ -529,8 +529,10 @@ static void paint_root(session_t *ps, const region_t *reg_paint) { * Generate shadow Picture for a window. */ static bool win_build_shadow(session_t *ps, struct managed_win *w, double opacity) { - const int width = w->widthb; - const int height = w->heightb; + /* const int width = w->widthb; */ + /* const int height = w->heightb; */ + const int width = w->newW; // TODO! + const int height = w->newH; // log_trace("(): building shadow for %s %d %d", w->name, width, height); xcb_image_t *shadow_image = NULL; @@ -909,7 +911,8 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) { pixman_region32_intersect(®_tmp, ®_tmp, &bshape); pixman_region32_fini(&bshape); - if (pixman_region32_not_empty(®_tmp)) { + reg_tmp = region; + if (pixman_region32_not_empty(®_tmp) || true) { set_tgt_clip(ps, ®_tmp); // Blur window background if (w->blur_background && diff --git a/src/win.c b/src/win.c index 9f7d6bc..e6e81ad 100644 --- a/src/win.c +++ b/src/win.c @@ -1190,6 +1190,12 @@ struct win *fill_win(session_t *ps, struct win *w) { .dim = false, .invert_color = false, .blur_background = false, + + .oldX = -10000, + .oldY = -10000, + .oldW = 0, + .oldH = 0, + .reg_ignore = NULL, // The following ones are updated for other reasons .pixmap_damaged = false, // updated by damage events @@ -1772,6 +1778,7 @@ static void destroy_win_finish(session_t *ps, struct win *w) { static void map_win_finish(struct managed_win *w) { w->in_openclose = false; + w->isOld = true; w->state = WSTATE_MAPPED; } @@ -2080,6 +2087,13 @@ void map_win_start(session_t *ps, struct managed_win *w) { // XXX Can we assume map_state is always viewable? w->a.map_state = XCB_MAP_STATE_VIEWABLE; + if (!w->isOld) { + w->oldX = -10000; + w->oldY = -10000; + w->oldW = 0; + w->oldH = 0; + } + win_update_screen(ps, w); // Set window event mask before reading properties so that no property diff --git a/src/win.h b/src/win.h index 4f1ee41..43f5c02 100644 --- a/src/win.h +++ b/src/win.h @@ -248,6 +248,13 @@ struct managed_win { /// Whether to blur window background. bool blur_background; + /// Animation state + int oldX; int oldY; int oldW; int oldH; + int newX; int newY; int newW; int newH; + float moveTimeX; float moveTimeY; + float moveTimeW; float moveTimeH; + bool isOld; + #ifdef CONFIG_OPENGL /// Textures and FBO background blur use. glx_blur_cache_t glx_blur_cache;