diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 48ee9e32c2..cc6c066904 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,7 @@ #ifdef __WXMSW__ #include +#include #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(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); diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp index 1e452b220e..6bce54dbc5 100644 --- a/src/slic3r/GUI/GUI_Utils.cpp +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -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) { diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index f7bebd5778..0d5249e254 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -18,6 +18,8 @@ #include #include +#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; +using HIDDeviceDetachedEvent = Event; +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); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index defd0b53af..1e22359ab2 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -31,6 +31,10 @@ #include #include "GUI_App.hpp" +#ifdef _WIN32 +#include +#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(); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 2ccd77666e..8c8b98090a 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -141,6 +141,10 @@ public: wxNotebook* m_tabpanel { nullptr }; wxProgressDialog* m_progress_dialog { nullptr }; std::shared_ptr m_statusbar; + +#ifdef _WIN32 + void* m_hDeviceNotify { nullptr }; +#endif // _WIN32 }; } // GUI diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 3c1ffeb3e2..32b6739e63 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -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 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(); diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index e686e12cc2..8f03606b06 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -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 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. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2a25762a8f..218677931a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -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"))); diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index d1f49f089b..41f0175eba 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -56,7 +56,7 @@ std::vector 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(GetModuleHandle(0)); - wndClass.lpfnWndProc = reinterpret_cast(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 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. diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp index 79ab4787d5..e1a8d6faf1 100644 --- a/src/slic3r/GUI/RemovableDriveManager.hpp +++ b/src/slic3r/GUI/RemovableDriveManager.hpp @@ -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 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 &out) const;