1 #ifndef slic3r_GUI_RemovableDriveManager_hpp_
2 #define slic3r_GUI_RemovableDriveManager_hpp_
3 
4 #include <vector>
5 #include <string>
6 
7 #include <boost/thread.hpp>
8 #include <mutex>
9 #include <condition_variable>
10 
11 // Custom wxWidget events
12 #include "Event.hpp"
13 
14 namespace Slic3r {
15 namespace GUI {
16 
17 struct DriveData
18 {
19 	std::string name;
20 	std::string path;
21 
clearSlic3r::GUI::DriveData22 	void clear() {
23 		name.clear();
24 		path.clear();
25 	}
emptySlic3r::GUI::DriveData26 	bool empty() const {
27 		return path.empty();
28 	}
29 };
30 
operator <(const DriveData & lhs,const DriveData & rhs)31 inline bool operator< (const DriveData &lhs, const DriveData &rhs) { return lhs.path < rhs.path; }
operator >(const DriveData & lhs,const DriveData & rhs)32 inline bool operator> (const DriveData &lhs, const DriveData &rhs) { return lhs.path > rhs.path; }
operator ==(const DriveData & lhs,const DriveData & rhs)33 inline bool operator==(const DriveData &lhs, const DriveData &rhs) { return lhs.path == rhs.path; }
34 
35 using RemovableDriveEjectEvent = Event<std::pair<DriveData, bool>>;
36 wxDECLARE_EVENT(EVT_REMOVABLE_DRIVE_EJECTED, RemovableDriveEjectEvent);
37 
38 using RemovableDrivesChangedEvent = SimpleEvent;
39 wxDECLARE_EVENT(EVT_REMOVABLE_DRIVES_CHANGED, RemovableDrivesChangedEvent);
40 
41 #if __APPLE__
42 	// Callbacks on device plug / unplug work reliably on OSX.
43 	#define REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
44 #endif // __APPLE__
45 
46 class RemovableDriveManager
47 {
48 public:
49 	RemovableDriveManager() = default;
50 	RemovableDriveManager(RemovableDriveManager const&) = delete;
51 	void operator=(RemovableDriveManager const&) = delete;
~RemovableDriveManager()52 	~RemovableDriveManager() { assert(! m_initialized); }
53 
54 	// Start the background thread and register this window as a target for update events.
55 	// Register for OSX notifications.
56 	void 		init(wxEvtHandler *callback_evt_handler);
57 	// Stop the background thread of the removable drive manager, so that no new updates will be sent out.
58 	// Deregister OSX notifications.
59 	void 		shutdown();
60 
61 	// Returns path to a removable media if it exists, prefering the input path.
62 	std::string get_removable_drive_path(const std::string &path);
is_path_on_removable_drive(const std::string & path)63 	bool        is_path_on_removable_drive(const std::string &path) { return this->get_removable_drive_path(path) == path; }
64 
65 	// Verify whether the path provided is on removable media. If so, save the path for further eject and return true, otherwise return false.
66 	bool 		set_and_verify_last_save_path(const std::string &path);
67 	// Eject drive of a file set by set_and_verify_last_save_path().
68 	// On Unix / OSX, the function blocks and sends out the EVT_REMOVABLE_DRIVE_EJECTED event on success.
69 	// On Windows, the function does not block, and the eject is detected in the background thread.
70 	void 		eject_drive();
71 
72 	// Status is used to retrieve info for showing UI buttons.
73 	// Status is called every time when change of UI buttons is possible therefore should not perform update.
74 	struct RemovableDrivesStatus {
75 		bool 	has_removable_drives { false };
76 		bool 	has_eject { false };
77 	};
78 	RemovableDrivesStatus status();
79 
80 	// Enumerates current drives and sends out wxWidget events on change or eject.
81 	// Called by each public method, by the background thread and from RemovableDriveManagerMM::on_device_unmount OSX notification handler.
82 	// Not to be called manually.
83 	// Public to be accessible from RemovableDriveManagerMM::on_device_unmount OSX notification handler.
84 	// It would be better to make this method private and friend to RemovableDriveManagerMM, but RemovableDriveManagerMM is an ObjectiveC class.
85 	void 		update();
set_exporting_finished(bool b)86 	void        set_exporting_finished(bool b) { m_exporting_finished = b; }
87 #ifdef _WIN32
88     // Called by Win32 Volume arrived / detached callback.
89 	void 		volumes_changed();
90 #endif // _WIN32
91 
92 private:
93 	bool 			 		m_initialized { false };
94 	wxEvtHandler*			m_callback_evt_handler { nullptr };
95 
96 #ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
97 	// Worker thread, worker thread synchronization and callbacks to the UI thread.
98 	void 					thread_proc();
99 	boost::thread 			m_thread;
100 	std::condition_variable m_thread_stop_condition;
101 	mutable std::mutex 		m_thread_stop_mutex;
102 	bool 					m_stop { false };
103 #ifdef _WIN32
104     std::atomic<bool>		m_wakeup { false };
105 #endif /* _WIN32 */
106 #endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
107 
108 	// Called from update() to enumerate removable drives.
109 	std::vector<DriveData> 	search_for_removable_drives() const;
110 
111 	// m_current_drives is guarded by m_drives_mutex
112 	// sorted ascending by path
113 	std::vector<DriveData> 	m_current_drives;
114 	mutable std::mutex 		m_drives_mutex;
115 	// Locking the update() function to avoid that the function is executed multiple times.
116 	mutable std::mutex 		m_inside_update_mutex;
117 
118 	// Returns drive path (same as path in DriveData) if exists otherwise empty string.
119 	std::string 			get_removable_drive_from_path(const std::string& path);
120 	// Returns iterator to a drive in m_current_drives with path equal to m_last_save_path or end().
121 	std::vector<DriveData>::const_iterator find_last_save_path_drive_data() const;
122 	// Set with set_and_verify_last_save_path() to a removable drive path to be ejected.
123 	std::string 			m_last_save_path;
124 	// Verifies that exporting was finished so drive can be ejected.
125 	// Set false by set_and_verify_last_save_path() that is called just before exporting.
126 	bool                    m_exporting_finished;
127 #if __APPLE__
128     void register_window_osx();
129     void unregister_window_osx();
130     void list_devices(std::vector<DriveData> &out) const;
131     // not used as of now
132     void eject_device(const std::string &path);
133     // Opaque pointer to RemovableDriveManagerMM
134     void *m_impl_osx;
135     boost::thread *m_eject_thread { nullptr };
136     void eject_thread_finish();
137 #endif
138 };
139 
140 }}
141 
142 #endif // slic3r_GUI_RemovableDriveManager_hpp_
143