From e1c9840c5b9dee5b08ec7251288bf68616840d6a Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 27 Jan 2021 16:46:11 +0100 Subject: [PATCH] screen: defer Paint() call using an IdleEvent This way, we avoid the assertion failure in FileListPage::PaintListItem(), caused by FileBrowserPage::Reload() resetting the FileList but not updating the ListWindow just yet; but the mpdclient::GetConnection() could return an idle event which would then trigger a synchronous repaint. We avoid lots of undefined behavior by deferring all Paint() calls with an IdleEvent, and maybe we save a few redundant Paint() calls. Fixes https://bugs.debian.org/981152 Closes https://github.com/MusicPlayerDaemon/ncmpc/issues/87 --- NEWS | 1 + src/screen.cxx | 2 +- src/screen.hxx | 16 +++++++++++++--- src/screen_init.cxx | 6 +++--- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/NEWS b/NEWS index fd486e85..f39234c6 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ ncmpc 0.43 - not yet released * show "conductor" and "work" on song page * playlist editor (work in progress) * file page: handle mouse double clicks +* fix crash bug * fix build failure on macOS * add azlyrics plugin diff --git a/src/screen.cxx b/src/screen.cxx index 6d39dbe0..714a7d3c 100644 --- a/src/screen.cxx +++ b/src/screen.cxx @@ -222,7 +222,7 @@ ScreenManager::Update(struct mpdclient &c, const DelayedSeek &seek) noexcept /* update the main window */ current_page->second->Update(c); - Paint(); + SchedulePaint(); } void diff --git a/src/screen.hxx b/src/screen.hxx index c4d8c082..3d672bdf 100644 --- a/src/screen.hxx +++ b/src/screen.hxx @@ -26,6 +26,7 @@ #include "StatusBar.hxx" #include "History.hxx" #include "Point.hxx" +#include "event/IdleEvent.hxx" #include "util/Compiler.h" #include @@ -44,7 +45,11 @@ class DelayedSeek; class EventLoop; class ScreenManager { - EventLoop &event_loop; + /** + * This event defers Paint() calls until after the EventLoop + * has handled all other events. + */ + IdleEvent paint_event; struct Layout { Size size; @@ -105,7 +110,7 @@ public: ~ScreenManager() noexcept; auto &GetEventLoop() const noexcept { - return event_loop; + return paint_event.GetEventLoop(); } void Init(struct mpdclient *c) noexcept; @@ -132,7 +137,6 @@ public: void Swap(struct mpdclient &c, const struct mpd_song *song) noexcept; void PaintTopWindow() noexcept; - void Paint() noexcept; void Update(struct mpdclient &c, const DelayedSeek &seek) noexcept; void OnCommand(struct mpdclient &c, DelayedSeek &seek, Command cmd); @@ -144,6 +148,12 @@ public: private: void NextMode(struct mpdclient &c, int offset) noexcept; + + void SchedulePaint() noexcept { + paint_event.Schedule(); + } + + void Paint() noexcept; }; #endif diff --git a/src/screen_init.cxx b/src/screen_init.cxx index 0ba0646c..b4c31db5 100644 --- a/src/screen_init.cxx +++ b/src/screen_init.cxx @@ -27,8 +27,8 @@ static const unsigned SCREEN_MIN_COLS = 14; static const unsigned SCREEN_MIN_ROWS = 5; -ScreenManager::ScreenManager(EventLoop &_event_loop) noexcept - :event_loop(_event_loop), +ScreenManager::ScreenManager(EventLoop &event_loop) noexcept + :paint_event(event_loop, BIND_THIS_METHOD(Paint)), layout({std::max(COLS, SCREEN_MIN_COLS), std::max(LINES, SCREEN_MIN_ROWS)}), title_bar({layout.title_x, layout.title_y}, layout.size.width), @@ -104,7 +104,7 @@ ScreenManager::OnResize() noexcept curs_set(1); curs_set(0); - Paint(); + SchedulePaint(); } void