From 58d20c77517580009b8237ec405358ae32fe7ed4 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Thu, 19 Sep 2024 15:55:57 +0200 Subject: [PATCH] menu_view: use timer for animation timing This patch removes the use of sync signals as time source. The animation phase is now timed using a timer connection as tick source while sync signals are used for scheduling the redraws. Issue #5347 --- repos/gems/src/app/menu_view/button_widget.h | 2 +- repos/gems/src/app/menu_view/cursor.h | 2 +- repos/gems/src/app/menu_view/dialog.h | 53 +++++++-- repos/gems/src/app/menu_view/label_widget.h | 2 +- repos/gems/src/app/menu_view/main.cc | 108 +------------------ repos/gems/src/app/menu_view/types.h | 2 + repos/gems/src/app/menu_view/widget.h | 2 +- 7 files changed, 57 insertions(+), 114 deletions(-) diff --git a/repos/gems/src/app/menu_view/button_widget.h b/repos/gems/src/app/menu_view/button_widget.h index 4d8f4bf6e8..016c1214ab 100644 --- a/repos/gems/src/app/menu_view/button_widget.h +++ b/repos/gems/src/app/menu_view/button_widget.h @@ -85,7 +85,7 @@ struct Menu_view::Button_widget : Widget, Animator::Item * or changing the selection state of a button, the transition * must be quick to provide a responsive feel. */ - enum { SLOW = 80, MEDIUM = 40, FAST = 3 }; + enum { SLOW = 40, MEDIUM = 20, FAST = 2 }; int steps = SLOW; if (_hovered && !new_hovered) steps = MEDIUM; if (!_hovered && new_hovered) steps = FAST; diff --git a/repos/gems/src/app/menu_view/cursor.h b/repos/gems/src/app/menu_view/cursor.h index 9b0c8b5dde..aa78036ff0 100644 --- a/repos/gems/src/app/menu_view/cursor.h +++ b/repos/gems/src/app/menu_view/cursor.h @@ -122,7 +122,7 @@ class Menu_view::Cursor : List_model::Element void update(Xml_node const &node) { - _move_to(_position_from_xml_node(node), Steps{12}); + _move_to(_position_from_xml_node(node), Steps{6}); } }; diff --git a/repos/gems/src/app/menu_view/dialog.h b/repos/gems/src/app/menu_view/dialog.h index 8e61fa020e..b4b16b8226 100644 --- a/repos/gems/src/app/menu_view/dialog.h +++ b/repos/gems/src/app/menu_view/dialog.h @@ -47,9 +47,9 @@ struct Menu_view::Dialog : List_model::Element struct Action : Interface { - virtual void trigger_redraw() = 0; virtual void hover_changed() = 0; virtual void observed_seq_number(Input::Seq_number) = 0; + virtual Ticks now() = 0; }; Action &_action; @@ -72,6 +72,15 @@ struct Menu_view::Dialog : List_model::Element void _handle_input(); + Signal_handler _gui_sync_handler { + _env.ep(), *this, &Dialog::_handle_gui_sync }; + + void _handle_gui_sync(); + + bool _gui_sync_enabled = false; + + Ticks _previous_sync { }; + Constructible _buffer { }; Gui::View_ref _view_ref { }; @@ -151,7 +160,7 @@ struct Menu_view::Dialog : List_model::Element _root_widget.gen_hover_model(xml, _hovered_position); } - void redraw() + void _redraw() { if (!_redraw_scheduled) return; @@ -190,7 +199,7 @@ struct Menu_view::Dialog : List_model::Element bool hovered() const { return _hovered; } - void animate() + void _animate() { bool const progress = _local_animator.active(); @@ -200,9 +209,14 @@ struct Menu_view::Dialog : List_model::Element _redraw_scheduled = true; } - bool animation_in_progress() const { return _local_animator.active(); } + void enforce_font_sytle_change() + { + _handle_dialog(); - bool redraw_scheduled() const { return _redraw_scheduled; } + /* fast-forward geometry animation */ + while (_local_animator.active()) + _animate(); + } /* * List_model @@ -237,7 +251,13 @@ void Menu_view::Dialog::_handle_dialog() _redraw_scheduled = true; _action.hover_changed(); - _action.trigger_redraw(); + + if (!_gui_sync_enabled) { + _gui.framebuffer.sync_sigh(_gui_sync_handler); + _gui_sync_enabled = true; + } + + _handle_gui_sync(); } @@ -287,4 +307,25 @@ void Menu_view::Dialog::_handle_input() _action.hover_changed(); } + +void Menu_view::Dialog::_handle_gui_sync() +{ + Ticks const now = _action.now(); + + Ticks const passed_ticks { now.cs - _previous_sync.cs }; + + for (unsigned i = 0; i < passed_ticks.cs; i++) + _animate(); + + if (passed_ticks.cs) + _redraw(); + + /* deactivate sync signalling when idle */ + if (_gui_sync_enabled && !_local_animator.active()) { + _gui.framebuffer.sync_sigh(Signal_context_capability()); + _gui_sync_enabled = false; + } + _previous_sync = now; +} + #endif /* _DIALOG_H_ */ diff --git a/repos/gems/src/app/menu_view/label_widget.h b/repos/gems/src/app/menu_view/label_widget.h index b640ec5d01..817bdc00fc 100644 --- a/repos/gems/src/app/menu_view/label_widget.h +++ b/repos/gems/src/app/menu_view/label_widget.h @@ -89,7 +89,7 @@ struct Menu_view::Label_widget : Widget, Cursor::Glyph_position _hover = node.attribute_value("hover", false); _factory.styles.with_label_style(node, [&] (Label_style style) { - _color.fade_to(style.color, Animated_color::Steps{80}); }); + _color.fade_to(style.color, Animated_color::Steps{40}); }); if (node.has_attribute("text")) { _text = node.attribute_value("text", _text); diff --git a/repos/gems/src/app/menu_view/main.cc b/repos/gems/src/app/menu_view/main.cc index e220ad1cc8..dee982d66a 100644 --- a/repos/gems/src/app/menu_view/main.cc +++ b/repos/gems/src/app/menu_view/main.cc @@ -83,45 +83,14 @@ struct Menu_view::Main : Dialog::Action } _input_seq_number { }; - struct Frame { uint64_t count; }; - - /* - * Timer used for animating widgets - */ - struct Frame_timer : Timer::Connection - { - enum { PERIOD = 10 }; - - Frame curr_frame() const { return { elapsed_ms() / PERIOD }; } - - void schedule() { trigger_once((Genode::uint64_t)Frame_timer::PERIOD*1000); } - - Frame_timer(Env &env) : Timer::Connection(env) { } - - } _timer { _env }; - - void _handle_frame_timer(); + Timer::Connection _timer { _env }; /** * Dialog::Action */ - void trigger_redraw() override + Ticks now() override { - /* - * If we have not processed a period for at least one frame, perform the - * processing immediately. This way, we avoid latencies when the dialog - * model is updated sporadically. - */ - Frame const curr_frame = _timer.curr_frame(); - if (curr_frame.count != _last_frame.count) { - - if (curr_frame.count - _last_frame.count > 10) - _last_frame = curr_frame; - - _handle_frame_timer(); - } else { - _timer.schedule(); - } + return { .cs = _timer.curr_time().trunc_to_plain_ms().value / 10 }; } /** @@ -137,38 +106,16 @@ struct Menu_view::Main : Dialog::Action _input_seq_number.update(seq); } - Signal_handler
_frame_timer_handler = { - _env.ep(), *this, &Main::_handle_frame_timer}; - Constructible _hover_reporter { }; void _update_hover_report(); - /** - * Frame of last call of 'handle_frame_timer' - */ - Frame _last_frame { }; - - /** - * Number of frames between two redraws - */ - static constexpr unsigned REDRAW_PERIOD = 2; - - /** - * Counter used for triggering redraws. Incremented in each frame-timer - * period, wraps at 'REDRAW_PERIOD'. The redraw is performed when the - * counter wraps. - */ - unsigned _frame_cnt = 0; - Main(Env &env, Vfs::Env &libc_vfs_env) : _env(env), _vfs_env(libc_vfs_env) { _config.sigh(_config_handler); _config_handler.local_submit(); /* apply initial configuration */ - - _timer.sigh(_frame_timer_handler); } }; @@ -239,57 +186,10 @@ void Menu_view::Main::_handle_config() /* re-assign font pointers in labels (needed due to font style change) */ if (!_styles.up_to_date()) { _dialogs.for_each([&] (Dialog &dialog) { - dialog._handle_dialog(); - - /* fast-forward geometry animation on font changes */ - while (dialog.animation_in_progress()) - dialog.animate(); - }); + dialog.enforce_font_sytle_change(); }); _styles.flush_outdated_styles(); } - trigger_redraw(); -} - - -void Menu_view::Main::_handle_frame_timer() -{ - _frame_cnt++; - - Frame const curr_frame = _timer.curr_frame(); - - unsigned const passed_frames = - max(unsigned(curr_frame.count - _last_frame.count), 4U); - - if (passed_frames > 0) - _dialogs.for_each([&] (Dialog &dialog) { - for (unsigned i = 0; i < passed_frames; i++) - dialog.animate(); }); - - bool any_redraw_scheduled = false; - _dialogs.for_each([&] (Dialog const &dialog) { - any_redraw_scheduled |= dialog.redraw_scheduled(); }); - - _last_frame = curr_frame; - - bool const redraw_skipped = any_redraw_scheduled && (_frame_cnt < REDRAW_PERIOD); - - if (!redraw_skipped) { - _frame_cnt = 0; - _dialogs.for_each([&] (Dialog &dialog) { - dialog.redraw(); }); - } - - bool any_animation_in_progress = false; - _dialogs.for_each([&] (Dialog const &dialog) { - any_animation_in_progress |= dialog.animation_in_progress(); }); - - /* - * Deactivate timer periods when idle, activate timer when an animation is - * in progress or a redraw is pending. - */ - if (any_animation_in_progress || redraw_skipped) - _timer.schedule(); } diff --git a/repos/gems/src/app/menu_view/types.h b/repos/gems/src/app/menu_view/types.h index a2cf878a0f..10449caafe 100644 --- a/repos/gems/src/app/menu_view/types.h +++ b/repos/gems/src/app/menu_view/types.h @@ -36,6 +36,8 @@ namespace Menu_view { using Point = Surface_base::Point; using Area = Surface_base::Area; using Rect = Surface_base::Rect; + + struct Ticks { uint64_t cs; /* centi-seconds (10 ms) */ }; } #endif /* _TYPES_H_ */ diff --git a/repos/gems/src/app/menu_view/widget.h b/repos/gems/src/app/menu_view/widget.h index 2c2fa64a0d..9b5d01de77 100644 --- a/repos/gems/src/app/menu_view/widget.h +++ b/repos/gems/src/app/menu_view/widget.h @@ -110,7 +110,7 @@ class Menu_view::Widget : List_model::Element return node.attribute_value("version", Version()); } - static Animated_rect::Steps motion_steps() { return { 60 }; }; + static Animated_rect::Steps motion_steps() { return { 30 }; }; protected: