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);