mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-12 01:07:57 -06:00
Refactoring of RemovableDriveManager
1) On Windows and Linux, polling of removable drives is done at a background thread, not at a UI thread. 2) On OSX, there is no polling, but a OS notification callback. 3) Refactored for clarity of the RemovableDriveManager interface.
This commit is contained in:
parent
fa00180170
commit
baad542d15
11 changed files with 640 additions and 753 deletions
|
@ -1,6 +1,8 @@
|
|||
#include "RemovableDriveManager.hpp"
|
||||
#include <iostream>
|
||||
#include "boost/nowide/convert.hpp"
|
||||
#include <libslic3r/libslic3r.h>
|
||||
|
||||
#include <boost/nowide/convert.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#if _WIN32
|
||||
#include <windows.h>
|
||||
|
@ -9,11 +11,9 @@
|
|||
#include <shlwapi.h>
|
||||
|
||||
#include <Dbt.h>
|
||||
GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72,
|
||||
0x8a,0x6d,0xb5,0x4c,0x2b,0x4f,0xc8,0x35 };
|
||||
|
||||
#else
|
||||
//linux includes
|
||||
// unix, linux & OSX includes
|
||||
#include <errno.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
|
@ -26,124 +26,171 @@ GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72,
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
wxDEFINE_EVENT(EVT_REMOVABLE_DRIVE_EJECTED, RemovableDriveEjectEvent);
|
||||
wxDEFINE_EVENT(EVT_REMOVABLE_DRIVES_CHANGED, RemovableDrivesChangedEvent);
|
||||
|
||||
#if _WIN32
|
||||
/* currently not used, left for possible future use
|
||||
INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
*/
|
||||
void RemovableDriveManager::search_for_drives()
|
||||
std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() const
|
||||
{
|
||||
m_current_drives.clear();
|
||||
//get logical drives flags by letter in alphabetical order
|
||||
DWORD drives_mask = GetLogicalDrives();
|
||||
for (size_t i = 0; i < 26; i++)
|
||||
{
|
||||
if(drives_mask & (1 << i))
|
||||
{
|
||||
std::string path (1,(char)('A' + i));
|
||||
path+=":";
|
||||
UINT drive_type = GetDriveTypeA(path.c_str());
|
||||
DWORD drives_mask = ::GetLogicalDrives();
|
||||
|
||||
// Allocate the buffers before the loop.
|
||||
std::wstring volume_name;
|
||||
volume_name.resize(MAX_PATH + 1);
|
||||
std::wstring file_system_name;
|
||||
file_system_name.resize(MAX_PATH + 1);
|
||||
|
||||
// Iterate the Windows drives from 'A' to 'Z'
|
||||
std::vector<DriveData> current_drives;
|
||||
for (size_t i = 0; i < 26; ++ i)
|
||||
if (drives_mask & (1 << i)) {
|
||||
std::string path { char('A' + i), ':' };
|
||||
UINT drive_type = ::GetDriveTypeA(path.c_str());
|
||||
// DRIVE_REMOVABLE on W are sd cards and usb thumbnails (not usb harddrives)
|
||||
if (drive_type == DRIVE_REMOVABLE)
|
||||
{
|
||||
if (drive_type == DRIVE_REMOVABLE) {
|
||||
// get name of drive
|
||||
std::wstring wpath = boost::nowide::widen(path);
|
||||
std::wstring volume_name;
|
||||
volume_name.resize(1024);
|
||||
std::wstring file_system_name;
|
||||
file_system_name.resize(1024);
|
||||
LPWSTR lp_volume_name_buffer = new wchar_t;
|
||||
BOOL error = GetVolumeInformationW(wpath.c_str(), &volume_name[0], sizeof(volume_name), NULL, NULL, NULL, &file_system_name[0], sizeof(file_system_name));
|
||||
if(error != 0)
|
||||
{
|
||||
volume_name.erase(std::find(volume_name.begin(), volume_name.end(), '\0'), volume_name.end());
|
||||
if (file_system_name != L"")
|
||||
{
|
||||
BOOL error = ::GetVolumeInformationW(wpath.c_str(), volume_name.data(), sizeof(volume_name), nullptr, nullptr, nullptr, file_system_name.data(), sizeof(file_system_name));
|
||||
if (error != 0) {
|
||||
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, NULL, NULL);
|
||||
if (free_space.QuadPart > 0)
|
||||
{
|
||||
::GetDiskFreeSpaceExA(path.c_str(), &free_space, nullptr, nullptr);
|
||||
if (free_space.QuadPart > 0) {
|
||||
path += "\\";
|
||||
m_current_drives.push_back(DriveData(boost::nowide::narrow(volume_name), path));
|
||||
current_drives.emplace_back(DriveData{ boost::nowide::narrow(volume_name), path });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return current_drives;
|
||||
}
|
||||
|
||||
// Called from UI therefore it blocks the UI thread.
|
||||
// It also blocks updates at the worker thread.
|
||||
// Win32 implementation.
|
||||
void RemovableDriveManager::eject_drive()
|
||||
{
|
||||
if (m_last_save_path.empty())
|
||||
return;
|
||||
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
this->update();
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
auto it_drive_data = this->find_last_save_path_drive_data();
|
||||
if (it_drive_data != m_current_drives.end()) {
|
||||
// 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);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
std::cerr << "Ejecting " << mpath << " failed " << GetLastError() << " \n";
|
||||
return;
|
||||
}
|
||||
DWORD deviceControlRetVal(0);
|
||||
//these 3 commands should eject device safely but they dont, the device does disappear from file explorer but the "device was safely remove" notification doesnt trigger.
|
||||
//sd cards does trigger WM_DEVICECHANGE messege, usb drives dont
|
||||
DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||
DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||
// some implemenatations also calls IOCTL_STORAGE_MEDIA_REMOVAL here but it returns error to me
|
||||
BOOL error = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||
if (error == 0) {
|
||||
CloseHandle(handle);
|
||||
BOOST_LOG_TRIVIAL(error) << "Ejecting " << mpath << " failed " << deviceControlRetVal << " " << GetLastError() << " \n";
|
||||
return;
|
||||
}
|
||||
CloseHandle(handle);
|
||||
m_drive_data_last_eject = *it_drive_data;
|
||||
}
|
||||
}
|
||||
void RemovableDriveManager::eject_drive(const std::string &path)
|
||||
|
||||
std::string RemovableDriveManager::get_removable_drive_path(const std::string &path)
|
||||
{
|
||||
if(m_current_drives.empty())
|
||||
return;
|
||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
this->update();
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
if (m_current_drives.empty())
|
||||
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());
|
||||
for (const DriveData &drive_data : m_current_drives) {
|
||||
char drive = drive_data.path[0];
|
||||
if (drive == 'A' + letter)
|
||||
return path;
|
||||
}
|
||||
return m_current_drives.front().path;
|
||||
}
|
||||
|
||||
std::string RemovableDriveManager::get_removable_drive_from_path(const std::string& path)
|
||||
{
|
||||
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());
|
||||
for (const DriveData &drive_data : m_current_drives) {
|
||||
assert(! drive_data.path.empty());
|
||||
if (drive_data.path.front() == 'A' + letter)
|
||||
return drive_data.path;
|
||||
}
|
||||
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)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
if ((*it).path == path)
|
||||
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)
|
||||
{
|
||||
// get handle to device
|
||||
std::string mpath = "\\\\.\\" + 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);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
std::cerr << "Ejecting " << mpath << " failed " << GetLastError() << " \n";
|
||||
return;
|
||||
}
|
||||
DWORD deviceControlRetVal(0);
|
||||
//these 3 commands should eject device safely but they dont, the device does disappear from file explorer but the "device was safely remove" notification doesnt trigger.
|
||||
//sd cards does trigger WM_DEVICECHANGE messege, usb drives dont
|
||||
|
||||
DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||
DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||
// some implemenatations also calls IOCTL_STORAGE_MEDIA_REMOVAL here but it returns error to me
|
||||
BOOL error = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||
if (error == 0)
|
||||
{
|
||||
CloseHandle(handle);
|
||||
std::cerr << "Ejecting " << mpath << " failed " << deviceControlRetVal << " " << GetLastError() << " \n";
|
||||
return;
|
||||
}
|
||||
CloseHandle(handle);
|
||||
m_did_eject = true;
|
||||
m_current_drives.erase(it);
|
||||
m_ejected_path = m_last_save_path;
|
||||
m_ejected_name = m_last_save_name;
|
||||
break;
|
||||
RemovableDriveManager::get_instance().update(0, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path)
|
||||
{
|
||||
if (m_current_drives.empty())
|
||||
return false;
|
||||
std::size_t found = path.find_last_of("\\");
|
||||
std::string new_path = path.substr(0, found);
|
||||
int letter = PathGetDriveNumberA(new_path.c_str());
|
||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
||||
{
|
||||
char drive = (*it).path[0];
|
||||
if (drive == ('A' + letter))
|
||||
return true;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Send all other messages on to the default windows handler.
|
||||
lRet = DefWindowProc(hWnd, message, wParam, lParam);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
std::string RemovableDriveManager::get_drive_from_path(const std::string& path)
|
||||
{
|
||||
std::size_t found = path.find_last_of("\\");
|
||||
std::string new_path = path.substr(0, found);
|
||||
int letter = PathGetDriveNumberA(new_path.c_str());
|
||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
||||
{
|
||||
char drive = (*it).path[0];
|
||||
if (drive == ('A' + letter))
|
||||
return (*it).path;
|
||||
}
|
||||
return "";
|
||||
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
|
||||
// currently not used, left for possible future use
|
||||
WNDCLASSEX wndClass;
|
||||
wndClass.cbSize = sizeof(WNDCLASSEX);
|
||||
wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
|
||||
|
@ -179,432 +226,291 @@ void RemovableDriveManager::register_window()
|
|||
}
|
||||
//ShowWindow(hWnd, SW_SHOWNORMAL);
|
||||
UpdateWindow(hWnd);
|
||||
*/
|
||||
}
|
||||
/* currently not used, left for possible future use
|
||||
INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
namespace search_for_drives_internal
|
||||
{
|
||||
// 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;
|
||||
|
||||
switch (message)
|
||||
static bool compare_filesystem_id(const std::string &path_a, const std::string &path_b)
|
||||
{
|
||||
case WM_CREATE:
|
||||
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
|
||||
struct stat buf;
|
||||
stat(path_a.c_str() ,&buf);
|
||||
dev_t id_a = buf.st_dev;
|
||||
stat(path_b.c_str() ,&buf);
|
||||
dev_t id_b = buf.st_dev;
|
||||
return id_a == id_b;
|
||||
}
|
||||
|
||||
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:
|
||||
void inspect_file(const std::string &path, const std::string &parent_path, std::vector<DriveData> &out)
|
||||
{
|
||||
// here is the important
|
||||
if(wParam == DBT_DEVICEREMOVECOMPLETE)
|
||||
{
|
||||
- RemovableDriveManager::get_instance().update(0, true);
|
||||
//confirms if the file is removable drive and adds it to vector
|
||||
|
||||
//if not same file system - could be removable drive
|
||||
if (! compare_filesystem_id(path, parent_path)) {
|
||||
//free space
|
||||
boost::filesystem::space_info si = boost::filesystem::space(path);
|
||||
if (si.available != 0) {
|
||||
//user id
|
||||
struct stat buf;
|
||||
stat(path.c_str(), &buf);
|
||||
uid_t uid = buf.st_uid;
|
||||
std::string username(std::getenv("USER"));
|
||||
struct passwd *pw = getpwuid(uid);
|
||||
if (pw != 0 && pw->pw_name == username)
|
||||
out.emplace_back(DriveData{ boost::filesystem::basename(boost::filesystem::path(path)), path });
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Send all other messages on to the default windows handler.
|
||||
lRet = DefWindowProc(hWnd, message, wParam, lParam);
|
||||
break;
|
||||
}
|
||||
return lRet;
|
||||
|
||||
}
|
||||
*/
|
||||
#else
|
||||
void RemovableDriveManager::search_for_drives()
|
||||
{
|
||||
|
||||
m_current_drives.clear();
|
||||
|
||||
#if __APPLE__
|
||||
// if on macos obj-c class will enumerate
|
||||
if(m_rdmmm)
|
||||
{
|
||||
m_rdmmm->list_devices();
|
||||
}
|
||||
#else
|
||||
|
||||
static void search_path(const std::string &path, const std::string &parent_path, std::vector<DriveData> &out)
|
||||
{
|
||||
glob_t globbuf;
|
||||
globbuf.gl_offs = 2;
|
||||
int error = glob(path.c_str(), GLOB_TILDE, NULL, &globbuf);
|
||||
if (error == 0) {
|
||||
for (size_t i = 0; i < globbuf.gl_pathc; ++ i)
|
||||
inspect_file(globbuf.gl_pathv[i], parent_path, out);
|
||||
} else {
|
||||
//if error - path probably doesnt exists so function just exits
|
||||
//std::cout<<"glob error "<< error<< "\n";
|
||||
}
|
||||
globfree(&globbuf);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() const
|
||||
{
|
||||
std::vector<DriveData> current_drives;
|
||||
|
||||
#if __APPLE__
|
||||
|
||||
this->list_devices(current_drives);
|
||||
|
||||
#else
|
||||
|
||||
//search /media/* folder
|
||||
search_path("/media/*", "/media");
|
||||
search_for_drives_internal::search_path("/media/*", "/media", current_drives);
|
||||
|
||||
//search_path("/Volumes/*", "/Volumes");
|
||||
std::string path(std::getenv("USER"));
|
||||
std::string pp(path);
|
||||
|
||||
{
|
||||
//search /media/USERNAME/* folder
|
||||
pp = "/media/"+pp;
|
||||
path = "/media/" + path + "/*";
|
||||
search_path(path, pp);
|
||||
//search /media/USERNAME/* folder
|
||||
pp = "/media/"+pp;
|
||||
path = "/media/" + path + "/*";
|
||||
search_for_drives_internal::search_path(path, pp, current_drives);
|
||||
|
||||
//search /run/media/USERNAME/* folder
|
||||
path = "/run" + path;
|
||||
pp = "/run"+pp;
|
||||
search_path(path, pp);
|
||||
|
||||
}
|
||||
//search /run/media/USERNAME/* folder
|
||||
path = "/run" + path;
|
||||
pp = "/run"+pp;
|
||||
search_for_drives_internal::search_path(path, pp, current_drives);
|
||||
#endif
|
||||
}
|
||||
void RemovableDriveManager::search_path(const std::string &path,const std::string &parent_path)
|
||||
{
|
||||
glob_t globbuf;
|
||||
globbuf.gl_offs = 2;
|
||||
int error = glob(path.c_str(), GLOB_TILDE, NULL, &globbuf);
|
||||
if(error == 0)
|
||||
{
|
||||
for(size_t i = 0; i < globbuf.gl_pathc; i++)
|
||||
{
|
||||
inspect_file(globbuf.gl_pathv[i], parent_path);
|
||||
}
|
||||
}else
|
||||
{
|
||||
//if error - path probably doesnt exists so function just exits
|
||||
//std::cout<<"glob error "<< error<< "\n";
|
||||
}
|
||||
|
||||
globfree(&globbuf);
|
||||
}
|
||||
void RemovableDriveManager::inspect_file(const std::string &path, const std::string &parent_path)
|
||||
{
|
||||
//confirms if the file is removable drive and adds it to vector
|
||||
|
||||
//if not same file system - could be removable drive
|
||||
if(!compare_filesystem_id(path, parent_path))
|
||||
{
|
||||
//free space
|
||||
boost::filesystem::space_info si = boost::filesystem::space(path);
|
||||
if(si.available != 0)
|
||||
{
|
||||
//user id
|
||||
struct stat buf;
|
||||
stat(path.c_str(), &buf);
|
||||
uid_t uid = buf.st_uid;
|
||||
std::string username(std::getenv("USER"));
|
||||
struct passwd *pw = getpwuid(uid);
|
||||
if (pw != 0 && pw->pw_name == username)
|
||||
m_current_drives.push_back(DriveData(boost::filesystem::basename(boost::filesystem::path(path)), path));
|
||||
}
|
||||
|
||||
}
|
||||
return current_drives;
|
||||
}
|
||||
bool RemovableDriveManager::compare_filesystem_id(const std::string &path_a, const std::string &path_b)
|
||||
|
||||
// Called from UI therefore it blocks the UI thread.
|
||||
// It also blocks updates at the worker thread.
|
||||
// Unix & OSX implementation.
|
||||
void RemovableDriveManager::eject_drive()
|
||||
{
|
||||
struct stat buf;
|
||||
stat(path_a.c_str() ,&buf);
|
||||
dev_t id_a = buf.st_dev;
|
||||
stat(path_b.c_str() ,&buf);
|
||||
dev_t id_b = buf.st_dev;
|
||||
return id_a == id_b;
|
||||
}
|
||||
void RemovableDriveManager::eject_drive(const std::string &path)
|
||||
{
|
||||
if (m_current_drives.empty())
|
||||
if (m_last_save_path.empty())
|
||||
return;
|
||||
|
||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
||||
{
|
||||
if((*it).path == path)
|
||||
{
|
||||
|
||||
std::string correct_path(path);
|
||||
for (size_t i = 0; i < correct_path.size(); ++i)
|
||||
{
|
||||
if(correct_path[i]==' ')
|
||||
{
|
||||
correct_path = correct_path.insert(i,1,'\\');
|
||||
i++;
|
||||
}
|
||||
}
|
||||
//std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n";
|
||||
// there is no usable command in c++ so terminal command is used instead
|
||||
// but neither triggers "succesful safe removal messege"
|
||||
std::string command = "";
|
||||
#if __APPLE__
|
||||
//m_rdmmm->eject_device(path);
|
||||
command = "diskutil unmount ";
|
||||
#else
|
||||
command = "umount ";
|
||||
#endif
|
||||
command += correct_path;
|
||||
int err = system(command.c_str());
|
||||
if(err)
|
||||
{
|
||||
std::cerr<<"Ejecting failed\n";
|
||||
return;
|
||||
}
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
this->update();
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
m_did_eject = true;
|
||||
m_current_drives.erase(it);
|
||||
m_ejected_path = m_last_save_path;
|
||||
m_ejected_name = m_last_save_name;
|
||||
break;
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
auto it_drive_data = this->find_last_save_path_drive_data();
|
||||
if (it_drive_data != m_current_drives.end()) {
|
||||
std::string correct_path(m_last_save_path);
|
||||
for (size_t i = 0; i < correct_path.size(); ++i)
|
||||
if (correct_path[i]==' ') {
|
||||
correct_path = correct_path.insert(i,1,'\\');
|
||||
++ i;
|
||||
}
|
||||
//std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n";
|
||||
// there is no usable command in c++ so terminal command is used instead
|
||||
// but neither triggers "succesful safe removal messege"
|
||||
std::string command =
|
||||
#if __APPLE__
|
||||
//this->eject_device(m_last_save_path);
|
||||
"diskutil unmount ";
|
||||
#else
|
||||
"umount ";
|
||||
#endif
|
||||
command += correct_path;
|
||||
int err = system(command.c_str());
|
||||
if (err) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Ejecting " << m_last_save_path << " failed";
|
||||
return;
|
||||
}
|
||||
|
||||
m_drive_data_last_eject = *it_drive_data;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path)
|
||||
std::string RemovableDriveManager::get_removable_drive_path(const std::string &path)
|
||||
{
|
||||
if (m_current_drives.empty())
|
||||
return false;
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
this->update();
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
std::size_t found = path.find_last_of("/");
|
||||
std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path;
|
||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
||||
{
|
||||
if(compare_filesystem_id(new_path, (*it).path))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
for (const DriveData &data : m_current_drives)
|
||||
if (search_for_drives_internal::compare_filesystem_id(new_path, data.path))
|
||||
return path;
|
||||
return m_current_drives.empty() ? std::string() : m_current_drives.front().path;
|
||||
}
|
||||
std::string RemovableDriveManager::get_drive_from_path(const std::string& path)
|
||||
|
||||
std::string RemovableDriveManager::get_removable_drive_from_path(const std::string& path)
|
||||
{
|
||||
std::size_t found = path.find_last_of("/");
|
||||
std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path;
|
||||
|
||||
// trim the filename
|
||||
found = new_path.find_last_of("/");
|
||||
new_path = new_path.substr(0, found);
|
||||
|
||||
//check if same filesystem
|
||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
||||
{
|
||||
if (compare_filesystem_id(new_path, (*it).path))
|
||||
return (*it).path;
|
||||
}
|
||||
return "";
|
||||
// check if same filesystem
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
for (const DriveData &drive_data : m_current_drives)
|
||||
if (search_for_drives_internal::compare_filesystem_id(new_path, drive_data.path))
|
||||
return drive_data.path;
|
||||
return std::string();
|
||||
}
|
||||
#endif
|
||||
|
||||
RemovableDriveManager::RemovableDriveManager():
|
||||
m_drives_count(0),
|
||||
m_last_update(0),
|
||||
m_last_save_path(""),
|
||||
m_last_save_name(""),
|
||||
m_last_save_path_verified(false),
|
||||
m_is_writing(false),
|
||||
m_did_eject(false),
|
||||
m_plater_ready_to_slice(true),
|
||||
m_ejected_path(""),
|
||||
m_ejected_name("")
|
||||
#if __APPLE__
|
||||
, m_rdmmm(new RDMMMWrapper())
|
||||
#endif
|
||||
{}
|
||||
RemovableDriveManager::~RemovableDriveManager()
|
||||
void RemovableDriveManager::init(wxEvtHandler *callback_evt_handler)
|
||||
{
|
||||
#if __APPLE__
|
||||
delete m_rdmmm;
|
||||
#endif
|
||||
}
|
||||
void RemovableDriveManager::init()
|
||||
{
|
||||
//add_callback([](void) { RemovableDriveManager::get_instance().print(); });
|
||||
assert(! m_initialized);
|
||||
assert(m_callback_evt_handler == nullptr);
|
||||
|
||||
if (m_initialized)
|
||||
return;
|
||||
|
||||
m_initialized = true;
|
||||
m_callback_evt_handler = callback_evt_handler;
|
||||
|
||||
#if _WIN32
|
||||
//register_window();
|
||||
//this->register_window_msw();
|
||||
#elif __APPLE__
|
||||
m_rdmmm->register_window();
|
||||
this->register_window_osx();
|
||||
#endif
|
||||
update(0, true);
|
||||
}
|
||||
bool RemovableDriveManager::update(const long time,const bool check)
|
||||
{
|
||||
if(time != 0) //time = 0 is forced update
|
||||
{
|
||||
long diff = m_last_update - time;
|
||||
if(diff <= -2)
|
||||
{
|
||||
m_last_update = time;
|
||||
}else
|
||||
{
|
||||
return false; // return value shouldnt matter if update didnt run
|
||||
}
|
||||
}
|
||||
search_for_drives();
|
||||
if (m_drives_count != m_current_drives.size())
|
||||
{
|
||||
if (check)
|
||||
{
|
||||
check_and_notify();
|
||||
}
|
||||
m_drives_count = m_current_drives.size();
|
||||
}
|
||||
return !m_current_drives.empty();
|
||||
|
||||
#ifdef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
this->update();
|
||||
#else // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
// Don't call update() manually, as the UI triggered APIs call this->update() anyways.
|
||||
m_thread = boost::thread((boost::bind(&RemovableDriveManager::thread_proc, this)));
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
}
|
||||
|
||||
bool RemovableDriveManager::is_drive_mounted(const std::string &path) const
|
||||
void RemovableDriveManager::shutdown()
|
||||
{
|
||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
||||
{
|
||||
if ((*it).path == path)
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
if (m_thread.joinable()) {
|
||||
// Stop the worker thread, if running.
|
||||
{
|
||||
return true;
|
||||
// Notify the worker thread to cancel wait on detection polling.
|
||||
std::lock_guard<std::mutex> lck(m_thread_stop_mutex);
|
||||
m_stop = true;
|
||||
}
|
||||
m_thread_stop_condition.notify_all();
|
||||
// Wait for the worker thread to stop.
|
||||
m_thread.join();
|
||||
m_stop = false;
|
||||
}
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
#if _WIN32
|
||||
//this->unregister_window_msw();
|
||||
#elif __APPLE__
|
||||
this->unregister_window_osx();
|
||||
#endif
|
||||
|
||||
m_initialized = false;
|
||||
}
|
||||
|
||||
bool RemovableDriveManager::set_and_verify_last_save_path(const std::string &path)
|
||||
{
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
this->update();
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
m_last_save_path = this->get_removable_drive_from_path(path);
|
||||
return ! m_last_save_path.empty();
|
||||
}
|
||||
|
||||
RemovableDriveManager::RemovableDrivesStatus RemovableDriveManager::status()
|
||||
{
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
this->update();
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
RemovableDriveManager::RemovableDrivesStatus out;
|
||||
{
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
out.has_eject = this->find_last_save_path_drive_data() != m_current_drives.end();
|
||||
out.has_removable_drives = ! m_current_drives.empty();
|
||||
}
|
||||
if (! out.has_eject)
|
||||
m_last_save_path.clear();
|
||||
return out;
|
||||
}
|
||||
|
||||
// Update is called from thread_proc() and from most of the public methods on demand.
|
||||
void RemovableDriveManager::update()
|
||||
{
|
||||
std::vector<DriveData> current_drives = this->search_for_removable_drives();
|
||||
|
||||
// Post update events.
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
std::sort(current_drives.begin(), current_drives.end());
|
||||
if (current_drives != m_current_drives) {
|
||||
if (! m_drive_data_last_eject.empty() && std::find(current_drives.begin(), current_drives.end(), m_drive_data_last_eject) == current_drives.end()) {
|
||||
assert(m_callback_evt_handler);
|
||||
if (m_callback_evt_handler)
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::move(m_drive_data_last_eject)));
|
||||
m_drive_data_last_eject.clear();
|
||||
} else {
|
||||
assert(m_callback_evt_handler);
|
||||
if (m_callback_evt_handler)
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDrivesChangedEvent(EVT_REMOVABLE_DRIVES_CHANGED));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
m_current_drives = std::move(current_drives);
|
||||
}
|
||||
std::string RemovableDriveManager::get_drive_path()
|
||||
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
void RemovableDriveManager::thread_proc()
|
||||
{
|
||||
if (m_current_drives.size() == 0)
|
||||
{
|
||||
reset_last_save_path();
|
||||
return "";
|
||||
}
|
||||
if (m_last_save_path_verified)
|
||||
return m_last_save_path;
|
||||
return m_current_drives.back().path;
|
||||
}
|
||||
std::string RemovableDriveManager::get_last_save_path() const
|
||||
{
|
||||
if (!m_last_save_path_verified)
|
||||
return "";
|
||||
return m_last_save_path;
|
||||
}
|
||||
std::string RemovableDriveManager::get_last_save_name() const
|
||||
{
|
||||
return m_last_save_name;
|
||||
}
|
||||
std::vector<DriveData> RemovableDriveManager::get_all_drives() const
|
||||
{
|
||||
return m_current_drives;
|
||||
}
|
||||
void RemovableDriveManager::check_and_notify()
|
||||
{
|
||||
if(m_drive_count_changed_callback)
|
||||
{
|
||||
m_drive_count_changed_callback(m_plater_ready_to_slice);
|
||||
}
|
||||
if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() && !is_drive_mounted(m_last_save_path))
|
||||
{
|
||||
for (auto it = m_callbacks.begin(); it != m_callbacks.end(); ++it)
|
||||
for (;;) {
|
||||
// Wait for 2 seconds before running the disk enumeration.
|
||||
// Cancellable.
|
||||
{
|
||||
(*it)();
|
||||
std::unique_lock<std::mutex> lck(m_thread_stop_mutex);
|
||||
m_thread_stop_condition.wait_for(lck, std::chrono::seconds(2), [this]{ return m_stop; });
|
||||
}
|
||||
if (m_stop)
|
||||
// Stop the worker thread.
|
||||
break;
|
||||
// Update m_current drives and send out update events.
|
||||
this->update();
|
||||
}
|
||||
}
|
||||
void RemovableDriveManager::add_remove_callback(std::function<void()> callback)
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
std::vector<DriveData>::const_iterator RemovableDriveManager::find_last_save_path_drive_data() const
|
||||
{
|
||||
m_callbacks.push_back(callback);
|
||||
return Slic3r::binary_find_by_predicate(m_current_drives.begin(), m_current_drives.end(),
|
||||
[this](const DriveData &data){ return data.path < m_last_save_path; },
|
||||
[this](const DriveData &data){ return data.path == m_last_save_path; });
|
||||
}
|
||||
void RemovableDriveManager::erase_callbacks()
|
||||
{
|
||||
m_callbacks.clear();
|
||||
}
|
||||
void RemovableDriveManager::set_drive_count_changed_callback(std::function<void(const bool)> callback)
|
||||
{
|
||||
m_drive_count_changed_callback = callback;
|
||||
}
|
||||
void RemovableDriveManager::set_plater_ready_to_slice(bool b)
|
||||
{
|
||||
m_plater_ready_to_slice = b;
|
||||
}
|
||||
void RemovableDriveManager::set_last_save_path(const std::string& path)
|
||||
{
|
||||
if(m_last_save_path_verified)// if old path is on drive
|
||||
{
|
||||
if(get_drive_from_path(path) != "") //and new is too, rewrite the path
|
||||
{
|
||||
m_last_save_path_verified = false;
|
||||
m_last_save_path = path;
|
||||
}//else do nothing
|
||||
}else
|
||||
{
|
||||
m_last_save_path = path;
|
||||
}
|
||||
}
|
||||
void RemovableDriveManager::verify_last_save_path()
|
||||
{
|
||||
std::string last_drive = get_drive_from_path(m_last_save_path);
|
||||
if (last_drive != "")
|
||||
{
|
||||
m_last_save_path_verified = true;
|
||||
m_last_save_path = last_drive;
|
||||
m_last_save_name = get_drive_name(last_drive);
|
||||
}else
|
||||
{
|
||||
reset_last_save_path();
|
||||
}
|
||||
}
|
||||
std::string RemovableDriveManager::get_drive_name(const std::string& path) const
|
||||
{
|
||||
if (m_current_drives.size() == 0)
|
||||
return "";
|
||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
||||
{
|
||||
if ((*it).path == path)
|
||||
{
|
||||
return (*it).name;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
bool RemovableDriveManager::is_last_drive_removed()
|
||||
{
|
||||
if(!m_last_save_path_verified)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool r = !is_drive_mounted(m_last_save_path);
|
||||
if (r)
|
||||
{
|
||||
reset_last_save_path();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
bool RemovableDriveManager::is_last_drive_removed_with_update(const long time)
|
||||
{
|
||||
update(time, false);
|
||||
return is_last_drive_removed();
|
||||
}
|
||||
void RemovableDriveManager::reset_last_save_path()
|
||||
{
|
||||
m_last_save_path_verified = false;
|
||||
m_last_save_path = "";
|
||||
m_last_save_name = "";
|
||||
}
|
||||
void RemovableDriveManager::set_is_writing(const bool b)
|
||||
{
|
||||
m_is_writing = b;
|
||||
if (b)
|
||||
{
|
||||
m_did_eject = false;
|
||||
}
|
||||
}
|
||||
bool RemovableDriveManager::get_is_writing() const
|
||||
{
|
||||
return m_is_writing;
|
||||
}
|
||||
bool RemovableDriveManager::get_did_eject() const
|
||||
{
|
||||
return m_did_eject;
|
||||
}
|
||||
void RemovableDriveManager::set_did_eject(const bool b)
|
||||
{
|
||||
m_did_eject = b;
|
||||
}
|
||||
size_t RemovableDriveManager::get_drives_count() const
|
||||
{
|
||||
return m_current_drives.size();
|
||||
}
|
||||
std::string RemovableDriveManager::get_ejected_path() const
|
||||
{
|
||||
return m_ejected_path;
|
||||
}
|
||||
std::string RemovableDriveManager::get_ejected_name() const
|
||||
{
|
||||
return m_ejected_name;
|
||||
}
|
||||
}}//namespace Slicer::Gui
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue