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

@ -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.