diff --git a/components/gui/include/gui.hpp b/components/gui/include/gui.hpp index e6a53ee..c36a2f3 100644 --- a/components/gui/include/gui.hpp +++ b/components/gui/include/gui.hpp @@ -94,5 +94,5 @@ class Gui { .callback = std::bind(&Gui::update, this), }}; espp::Logger logger_; - mutable std::recursive_mutex mutex_; + std::recursive_mutex &mutex_{Tab5Emu::lvgl_mutex()}; // shared: LVGL is not thread-safe }; diff --git a/components/gui/src/gui.cpp b/components/gui/src/gui.cpp index 4a4157e..37e1235 100644 --- a/components/gui/src/gui.cpp +++ b/components/gui/src/gui.cpp @@ -39,6 +39,8 @@ Gui::~Gui() { void Gui::pause() { paused_ = true; task_.stop(); + // wait out a timer callback already in lv_timer_handler() (stop() does not) + std::lock_guard lk(mutex_); } void Gui::resume() { diff --git a/components/menu/include/menu.hpp b/components/menu/include/menu.hpp index d47df65..2585cc3 100644 --- a/components/menu/include/menu.hpp +++ b/components/menu/include/menu.hpp @@ -92,5 +92,5 @@ class Menu { .callback = std::bind(&Menu::update, this), }}; espp::Logger logger_; - std::recursive_mutex mutex_; + std::recursive_mutex &mutex_{Tab5Emu::lvgl_mutex()}; // shared: LVGL is not thread-safe }; diff --git a/components/menu/src/menu.cpp b/components/menu/src/menu.cpp index 0b65b84..048b868 100644 --- a/components/menu/src/menu.cpp +++ b/components/menu/src/menu.cpp @@ -36,6 +36,8 @@ void Menu::select_slot(int slot) { void Menu::pause() { paused_ = true; task_.stop(); + // wait out a timer callback already in lv_timer_handler() (stop() does not) + std::lock_guard lk(mutex_); } void Menu::resume() { diff --git a/components/tab5-emu/include/tab5-emu.hpp b/components/tab5-emu/include/tab5-emu.hpp index 14f2744..0a3c274 100644 --- a/components/tab5-emu/include/tab5-emu.hpp +++ b/components/tab5-emu/include/tab5-emu.hpp @@ -194,6 +194,13 @@ class Tab5Emu : public espp::BaseComponent { /// focused slider / dropdown), A = enter, B = escape. Menus attach their /// lv_group to it (lv_indev_set_group) when shown. lv_indev_t *keypad_indev() const { return keypad_indev_; } + /// The one lock for LVGL (built without LV_USE_OS, so it is not + /// thread-safe): the launcher's and the pause menu's timers and their + /// event-manager callbacks (volume) all touch it from different tasks. + static std::recursive_mutex &lvgl_mutex() { + static std::recursive_mutex m; + return m; + } /// Stop the USB host library (keyboard / mouse stop working). The USB /// controller is shared with device mode (see initialize_usb_msc()). void deinitialize_usb_host();