Welcome to mirror list, hosted at ThFree Co, Russian Federation.

Mouse3DController.hpp « GUI « slic3r « src - github.com/supermerill/SuperSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 8f03606b06132bcf63404a36718a7035ec9627f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#ifndef slic3r_Mouse3DController_hpp_
#define slic3r_Mouse3DController_hpp_

// Enabled debug output to console and extended imgui dialog
#define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT 0

#include "libslic3r/Point.hpp"

#include "hidapi.h"

#include <queue>
#include <thread>
#include <vector>
#include <chrono>
#include <condition_variable>

#include <tbb/mutex.h>

namespace Slic3r {

class AppConfig;

namespace GUI {

struct Camera;
class GLCanvas3D;

class Mouse3DController
{
	// Parameters, which are configured by the ImGUI dialog when pressing Ctrl+M.
	// The UI thread modifies a copy of the parameters and indicates to the background thread that there was a change
	// to copy the parameters.
	struct Params
	{
		static constexpr double DefaultTranslationScale = 2.5;
		static constexpr double MaxTranslationDeadzone = 0.2;
		static constexpr double DefaultTranslationDeadzone = 0.5 * MaxTranslationDeadzone;
		static constexpr float  DefaultRotationScale = 1.0f;
		static constexpr float  MaxRotationDeadzone = 0.2f;
		static constexpr float  DefaultRotationDeadzone = 0.5f * MaxRotationDeadzone;
		static constexpr double DefaultZoomScale = 0.1;

        template <typename Number>
        struct CustomParameters
        {
            Number scale;
            Number deadzone;
        };

        CustomParameters<double> translation { DefaultTranslationScale, DefaultTranslationDeadzone };
        CustomParameters<float>  rotation { DefaultRotationScale, DefaultRotationDeadzone };
        CustomParameters<double> zoom { DefaultZoomScale, 0.0 };
        // Do not process button presses from 3DConnexion device, let the user map the 3DConnexion keys in 3DConnexion driver.
        bool 					 buttons_enabled { false };
        // The default value of 15 for max_size seems to work fine on all platforms
        // The effects of changing this value can be tested by setting ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT to 1
        // and playing with the imgui dialog which shows by pressing CTRL+M
        size_t 					 input_queue_max_size { 15 };
	};

	// Queue of the 3DConnexion input events (translations, rotations, button presses).
    class State
    {
    public:
        struct QueueItem {
            static QueueItem translation(const Vec3d &translation) { QueueItem out; out.vector = translation; out.type_or_buttons = TranslationType; return out; }
            static QueueItem rotation(const Vec3d &rotation) { QueueItem out; out.vector = rotation; out.type_or_buttons = RotationType; return out; }
            static QueueItem buttons(unsigned int buttons) { QueueItem out; out.type_or_buttons = buttons; return out; }

            bool 			 is_translation() const { return this->type_or_buttons == TranslationType; }
            bool 			 is_rotation() const { return this->type_or_buttons == RotationType; }
            bool 			 is_buttons() const { return ! this->is_translation() && ! this->is_rotation(); }

            Vec3d        	 vector;
            unsigned int 	 type_or_buttons;

            static constexpr unsigned int TranslationType = std::numeric_limits<unsigned int>::max();
            static constexpr unsigned int RotationType    = TranslationType - 1;
        };

    private:
    	// m_input_queue is accessed by the background thread and by the UI thread. Access to m_input_queue
    	// is guarded with m_input_queue_mutex.
        std::deque<QueueItem> m_input_queue;
        mutable tbb::mutex	  m_input_queue_mutex;

#ifdef WIN32
        // When the 3Dconnexion driver is running the system gets, by default, mouse wheel events when rotations around the X axis are detected.
        // We want to filter these out because we are getting the data directly from the device, bypassing the driver, and those mouse wheel events interfere
        // by triggering unwanted zoom in/out of the scene
        // The following variable is used to count the potential mouse wheel events triggered and is updated by: 
        // Mouse3DController::collect_input() through the call to the append_rotation() method
        // GLCanvas3D::on_mouse_wheel() through the call to the process_mouse_wheel() method
        // GLCanvas3D::on_idle() through the call to the apply() method
        unsigned int 		  m_mouse_wheel_counter { 0 };
#endif /* WIN32 */

    public:
        // Called by the background thread or by by Mouse3DHandlerMac.mm when a new event is received from 3DConnexion device.
        void append_translation(const Vec3d& translation, size_t input_queue_max_size);
        void append_rotation(const Vec3f& rotation, size_t input_queue_max_size);
        void append_button(unsigned int id, size_t input_queue_max_size);

#ifdef WIN32
        // Called by GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
        // to filter out spurious mouse scroll events produced by the 3DConnexion driver on Windows.
        bool process_mouse_wheel();
#endif /* WIN32 */

#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
        Vec3d               get_first_vector_of_type(unsigned int type) const {
            tbb::mutex::scoped_lock lock(m_input_queue_mutex);
            auto it = std::find_if(m_input_queue.begin(), m_input_queue.end(), [type](const QueueItem& item) { return item.type_or_buttons == type; });
            return (it == m_input_queue.end()) ? Vec3d::Zero() : it->vector;
        }
        size_t              input_queue_size_current() const { 
        	tbb::mutex::scoped_lock lock(m_input_queue_mutex); 
        	return m_input_queue.size(); 
        }
        std::atomic<size_t> input_queue_max_size_achieved;
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT

        // Apply the 3DConnexion events stored in the input queue, reset the input queue.
        // Returns true if any change to the camera took place.
        bool apply(const Params &params, Camera& camera);
    };

    // Background thread works with this copy.
    Params 				m_params;
    // UI thread will read / write this copy.
    Params 				m_params_ui;
    bool 	            m_params_ui_changed { false };
    mutable tbb::mutex	m_params_ui_mutex;

    // This is a database of parametes of all 3DConnexion devices ever connected.
    // This database is loaded from AppConfig on application start and it is stored to AppConfig on application exit.
    // We need to do that as the AppConfig is not thread safe and we need read the parameters on device connect / disconnect,
    // which is now done by a background thread.
    std::map<std::string, Params> m_params_by_device;

    mutable State 		m_state;
    std::atomic<bool> 	m_connected { false };
    std::string 		m_device_str;

#if ! __APPLE__
    // Worker thread for enumerating devices, connecting, reading data from the device and closing the device.
    std::thread 		m_thread;
    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;
   	std::condition_variable m_stop_condition;
#endif

   	// Is the ImGUI dialog shown? Accessed from UI thread only.
    mutable bool 		m_show_settings_dialog { false };
    // Set to true when ther user closes the dialog by clicking on [X] or [Close] buttons. Accessed from UI thread only.
    mutable bool 		m_settings_dialog_closed_by_user { false };

public:
	// Load the device parameter database from appconfig. To be called on application startup.
	void load_config(const AppConfig &appconfig);
	// Store the device parameter database back to appconfig. To be called on application closeup.
	void save_config(AppConfig &appconfig) const;
	// Start the background thread to detect and connect to a HID device (Windows and Linux).
	// Connect to a 3DConnextion driver (OSX).
	// Call load_config() before init().
    void init();
	// Stop the background thread (Windows and Linux).
	// Disconnect from a 3DConnextion driver (OSX).
	// Call save_config() after shutdown().
    void shutdown();

    bool connected() const { return m_connected; }

#if __APPLE__
    // Interfacing with the Objective C code (MouseHandlerMac.mm)
    void connected(std::string device_name);
    void disconnected();
    typedef std::array<double, 6> DataPacketAxis;
	// Unpack a 3DConnexion packet provided by the 3DConnexion driver into m_state. Called by Mouse3DHandlerMac.mm
    bool handle_input(const DataPacketAxis& packet);
#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.
    bool process_mouse_wheel() { return m_state.process_mouse_wheel(); }
#endif // WIN32

    // Apply the received 3DConnexion mouse events to the camera. Called from the UI rendering thread.
    bool apply(Camera& camera);

    bool is_settings_dialog_shown() const { return m_show_settings_dialog; }
    void show_settings_dialog(bool show) { m_show_settings_dialog = show && this->connected(); }
    void render_settings_dialog(GLCanvas3D& canvas) const;

#if ! __APPLE__
private:
    bool connect_device();
    void disconnect_device();

    // secondary thread methods
    void run();
    void collect_input();

    typedef std::array<unsigned char, 13> DataPacketRaw;

	// Unpack raw 3DConnexion HID packet of a wired 3D mouse into m_state. Called by the worker thread.
    static bool handle_input(const DataPacketRaw& packet, const int packet_lenght, const Params &params, State &state_in_out);
    // The following is called by handle_input() from the worker thread.
    static bool handle_packet(const DataPacketRaw& packet, const Params &params, State &state_in_out);
    static bool handle_wireless_packet(const DataPacketRaw& packet, const Params &params, State &state_in_out);
    static bool handle_packet_translation(const DataPacketRaw& packet, const Params &params, State &state_in_out);
    static bool handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte, const Params &params, State &state_in_out);
    static bool handle_packet_button(const DataPacketRaw& packet, unsigned int packet_size, const Params &params, State &state_in_out);
#endif /* __APPLE__ */
};

} // namespace GUI
} // namespace Slic3r


#endif // slic3r_Mouse3DController_hpp_