Windows specific refactoring of Mouse3DController and RemovableDriveManager.

PrusaSlicer newly registers by Windows operating system for HID USB
plug / unplug notifications and for Volume attach / detach notifications,
and the background threads of the two respective services are waken up
on these Windows notifications.
The RemovableDriveManager also wakes up every 30 seconds to cope with
the drives ejected from Windows Explorer or from another application,
for example Cura, for which Windows OS does not send out notifications.
This commit is contained in:
bubnikv 2020-03-13 14:19:02 +01:00
parent 3684eea53d
commit 2f6326a2eb
10 changed files with 207 additions and 109 deletions

View file

@ -10,6 +10,7 @@
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/log/trivial.hpp>
#include <boost/nowide/convert.hpp>
#include <wx/stdpaths.h>
#include <wx/imagpng.h>
@ -50,6 +51,7 @@
#ifdef __WXMSW__
#include <Shlobj.h>
#include <dbt.h>
#endif // __WXMSW__
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG
@ -60,6 +62,7 @@
namespace Slic3r {
namespace GUI {
class MainFrame;
wxString file_wildcards(FileType file_type, const std::string &custom_extension)
{
@ -96,9 +99,9 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); }
static void register_dpi_event()
{
#ifdef WIN32
static void register_win32_dpi_event()
{
enum { WM_DPICHANGED_ = 0x02e0 };
wxWindow::MSWRegisterMessageHandler(WM_DPICHANGED_, [](wxWindow *win, WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) {
@ -111,9 +114,52 @@ static void register_dpi_event()
return true;
});
#endif
}
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 };
static void register_win32_device_notification_event()
{
enum { WM_DPICHANGED_ = 0x02e0 };
wxWindow::MSWRegisterMessageHandler(WM_DEVICECHANGE, [](wxWindow *win, WXUINT /* nMsg */, WXWPARAM wParam, WXLPARAM lParam) {
// Some messages are sent to top level windows by default, some messages are sent to only registered windows, and we explictely register on MainFrame only.
auto main_frame = dynamic_cast<MainFrame*>(win);
auto plater = (main_frame == nullptr) ? nullptr : main_frame->plater();
if (plater == nullptr)
// Maybe some other top level window like a dialog or maybe a pop-up menu?
return true;
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
switch (wParam) {
case DBT_DEVICEARRIVAL:
if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
plater->GetEventHandler()->AddPendingEvent(VolumeAttachedEvent(EVT_VOLUME_ATTACHED));
else if (lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
PDEV_BROADCAST_DEVICEINTERFACE lpdbi = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
// if (lpdbi->dbcc_classguid == GUID_DEVINTERFACE_VOLUME) {
// printf("DBT_DEVICEARRIVAL %d - Media has arrived: %ws\n", msg_count, lpdbi->dbcc_name);
if (lpdbi->dbcc_classguid == GUID_DEVINTERFACE_HID)
plater->GetEventHandler()->AddPendingEvent(HIDDeviceAttachedEvent(EVT_HID_DEVICE_ATTACHED, boost::nowide::narrow(lpdbi->dbcc_name)));
}
break;
case DBT_DEVICEREMOVECOMPLETE:
if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
plater->GetEventHandler()->AddPendingEvent(VolumeDetachedEvent(EVT_VOLUME_DETACHED));
else if (lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
PDEV_BROADCAST_DEVICEINTERFACE lpdbi = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
// if (lpdbi->dbcc_classguid == GUID_DEVINTERFACE_VOLUME)
// printf("DBT_DEVICEARRIVAL %d - Media was removed: %ws\n", msg_count, lpdbi->dbcc_name);
if (lpdbi->dbcc_classguid == GUID_DEVINTERFACE_HID)
plater->GetEventHandler()->AddPendingEvent(HIDDeviceDetachedEvent(EVT_HID_DEVICE_DETACHED, boost::nowide::narrow(lpdbi->dbcc_name)));
}
break;
default:
break;
}
return true;
});
}
#endif // WIN32
static void generic_exception_handle()
{
@ -248,7 +294,10 @@ bool GUI_App::on_init_inner()
show_error(nullptr, ex.what());
}
register_dpi_event();
#ifdef WIN32
register_win32_dpi_event();
register_win32_device_notification_event();
#endif // WIN32
// Let the libslic3r know the callback, which will translate messages on demand.
Slic3r::I18N::set_translate_callback(libslic3r_translate_callback);

View file

@ -21,6 +21,10 @@
namespace Slic3r {
namespace GUI {
wxDEFINE_EVENT(EVT_HID_DEVICE_ATTACHED, HIDDeviceAttachedEvent);
wxDEFINE_EVENT(EVT_HID_DEVICE_DETACHED, HIDDeviceDetachedEvent);
wxDEFINE_EVENT(EVT_VOLUME_ATTACHED, VolumeAttachedEvent);
wxDEFINE_EVENT(EVT_VOLUME_DETACHED, VolumeDetachedEvent);
wxTopLevelWindow* find_toplevel_parent(wxWindow *window)
{

View file

@ -18,6 +18,8 @@
#include <wx/debug.h>
#include <wx/settings.h>
#include "Event.hpp"
class wxCheckBox;
class wxTopLevelWindow;
class wxRect;
@ -26,6 +28,19 @@ class wxRect;
namespace Slic3r {
namespace GUI {
#ifdef _WIN32
// USB HID attach / detach events from Windows OS.
using HIDDeviceAttachedEvent = Event<std::string>;
using HIDDeviceDetachedEvent = Event<std::string>;
wxDECLARE_EVENT(EVT_HID_DEVICE_ATTACHED, HIDDeviceAttachedEvent);
wxDECLARE_EVENT(EVT_HID_DEVICE_DETACHED, HIDDeviceDetachedEvent);
// Disk aka Volume attach / detach events from Windows OS.
using VolumeAttachedEvent = SimpleEvent;
using VolumeDetachedEvent = SimpleEvent;
wxDECLARE_EVENT(EVT_VOLUME_ATTACHED, VolumeAttachedEvent);
wxDECLARE_EVENT(EVT_VOLUME_DETACHED, VolumeDetachedEvent);
#endif /* _WIN32 */
wxTopLevelWindow* find_toplevel_parent(wxWindow *window);

View file

@ -31,6 +31,10 @@
#include <fstream>
#include "GUI_App.hpp"
#ifdef _WIN32
#include <dbt.h>
#endif // _WIN32
namespace Slic3r {
namespace GUI {
@ -104,6 +108,31 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
update_title();
// declare events
Bind(wxEVT_CREATE, [this](wxWindowCreateEvent& event) {
#ifdef _WIN32
//static GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED };
//static GUID GUID_DEVINTERFACE_DISK = { 0x53f56307, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b };
//static GUID GUID_DEVINTERFACE_VOLUME = { 0x71a27cdd, 0x812a, 0x11d0, 0xbe, 0xc7, 0x08, 0x00, 0x2b, 0xe2, 0x09, 0x2f };
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 };
// Register USB HID (Human Interface Devices) notifications to trigger the 3DConnexion enumeration.
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = { 0 };
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_HID;
m_hDeviceNotify = ::RegisterDeviceNotification(this->GetHWND(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
// or register for file handle change?
// DEV_BROADCAST_HANDLE NotificationFilter = { 0 };
// NotificationFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
// NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE;
#endif // _WIN32
// propagate event
event.Skip();
});
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) {
if (event.CanVeto() && !wxGetApp().check_unsaved_changes()) {
event.Veto();
@ -131,6 +160,11 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
// Called when closing the application and when switching the application language.
void MainFrame::shutdown()
{
#ifdef _WIN32
::UnregisterDeviceNotification(HDEVNOTIFY(m_hDeviceNotify));
m_hDeviceNotify = nullptr;
#endif // _WIN32
if (m_plater)
m_plater->stop_jobs();

View file

@ -141,6 +141,10 @@ public:
wxNotebook* m_tabpanel { nullptr };
wxProgressDialog* m_progress_dialog { nullptr };
std::shared_ptr<ProgressStatusBar> m_statusbar;
#ifdef _WIN32
void* m_hDeviceNotify { nullptr };
#endif // _WIN32
};
} // GUI

View file

@ -99,6 +99,25 @@ void Mouse3DController::State::append_button(unsigned int id, size_t /* input_qu
}
#ifdef WIN32
// Called by Win32 HID enumeration callback.
void Mouse3DController::device_attached(const std::string &device)
{
int vid = 0;
int pid = 0;
if (sscanf(device.c_str(), "\\\\?\\HID#VID_%x&PID_%x&", &vid, &pid) == 2) {
// BOOST_LOG_TRIVIAL(trace) << boost::format("Mouse3DController::device_attached(VID_%04xxPID_%04x)") % vid % pid;
// BOOST_LOG_TRIVIAL(trace) << "Mouse3DController::device_attached: " << device;
if (std::find(_3DCONNEXION_VENDORS.begin(), _3DCONNEXION_VENDORS.end(), vid) != _3DCONNEXION_VENDORS.end()) {
// Signal the worker thread to wake up and enumerate HID devices, if not connected at the moment.
// The message may come multiple times per each USB device. For example, some USB wireless dongles register as multiple HID sockets
// for multiple devices to connect to.
// Never mind, enumeration will be performed until connected.
m_wakeup = true;
m_stop_condition.notify_all();
}
}
}
// Filter out mouse scroll events produced by the 3DConnexion driver.
bool Mouse3DController::State::process_mouse_wheel()
{
@ -486,6 +505,11 @@ void Mouse3DController::run()
return;
}
#ifdef _WIN32
// Enumerate once just after thread start.
m_wakeup = true;
#endif // _WIN32
for (;;) {
{
tbb::mutex::scoped_lock lock(m_params_ui_mutex);
@ -518,7 +542,13 @@ bool Mouse3DController::connect_device()
{
// Wait for 2 seconds, but cancellable by m_stop.
std::unique_lock<std::mutex> lock(m_stop_condition_mutex);
m_stop_condition.wait_for(lock, std::chrono::seconds(2), [this]{ return this->m_stop; });
#ifdef _WIN32
// Wait indifinetely for the stop signal.
m_stop_condition.wait(lock, [this]{ return m_stop || m_wakeup; });
m_wakeup = false;
#else
m_stop_condition.wait_for(lock, std::chrono::seconds(2), [this]{ return m_stop; });
#endif
}
if (m_stop)
@ -528,10 +558,14 @@ bool Mouse3DController::connect_device()
hid_device_info* devices = hid_enumerate(0, 0);
if (devices == nullptr)
{
BOOST_LOG_TRIVIAL(error) << "Unable to enumerate HID devices";
BOOST_LOG_TRIVIAL(trace) << "Mouse3DController::connect_device() - no HID device enumerated.";
return false;
}
#ifdef _WIN32
BOOST_LOG_TRIVIAL(trace) << "Mouse3DController::connect_device() - enumerating HID devices.";
#endif // _WIN32
// Searches for 1st connected 3Dconnexion device
struct DeviceData
{
@ -785,6 +819,10 @@ void Mouse3DController::disconnect_device()
}
m_device_str.clear();
m_connected = false;
#ifdef _WIN32
// Enumerate once immediately after disconnect.
m_wakeup = true;
#endif // _WIN32
wxGetApp().plater()->get_camera().recover_from_free_camera();
wxGetApp().plater()->set_current_canvas_as_dirty();
wxWakeUpIdle();

View file

@ -148,6 +148,9 @@ class Mouse3DController
hid_device* m_device { nullptr };
// Using m_stop_condition_mutex to synchronize m_stop.
bool m_stop { false };
#ifdef _WIN32
std::atomic<bool> m_wakeup { false };
#endif /* _WIN32 */
// Mutex and condition variable for sleeping during the detection of 3DConnexion devices by polling while allowing
// cancellation before the end of the polling interval.
std::mutex m_stop_condition_mutex;
@ -185,6 +188,9 @@ public:
#endif // __APPLE__
#ifdef WIN32
// Called by Win32 HID enumeration callback.
void device_attached(const std::string &device);
// On Windows, the 3DConnexion driver sends out mouse wheel rotation events to an active application
// if the application does not register at the driver. This is a workaround to ignore these superfluous
// mouse wheel events.

View file

@ -2188,10 +2188,16 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
// Load the 3DConnexion device database.
mouse3d_controller.load_config(*wxGetApp().app_config);
// Start the background thread to detect and connect to a HID device (Windows and Linux).
// Connect to a 3DConnextion driver (OSX).
// Connect to a 3DConnextion driver (OSX).
mouse3d_controller.init();
#ifdef _WIN32
// Register an USB HID (Human Interface Device) attach event. evt contains Win32 path to the USB device containing VID, PID and other info.
// This event wakes up the Mouse3DController's background thread to enumerate HID devices, if the VID of the callback event
// is one of the 3D Mouse vendors (3DConnexion or Logitech).
this->q->Bind(EVT_HID_DEVICE_ATTACHED, [this](HIDDeviceAttachedEvent &evt) {
mouse3d_controller.device_attached(evt.data);
});
#endif /* _WIN32 */
this->q->Bind(EVT_REMOVABLE_DRIVE_EJECTED, [this](RemovableDriveEjectEvent &evt) {
if (evt.data.second) {
@ -2205,6 +2211,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
this->q->Bind(EVT_REMOVABLE_DRIVES_CHANGED, [this](RemovableDrivesChangedEvent &) { this->show_action_buttons(this->ready_to_slice); });
// Start the background thread and register this window as a target for update events.
wxGetApp().removable_drive_manager()->init(this->q);
#ifdef _WIN32
// Trigger enumeration of removable media on Win32 notification.
this->q->Bind(EVT_VOLUME_ATTACHED, [this](VolumeAttachedEvent &evt) { wxGetApp().removable_drive_manager()->volumes_changed(); });
this->q->Bind(EVT_VOLUME_DETACHED, [this](VolumeDetachedEvent &evt) { wxGetApp().removable_drive_manager()->volumes_changed(); });
#endif /* _WIN32 */
// Initialize the Undo / Redo stack with a first snapshot.
this->take_snapshot(_(L("New Project")));

View file

@ -56,7 +56,7 @@ std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() cons
volume_name.erase(volume_name.begin() + wcslen(volume_name.c_str()), volume_name.end());
if (! file_system_name.empty()) {
ULARGE_INTEGER free_space;
::GetDiskFreeSpaceExA(path.c_str(), &free_space, nullptr, nullptr);
::GetDiskFreeSpaceExW(wpath.c_str(), &free_space, nullptr, nullptr);
if (free_space.QuadPart > 0) {
path += "\\";
current_drives.emplace_back(DriveData{ boost::nowide::narrow(volume_name), path });
@ -86,7 +86,7 @@ void RemovableDriveManager::eject_drive()
// get handle to device
std::string mpath = "\\\\.\\" + m_last_save_path;
mpath = mpath.substr(0, mpath.size() - 1);
HANDLE handle = CreateFileA(mpath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
HANDLE handle = CreateFileW(boost::nowide::widen(mpath).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE) {
std::cerr << "Ejecting " << mpath << " failed " << GetLastError() << " \n";
assert(m_callback_evt_handler);
@ -128,7 +128,7 @@ std::string RemovableDriveManager::get_removable_drive_path(const std::string &p
return std::string();
std::size_t found = path.find_last_of("\\");
std::string new_path = path.substr(0, found);
int letter = PathGetDriveNumberA(new_path.c_str());
int letter = PathGetDriveNumberW(boost::nowide::widen(new_path).c_str());
for (const DriveData &drive_data : m_current_drives) {
char drive = drive_data.path[0];
if (drive == 'A' + letter)
@ -142,7 +142,7 @@ std::string RemovableDriveManager::get_removable_drive_from_path(const std::stri
tbb::mutex::scoped_lock lock(m_drives_mutex);
std::size_t found = path.find_last_of("\\");
std::string new_path = path.substr(0, found);
int letter = PathGetDriveNumberA(new_path.c_str());
int letter = PathGetDriveNumberW(boost::nowide::widen(new_path).c_str());
for (const DriveData &drive_data : m_current_drives) {
assert(! drive_data.path.empty());
if (drive_data.path.front() == 'A' + letter)
@ -151,93 +151,16 @@ std::string RemovableDriveManager::get_removable_drive_from_path(const std::stri
return std::string();
}
#if 0
// currently not used, left for possible future use
INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
// Called by Win32 Volume arrived / detached callback.
void RemovableDriveManager::volumes_changed()
{
// here we need to catch messeges about device removal
// problem is that when ejecting usb (how is it implemented above) there is no messege dispached. Only after physical removal of the device.
//uncomment register_window() in init() to register and comment update() in GUI_App.cpp (only for windows!) to stop recieving periodical updates
LRESULT lRet = 1;
static HDEVNOTIFY hDeviceNotify;
static constexpr GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72, 0x8a,0x6d,0xb5,0x4c,0x2b,0x4f,0xc8,0x35 };
switch (message)
{
case WM_CREATE:
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
NotificationFilter.dbcc_classguid = WceusbshGUID;
hDeviceNotify = RegisterDeviceNotification(hWnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
break;
case WM_DEVICECHANGE:
{
// here is the important
if(wParam == DBT_DEVICEREMOVECOMPLETE)
{
RemovableDriveManager::get_instance().update(0, true);
}
if (m_initialized) {
// Signal the worker thread to wake up and enumerate removable drives.
m_wakeup = true;
m_thread_stop_condition.notify_all();
}
break;
default:
// Send all other messages on to the default windows handler.
lRet = DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return lRet;
}
void RemovableDriveManager::register_window()
{
//creates new unvisible window that is recieving callbacks from system
// structure to register
// currently not used, left for possible future use
WNDCLASSEX wndClass;
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wndClass.hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(0));
wndClass.lpfnWndProc = reinterpret_cast<WNDPROC>(WinProcCallback);//this is callback
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hIcon = LoadIcon(0, IDI_APPLICATION);
wndClass.hbrBackground = CreateSolidBrush(RGB(192, 192, 192));
wndClass.hCursor = LoadCursor(0, IDC_ARROW);
wndClass.lpszClassName = L"PrusaSlicer_aux_class";
wndClass.lpszMenuName = NULL;
wndClass.hIconSm = wndClass.hIcon;
if(!RegisterClassEx(&wndClass))
{
DWORD err = GetLastError();
return;
}
HWND hWnd = CreateWindowEx(
WS_EX_NOACTIVATE,
L"PrusaSlicer_aux_class",
L"PrusaSlicer_aux_wnd",
WS_DISABLED, // style
CW_USEDEFAULT, 0,
640, 480,
NULL, NULL,
GetModuleHandle(NULL),
NULL);
if(hWnd == NULL)
{
DWORD err = GetLastError();
}
//ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
}
#endif
#else
namespace search_for_drives_internal
@ -424,9 +347,7 @@ void RemovableDriveManager::init(wxEvtHandler *callback_evt_handler)
m_initialized = true;
m_callback_evt_handler = callback_evt_handler;
#if _WIN32
//this->register_window_msw();
#elif __APPLE__
#if __APPLE__
this->register_window_osx();
#endif
@ -440,6 +361,10 @@ void RemovableDriveManager::init(wxEvtHandler *callback_evt_handler)
void RemovableDriveManager::shutdown()
{
#if __APPLE__
this->unregister_window_osx();
#endif
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
if (m_thread.joinable()) {
// Stop the worker thread, if running.
@ -455,12 +380,6 @@ void RemovableDriveManager::shutdown()
}
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
#if _WIN32
//this->unregister_window_msw();
#elif __APPLE__
this->unregister_window_osx();
#endif
m_initialized = false;
m_callback_evt_handler = nullptr;
}
@ -493,6 +412,10 @@ RemovableDriveManager::RemovableDrivesStatus RemovableDriveManager::status()
void RemovableDriveManager::update()
{
tbb::mutex::scoped_lock inside_update_lock;
#ifdef _WIN32
// All wake up calls up to now are now consumed when the drive enumeration starts.
m_wakeup = false;
#endif // _WIN32
if (inside_update_lock.try_acquire(m_inside_update_mutex)) {
// Got the lock without waiting. That means, the update was not running.
// Run the update.
@ -516,12 +439,21 @@ void RemovableDriveManager::update()
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
void RemovableDriveManager::thread_proc()
{
// Signal the worker thread to update initially.
m_wakeup = true;
for (;;) {
// Wait for 2 seconds before running the disk enumeration.
// Cancellable.
{
std::unique_lock<std::mutex> lck(m_thread_stop_mutex);
#ifdef _WIN32
// Wait 30 seconds for the stop signal, wake up time to time to remove those devices that the user ejected in file explorer
// or another application (for example in Cura). This is a workaround, as Windows does not send an event on software eject of a drive.
m_thread_stop_condition.wait_for(lck, std::chrono::seconds(30), [this]{ return m_stop || m_wakeup; });
#else
m_thread_stop_condition.wait_for(lck, std::chrono::seconds(2), [this]{ return m_stop; });
#endif
}
if (m_stop)
// Stop the worker thread.

View file

@ -84,6 +84,11 @@ public:
// It would be better to make this method private and friend to RemovableDriveManagerMM, but RemovableDriveManagerMM is an ObjectiveC class.
void update();
#ifdef _WIN32
// Called by Win32 Volume arrived / detached callback.
void volumes_changed();
#endif // _WIN32
private:
bool m_initialized { false };
wxEvtHandler* m_callback_evt_handler { nullptr };
@ -95,6 +100,9 @@ private:
std::condition_variable m_thread_stop_condition;
mutable std::mutex m_thread_stop_mutex;
bool m_stop { false };
#ifdef _WIN32
std::atomic<bool> m_wakeup { false };
#endif /* _WIN32 */
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
// Called from update() to enumerate removable drives.
@ -114,10 +122,7 @@ private:
// Set with set_and_verify_last_save_path() to a removable drive path to be ejected.
std::string m_last_save_path;
#if _WIN32
//registers for notifications by creating invisible window
//void register_window_msw();
#elif __APPLE__
#if __APPLE__
void register_window_osx();
void unregister_window_osx();
void list_devices(std::vector<DriveData> &out) const;