diff --git a/src/fs/fs_common.hpp b/src/fs/fs_common.hpp index d3d93af..6b395f6 100644 --- a/src/fs/fs_common.hpp +++ b/src/fs/fs_common.hpp @@ -165,7 +165,7 @@ class Filesystem { public: Type type; - std::string_view name, mount_name; + std::string name, mount_name; protected: devoptab_t devoptab = {}; diff --git a/src/main.cpp b/src/main.cpp index 3c79943..1503de6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,9 +15,12 @@ // You should have received a copy of the GNU General Public License // along with SwitchWave. If not, see . +#include #include #include #include +#include +#include #include #include @@ -137,23 +140,47 @@ void mpv_presetup() { std::printf("Dumped standard font\n"); } +// libusbhsfs invokes the populate callback on its USB-event thread. +// Stash the new device list under a mutex; the main thread applies +// the change so context.filesystems / cur_fs stay single-threaded. +std::mutex g_ums_pending_mtx; +std::optional> g_ums_pending; + void ums_devices_changed_cb(const std::vector &devices, void *user) { - auto &context = *static_cast(user); + auto lk = std::scoped_lock(g_ums_pending_mtx); + g_ums_pending = devices; +} + +void apply_pending_ums_changes(sw::Context &context) { + std::vector devices; + { + auto lk = std::scoped_lock(g_ums_pending_mtx); + if (!g_ums_pending) + return; + devices = std::move(*g_ums_pending); + g_ums_pending.reset(); + } // Remove unmounted devices - for (auto &fs: context.filesystems) { + bool removed_cur = false; + std::erase_if(context.filesystems, [&](const auto &fs) { if (fs->type != sw::fs::Filesystem::Type::Usb) - continue; + return false; auto it = std::find_if(devices.begin(), devices.end(), [&fs](const auto &dev) { return fs->mount_name == dev.mount_name; }); if (it == devices.end()) { - std::erase(context.filesystems, fs); - context.cur_fs = context.filesystems.front(); + if (context.cur_fs == fs) + removed_cur = true; + return true; } - } + return false; + }); + + if (removed_cur && !context.filesystems.empty()) + context.cur_fs = context.filesystems.front(); // Add new devices for (auto &dev: devices) { @@ -181,6 +208,8 @@ int menu_loop(sw::Renderer &renderer, sw::Context &context) { break; } + apply_pending_ums_changes(context); + padUpdate(&g_pad); auto has_touches = hidGetTouchScreenStates(&g_touch_state, 1); ImGui::nx::newFrame(&g_pad, has_touches ? &g_touch_state : nullptr); @@ -277,6 +306,18 @@ int video_loop(sw::Renderer &renderer, sw::Context &context) { break; } + apply_pending_ums_changes(context); + + // If the filesystem hosting the playing file was unplugged + // (e.g. USB key removed mid-playback), stop mpv and bail out + // of the player loop so it can't keep reading a dead device. + if (!context.cur_file.empty() && + !context.get_filesystem(sw::fs::Path::mountpoint(context.cur_file))) { + lmpv.command_async("stop"); + context.set_error(-EIO); + break; + } + padUpdate(&g_pad); auto has_touches = hidGetTouchScreenStates(&g_touch_state, 1); ImGui::nx::newFrame(&g_pad, has_touches ? &g_touch_state : nullptr);