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:
parent
3684eea53d
commit
2f6326a2eb
10 changed files with 207 additions and 109 deletions
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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")));
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue