Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/fs/fs_common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ class Filesystem {

public:
Type type;
std::string_view name, mount_name;
std::string name, mount_name;

protected:
devoptab_t devoptab = {};
Expand Down
53 changes: 47 additions & 6 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@
// You should have received a copy of the GNU General Public License
// along with SwitchWave. If not, see <http://www.gnu.org/licenses/>.

#include <cerrno>
#include <cstdio>
#include <chrono>
#include <filesystem>
#include <mutex>
#include <optional>
#include <string_view>
#include <thread>

Expand Down Expand Up @@ -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<std::vector<sw::fs::UmsController::Device>> g_ums_pending;

void ums_devices_changed_cb(const std::vector<sw::fs::UmsController::Device> &devices, void *user) {
auto &context = *static_cast<sw::Context *>(user);
auto lk = std::scoped_lock(g_ums_pending_mtx);
g_ums_pending = devices;
}

void apply_pending_ums_changes(sw::Context &context) {
std::vector<sw::fs::UmsController::Device> 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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down