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;