diff --git a/include/view.h b/include/view.h index 5e1c5baf..7be4adc9 100644 --- a/include/view.h +++ b/include/view.h @@ -215,6 +215,9 @@ struct view { struct wlr_scene_tree *scene_tree; struct wlr_scene_tree *content_tree; + struct wlr_scene_buffer *animation; + uint64_t animation_start_time; + bool mapped; bool been_mapped; bool ssd_enabled; @@ -292,6 +295,7 @@ struct view { struct wl_listener request_maximize; struct wl_listener request_fullscreen; struct wl_listener set_title; + struct wl_listener output_commit; struct foreign_toplevel *foreign_toplevel; diff --git a/src/view-impl-common.c b/src/view-impl-common.c index 9e2c1243..bb01a8fb 100644 --- a/src/view-impl-common.c +++ b/src/view-impl-common.c @@ -3,10 +3,91 @@ #include "view-impl-common.h" #include #include +#include +#include +#include #include "foreign-toplevel.h" #include "labwc.h" #include "view.h" #include "window-rules.h" +#include "workspaces.h" + +static inline uint64_t +timespec_to_us(const struct timespec *ts) +{ + return (uint64_t)ts->tv_sec * UINT64_C(1000000) + + (uint64_t)ts->tv_nsec / UINT64_C(1000); +} + +static inline uint64_t +gettime_us(void) +{ + struct timespec ts = { 0 }; + clock_gettime(CLOCK_MONOTONIC, &ts); + return timespec_to_us(&ts); +} + +static void +adjust_scaled_position(struct view *view, double scale) +{ + int x = view->current.x; + int y = view->current.y; + double width = view->current.width; + double height = view->current.height; + + int x_pos = x + round(width * (1.0 - scale) / 2.0); + int y_pos = y + round(height * (1.0 - scale) / 2.0); + + wlr_scene_node_set_position(&view->scene_tree->node, x_pos, y_pos); +} + +static void +view_run_animation(struct view *view, uint64_t now) +{ + uint64_t elapsed_us = now - view->animation_start_time; + double t = (double)elapsed_us * 1e-6; + double duration = 0.2; // s + double scale = fmax(t / duration, 0.01); + + if (scale < 1.0) { + wlr_scene_node_set_scale(&view->scene_tree->node, scale); + adjust_scaled_position(view, scale); + } else { + int x = view->current.x; + int y = view->current.y; + wlr_scene_node_set_position(&view->scene_tree->node, x, y); + wlr_scene_node_set_scale(&view->scene_tree->node, 1.0); + wl_list_remove(&view->output_commit.link); + wl_list_init(&view->output_commit.link); + view->animation_start_time = 0; + } +} + +static void +handle_output_commit(struct wl_listener *listener, void *data) +{ + struct wlr_output_event_commit *event = data; + struct view *view = wl_container_of(listener, view, output_commit); + + if (!(event->state->committed & WLR_OUTPUT_STATE_BUFFER)) { + return; + } + + view_run_animation(view, gettime_us()); + + wlr_output_update_needs_frame(event->output); +} + +static void +view_start_animation(struct view *view) +{ + view->output_commit.notify = handle_output_commit; + wl_signal_add(&view->output->wlr_output->events.commit, &view->output_commit); + + uint64_t now = gettime_us(); + view->animation_start_time = now; + view_run_animation(view, now); +} void view_impl_map(struct view *view) @@ -16,6 +97,7 @@ view_impl_map(struct view *view) view_update_app_id(view); if (!view->been_mapped) { window_rules_apply(view, LAB_WINDOW_RULE_EVENT_ON_FIRST_MAP); + view_start_animation(view); } /* @@ -112,4 +194,8 @@ view_impl_apply_geometry(struct view *view, int w, int h) if (!wlr_box_equal(current, &old)) { view_moved(view); } + + if (view->animation_start_time) { + adjust_scaled_position(view, view->scene_tree->node.scale); + } } diff --git a/src/view.c b/src/view.c index d6ac89b9..378bde3b 100644 --- a/src/view.c +++ b/src/view.c @@ -2597,6 +2597,9 @@ view_destroy(struct view *view) wl_list_remove(&view->set_title.link); wl_list_remove(&view->destroy.link); + wl_list_remove(&view->output_commit.link); + wl_list_init(&view->output_commit.link); + if (view->foreign_toplevel) { foreign_toplevel_destroy(view->foreign_toplevel); view->foreign_toplevel = NULL;