2019-09-27 12:52:19 +00:00
# include "libslic3r/libslic3r.h"
# include "Mouse3DController.hpp"
2019-10-03 09:38:31 +00:00
# include "Camera.hpp"
2019-09-27 12:52:19 +00:00
# include "GUI_App.hpp"
# include "PresetBundle.hpp"
2019-10-03 08:26:28 +00:00
# include "AppConfig.hpp"
2019-12-16 12:35:45 +00:00
# include "GLCanvas3D.hpp"
2019-09-27 12:52:19 +00:00
# include <wx/glcanvas.h>
2019-10-02 13:55:26 +00:00
# include <boost/nowide/convert.hpp>
2019-10-04 08:59:27 +00:00
# include <boost/log/trivial.hpp>
2019-10-02 13:55:26 +00:00
# include "I18N.hpp"
2019-10-09 12:01:13 +00:00
# include <bitset>
2020-01-21 13:34:22 +00:00
//unofficial linux lib
//#include <spnav.h>
2019-09-30 12:58:51 +00:00
// WARN: If updating these lists, please also update resources/udev/90-3dconnexion.rules
2019-09-27 12:52:19 +00:00
static const std : : vector < int > _3DCONNEXION_VENDORS =
{
0x046d , // LOGITECH = 1133 // Logitech (3Dconnexion is made by Logitech)
0x256F // 3DCONNECTION = 9583 // 3Dconnexion
} ;
2019-10-08 11:38:08 +00:00
// See: https://github.com/FreeSpacenav/spacenavd/blob/a9eccf34e7cac969ee399f625aef827f4f4aaec6/src/dev.c#L202
2019-09-27 12:52:19 +00:00
static const std : : vector < int > _3DCONNEXION_DEVICES =
{
2019-10-08 11:38:08 +00:00
0xc603 , /* 50691 spacemouse plus XT */
0xc605 , /* 50693 cadman */
0xc606 , /* 50694 spacemouse classic */
0xc621 , /* 50721 spaceball 5000 */
0xc623 , /* 50723 space traveller */
0xc625 , /* 50725 space pilot */
0xc626 , /* 50726 space navigator *TESTED* */
0xc627 , /* 50727 space explorer */
0xc628 , /* 50728 space navigator for notebooks*/
0xc629 , /* 50729 space pilot pro*/
0xc62b , /* 50731 space mouse pro*/
2019-10-09 12:01:13 +00:00
0xc62e , /* 50734 spacemouse wireless (USB cable) *TESTED* */
0xc62f , /* 50735 spacemouse wireless receiver */
2019-10-08 11:38:08 +00:00
0xc631 , /* 50737 spacemouse pro wireless *TESTED* */
0xc632 , /* 50738 spacemouse pro wireless receiver */
0xc633 , /* 50739 spacemouse enterprise */
2019-10-09 12:01:13 +00:00
0xc635 , /* 50741 spacemouse compact *TESTED* */
2019-10-08 11:38:08 +00:00
0xc636 , /* 50742 spacemouse module */
0xc640 , /* 50752 nulooq */
2019-10-11 13:29:57 +00:00
0xc652 , /* 50770 3Dconnexion universal receiver *TESTED* */
2019-09-27 12:52:19 +00:00
} ;
namespace Slic3r {
namespace GUI {
2020-03-04 10:36:36 +00:00
2019-10-09 13:23:30 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2020-03-04 10:36:36 +00:00
template < typename T >
void update_maximum ( std : : atomic < T > & maximum_value , T const & value ) noexcept
2019-10-08 06:44:50 +00:00
{
2020-03-04 10:36:36 +00:00
T prev_value = maximum_value ;
while ( prev_value < value & & ! maximum_value . compare_exchange_weak ( prev_value , value ) ) ;
2019-10-08 06:44:50 +00:00
}
2020-03-04 10:36:36 +00:00
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-10-08 06:44:50 +00:00
2020-03-04 10:36:36 +00:00
void Mouse3DController : : State : : append_translation ( const Vec3d & translation , size_t input_queue_max_size )
2019-10-10 09:55:17 +00:00
{
2020-03-04 10:36:36 +00:00
tbb : : mutex : : scoped_lock lock ( m_input_queue_mutex ) ;
while ( m_input_queue . size ( ) > = input_queue_max_size )
m_input_queue . pop_front ( ) ;
m_input_queue . emplace_back ( QueueItem : : translation ( translation ) ) ;
2019-10-10 09:55:17 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2020-03-04 10:36:36 +00:00
update_maximum ( input_queue_max_size_achieved , m_input_queue . size ( ) ) ;
2019-10-10 09:55:17 +00:00
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
}
2020-03-04 10:36:36 +00:00
void Mouse3DController : : State : : append_rotation ( const Vec3f & rotation , size_t input_queue_max_size )
2019-10-10 09:55:17 +00:00
{
2020-03-04 10:36:36 +00:00
tbb : : mutex : : scoped_lock lock ( m_input_queue_mutex ) ;
while ( m_input_queue . size ( ) > = input_queue_max_size )
m_input_queue . pop_front ( ) ;
m_input_queue . emplace_back ( QueueItem : : rotation ( rotation . cast < double > ( ) ) ) ;
# ifdef WIN32
if ( rotation . x ( ) ! = 0.0f )
+ + m_mouse_wheel_counter ;
# endif // WIN32
2019-10-10 09:55:17 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2020-03-04 10:36:36 +00:00
update_maximum ( input_queue_max_size_achieved , m_input_queue . size ( ) ) ;
2019-10-10 09:55:17 +00:00
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
}
2020-03-04 10:36:36 +00:00
void Mouse3DController : : State : : append_button ( unsigned int id , size_t /* input_queue_max_size */ )
2019-10-10 09:55:17 +00:00
{
2020-03-04 10:36:36 +00:00
tbb : : mutex : : scoped_lock lock ( m_input_queue_mutex ) ;
m_input_queue . emplace_back ( QueueItem : : buttons ( id ) ) ;
2019-10-10 09:55:17 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2020-03-04 10:36:36 +00:00
update_maximum ( input_queue_max_size_achieved , m_input_queue . size ( ) ) ;
2019-10-10 09:55:17 +00:00
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
}
2020-03-04 10:36:36 +00:00
# ifdef WIN32
2020-03-13 13:19:02 +00:00
// 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 ( ) ;
}
}
}
2020-03-04 10:36:36 +00:00
// Filter out mouse scroll events produced by the 3DConnexion driver.
2019-10-08 06:44:50 +00:00
bool Mouse3DController : : State : : process_mouse_wheel ( )
2019-09-27 12:52:19 +00:00
{
2020-03-04 10:36:36 +00:00
tbb : : mutex : : scoped_lock lock ( m_input_queue_mutex ) ;
if ( m_mouse_wheel_counter = = 0 )
// No 3DConnexion rotation has been captured since the last mouse scroll event.
2019-10-08 11:38:08 +00:00
return false ;
2020-03-04 10:36:36 +00:00
if ( std : : find_if ( m_input_queue . begin ( ) , m_input_queue . end ( ) , [ ] ( const QueueItem & item ) { return item . is_rotation ( ) ; } ) ! = m_input_queue . end ( ) ) {
// There is a rotation stored in the queue. Suppress one mouse scroll event.
- - m_mouse_wheel_counter ;
2019-10-08 06:44:50 +00:00
return true ;
}
2020-03-04 10:36:36 +00:00
m_mouse_wheel_counter = 0 ;
2019-10-11 07:16:20 +00:00
return true ;
2019-09-27 12:52:19 +00:00
}
2020-03-04 10:36:36 +00:00
# endif // WIN32
2019-09-27 12:52:19 +00:00
2020-03-04 10:36:36 +00:00
bool Mouse3DController : : State : : apply ( const Mouse3DController : : Params & params , Camera & camera )
2019-10-10 09:55:17 +00:00
{
2020-03-04 10:36:36 +00:00
if ( ! wxGetApp ( ) . IsActive ( ) )
2019-09-27 12:52:19 +00:00
return false ;
2020-03-04 10:36:36 +00:00
std : : deque < QueueItem > input_queue ;
2019-09-27 12:52:19 +00:00
{
2020-03-04 10:36:36 +00:00
// Atomically move m_input_queue to input_queue.
tbb : : mutex : : scoped_lock lock ( m_input_queue_mutex ) ;
input_queue = std : : move ( m_input_queue ) ;
m_input_queue . clear ( ) ;
2019-09-27 12:52:19 +00:00
}
2020-03-04 10:36:36 +00:00
for ( const QueueItem & input_queue_item : input_queue ) {
if ( input_queue_item . is_translation ( ) ) {
2020-03-20 16:19:20 +00:00
Vec3d translation = params . swap_yz ? Vec3d ( input_queue_item . vector . x ( ) , - input_queue_item . vector . z ( ) , input_queue_item . vector . y ( ) ) : input_queue_item . vector ;
2020-03-20 12:09:42 +00:00
double zoom_factor = camera . min_zoom ( ) / camera . get_zoom ( ) ;
2020-03-04 10:36:36 +00:00
camera . set_target ( camera . get_target ( ) + zoom_factor * params . translation . scale * ( translation . x ( ) * camera . get_dir_right ( ) + translation . z ( ) * camera . get_dir_up ( ) ) ) ;
2020-03-10 12:43:49 +00:00
if ( translation . y ( ) ! = 0.0 )
camera . update_zoom ( params . zoom . scale * translation . y ( ) ) ;
} else if ( input_queue_item . is_rotation ( ) ) {
2020-03-20 15:13:08 +00:00
Vec3d rot = params . rotation . scale * input_queue_item . vector * ( PI / 180. ) ;
if ( params . swap_yz )
2020-03-20 16:19:20 +00:00
rot = Vec3d ( rot . x ( ) , - rot . z ( ) , rot . y ( ) ) ;
2020-03-20 15:13:08 +00:00
camera . rotate_local_around_target ( Vec3d ( rot . x ( ) , - rot . z ( ) , rot . y ( ) ) ) ;
2020-03-04 10:36:36 +00:00
break ;
} else {
assert ( input_queue_item . is_buttons ( ) ) ;
switch ( input_queue_item . type_or_buttons ) {
case 0 : camera . update_zoom ( 1.0 ) ; break ;
case 1 : camera . update_zoom ( - 1.0 ) ; break ;
default : break ;
}
}
2019-09-27 12:52:19 +00:00
}
2020-03-04 10:36:36 +00:00
return ! input_queue . empty ( ) ;
2019-09-27 12:52:19 +00:00
}
2020-03-04 10:36:36 +00:00
// Load the device parameter database from appconfig. To be called on application startup.
void Mouse3DController : : load_config ( const AppConfig & appconfig )
2019-09-27 12:52:19 +00:00
{
2020-03-04 10:36:36 +00:00
// We do not synchronize m_params_by_device with the background thread explicitely
// as there should be a full memory barrier executed once the background thread is started.
m_params_by_device . clear ( ) ;
for ( const std : : string & device_name : appconfig . get_mouse_device_names ( ) ) {
double translation_speed = 4.0 ;
float rotation_speed = 4.0 ;
double translation_deadzone = Params : : DefaultTranslationDeadzone ;
float rotation_deadzone = Params : : DefaultRotationDeadzone ;
double zoom_speed = 2.0 ;
2020-03-20 15:13:08 +00:00
bool swap_yz = false ;
2020-03-20 12:09:42 +00:00
appconfig . get_mouse_device_translation_speed ( device_name , translation_speed ) ;
2020-03-04 10:36:36 +00:00
appconfig . get_mouse_device_translation_deadzone ( device_name , translation_deadzone ) ;
appconfig . get_mouse_device_rotation_speed ( device_name , rotation_speed ) ;
appconfig . get_mouse_device_rotation_deadzone ( device_name , rotation_deadzone ) ;
appconfig . get_mouse_device_zoom_speed ( device_name , zoom_speed ) ;
2020-03-20 15:13:08 +00:00
appconfig . get_mouse_device_swap_yz ( device_name , swap_yz ) ;
2020-03-20 12:09:42 +00:00
// clamp to valid values
2020-03-04 10:36:36 +00:00
Params params ;
params . translation . scale = Params : : DefaultTranslationScale * std : : clamp ( translation_speed , 0.1 , 10.0 ) ;
params . translation . deadzone = std : : clamp ( translation_deadzone , 0.0 , Params : : MaxTranslationDeadzone ) ;
params . rotation . scale = Params : : DefaultRotationScale * std : : clamp ( rotation_speed , 0.1f , 10.0f ) ;
params . rotation . deadzone = std : : clamp ( rotation_deadzone , 0.0f , Params : : MaxRotationDeadzone ) ;
params . zoom . scale = Params : : DefaultZoomScale * std : : clamp ( zoom_speed , 0.1 , 10.0 ) ;
2020-03-20 15:13:08 +00:00
params . swap_yz = swap_yz ;
2020-03-20 12:09:42 +00:00
m_params_by_device [ device_name ] = std : : move ( params ) ;
2020-03-04 10:36:36 +00:00
}
2019-09-27 12:52:19 +00:00
}
2020-03-04 10:36:36 +00:00
// Store the device parameter database back to appconfig. To be called on application closeup.
void Mouse3DController : : save_config ( AppConfig & appconfig ) const
2019-09-27 12:52:19 +00:00
{
2020-03-04 10:36:36 +00:00
// We do not synchronize m_params_by_device with the background thread explicitely
// as there should be a full memory barrier executed once the background thread is stopped.
for ( const std : : pair < std : : string , Params > & key_value_pair : m_params_by_device ) {
const std : : string & device_name = key_value_pair . first ;
const Params & params = key_value_pair . second ;
// Store current device parameters into the config
2020-03-20 12:09:42 +00:00
appconfig . set_mouse_device ( device_name , params . translation . scale / Params : : DefaultTranslationScale , params . translation . deadzone ,
2020-03-20 15:13:08 +00:00
params . rotation . scale / Params : : DefaultRotationScale , params . rotation . deadzone , params . zoom . scale / Params : : DefaultZoomScale , params . swap_yz ) ;
2020-03-20 12:09:42 +00:00
}
2019-09-27 12:52:19 +00:00
}
2019-10-03 10:16:59 +00:00
bool Mouse3DController : : apply ( Camera & camera )
{
2019-10-04 08:59:27 +00:00
// check if the user unplugged the device
2020-03-04 10:36:36 +00:00
if ( ! m_connected ) {
2019-12-16 12:35:45 +00:00
// hides the settings dialog if the user un-plug the device
m_show_settings_dialog = false ;
m_settings_dialog_closed_by_user = false ;
2019-10-04 08:59:27 +00:00
}
2020-03-04 10:36:36 +00:00
return m_state . apply ( m_params , camera ) ;
2019-10-03 10:16:59 +00:00
}
2019-12-16 12:35:45 +00:00
void Mouse3DController : : render_settings_dialog ( GLCanvas3D & canvas ) const
2019-10-02 13:55:26 +00:00
{
2020-03-04 10:36:36 +00:00
if ( ! m_show_settings_dialog | | ! m_connected )
2019-10-02 13:55:26 +00:00
return ;
2019-12-16 12:35:45 +00:00
// when the user clicks on [X] or [Close] button we need to trigger
// an extra frame to let the dialog disappear
if ( m_settings_dialog_closed_by_user )
{
m_show_settings_dialog = false ;
m_settings_dialog_closed_by_user = false ;
canvas . request_extra_frame ( ) ;
return ;
}
2019-10-02 13:55:26 +00:00
2020-03-04 10:36:36 +00:00
Params params_copy ;
bool params_changed = false ;
{
tbb : : mutex : : scoped_lock lock ( m_params_ui_mutex ) ;
params_copy = m_params_ui ;
}
2019-12-16 12:35:45 +00:00
Size cnv_size = canvas . get_canvas_size ( ) ;
ImGuiWrapper & imgui = * wxGetApp ( ) . imgui ( ) ;
imgui . set_next_window_pos ( 0.5f * ( float ) cnv_size . get_width ( ) , 0.5f * ( float ) cnv_size . get_height ( ) , ImGuiCond_Always , 0.5f , 0.5f ) ;
2019-10-02 13:55:26 +00:00
2019-12-16 12:35:45 +00:00
static ImVec2 last_win_size ( 0.0f , 0.0f ) ;
bool shown = true ;
if ( imgui . begin ( _ ( L ( " 3Dconnexion settings " ) ) , & shown , ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse ) )
{
if ( shown )
{
ImVec2 win_size = ImGui : : GetWindowSize ( ) ;
if ( ( last_win_size . x ! = win_size . x ) | | ( last_win_size . y ! = win_size . y ) )
{
// when the user clicks on [X] button, the next time the dialog is shown
// has a dummy size, so we trigger an extra frame to let it have the correct size
last_win_size = win_size ;
canvas . request_extra_frame ( ) ;
}
2019-10-02 13:55:26 +00:00
2019-12-16 12:35:45 +00:00
const ImVec4 & color = ImGui : : GetStyleColorVec4 ( ImGuiCol_Separator ) ;
ImGui : : PushStyleColor ( ImGuiCol_Text , color ) ;
imgui . text ( _ ( L ( " Device: " ) ) ) ;
ImGui : : PopStyleColor ( ) ;
ImGui : : SameLine ( ) ;
imgui . text ( m_device_str ) ;
2019-10-02 13:55:26 +00:00
2019-12-16 12:35:45 +00:00
ImGui : : Separator ( ) ;
ImGui : : PushStyleColor ( ImGuiCol_Text , color ) ;
imgui . text ( _ ( L ( " Speed: " ) ) ) ;
ImGui : : PopStyleColor ( ) ;
2019-10-02 13:55:26 +00:00
2020-03-04 10:36:36 +00:00
float translation_scale = ( float ) params_copy . translation . scale / Params : : DefaultTranslationScale ;
if ( imgui . slider_float ( _ ( L ( " Translation " ) ) + " ##1 " , & translation_scale , 0.1f , 10.0f , " %.1f " ) ) {
params_copy . translation . scale = Params : : DefaultTranslationScale * ( double ) translation_scale ;
params_changed = true ;
}
2019-10-02 13:55:26 +00:00
2020-03-04 10:36:36 +00:00
float rotation_scale = params_copy . rotation . scale / Params : : DefaultRotationScale ;
if ( imgui . slider_float ( _ ( L ( " Rotation " ) ) + " ##1 " , & rotation_scale , 0.1f , 10.0f , " %.1f " ) ) {
params_copy . rotation . scale = Params : : DefaultRotationScale * rotation_scale ;
params_changed = true ;
}
2019-10-08 12:32:05 +00:00
2020-03-04 10:36:36 +00:00
float zoom_scale = params_copy . zoom . scale / Params : : DefaultZoomScale ;
if ( imgui . slider_float ( _ ( L ( " Zoom " ) ) , & zoom_scale , 0.1f , 10.0f , " %.1f " ) ) {
params_copy . zoom . scale = Params : : DefaultZoomScale * zoom_scale ;
params_changed = true ;
}
2020-01-03 13:42:52 +00:00
2019-12-16 12:35:45 +00:00
ImGui : : Separator ( ) ;
ImGui : : PushStyleColor ( ImGuiCol_Text , color ) ;
imgui . text ( _ ( L ( " Deadzone: " ) ) ) ;
ImGui : : PopStyleColor ( ) ;
2019-10-08 12:32:05 +00:00
2020-03-04 10:36:36 +00:00
float translation_deadzone = ( float ) params_copy . translation . deadzone ;
if ( imgui . slider_float ( _ ( L ( " Translation " ) ) + " / " + _ ( L ( " Zoom " ) ) , & translation_deadzone , 0.0f , ( float ) Params : : MaxTranslationDeadzone , " %.2f " ) ) {
params_copy . translation . deadzone = ( double ) translation_deadzone ;
params_changed = true ;
}
2019-10-08 12:32:05 +00:00
2020-03-04 10:36:36 +00:00
float rotation_deadzone = params_copy . rotation . deadzone ;
if ( imgui . slider_float ( _ ( L ( " Rotation " ) ) + " ##2 " , & rotation_deadzone , 0.0f , Params : : MaxRotationDeadzone , " %.2f " ) ) {
params_copy . rotation . deadzone = rotation_deadzone ;
params_changed = true ;
}
2019-10-02 13:55:26 +00:00
2020-03-20 12:09:42 +00:00
ImGui : : Separator ( ) ;
ImGui : : PushStyleColor ( ImGuiCol_Text , color ) ;
imgui . text ( _ ( L ( " Options: " ) ) ) ;
ImGui : : PopStyleColor ( ) ;
2020-03-20 15:13:08 +00:00
bool swap_yz = params_copy . swap_yz ;
if ( imgui . checkbox ( " Swap Y/Z axes " , swap_yz ) ) {
params_copy . swap_yz = swap_yz ;
2020-03-20 12:09:42 +00:00
params_changed = true ;
}
2019-10-09 12:18:43 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-12-16 12:35:45 +00:00
ImGui : : Separator ( ) ;
ImGui : : Separator ( ) ;
ImGui : : PushStyleColor ( ImGuiCol_Text , color ) ;
imgui . text ( " DEBUG: " ) ;
imgui . text ( " Vectors: " ) ;
ImGui : : PopStyleColor ( ) ;
2020-03-04 10:36:36 +00:00
Vec3f translation = m_state . get_first_vector_of_type ( State : : QueueItem : : TranslationType ) . cast < float > ( ) ;
Vec3f rotation = m_state . get_first_vector_of_type ( State : : QueueItem : : RotationType ) . cast < float > ( ) ;
2019-12-16 12:35:45 +00:00
ImGui : : InputFloat3 ( " Translation##3 " , translation . data ( ) , " %.3f " , ImGuiInputTextFlags_ReadOnly ) ;
ImGui : : InputFloat3 ( " Rotation##3 " , rotation . data ( ) , " %.3f " , ImGuiInputTextFlags_ReadOnly ) ;
ImGui : : PushStyleColor ( ImGuiCol_Text , color ) ;
imgui . text ( " Queue size: " ) ;
ImGui : : PopStyleColor ( ) ;
2020-03-04 10:36:36 +00:00
int input_queue_size_current [ 2 ] = { int ( m_state . input_queue_size_current ( ) ) , int ( m_state . input_queue_max_size_achieved ) } ;
ImGui : : InputInt2 ( " Current##4 " , input_queue_size_current , ImGuiInputTextFlags_ReadOnly ) ;
2019-12-16 12:35:45 +00:00
2020-03-04 10:36:36 +00:00
int input_queue_size_param = int ( params_copy . input_queue_max_size ) ;
if ( ImGui : : InputInt ( " Max size " , & input_queue_size_param , 1 , 1 , ImGuiInputTextFlags_ReadOnly ) )
2019-12-16 12:35:45 +00:00
{
2020-03-04 10:36:36 +00:00
if ( input_queue_size_param > 0 ) {
params_copy . input_queue_max_size = input_queue_size_param ;
params_changed = true ;
}
2019-12-16 12:35:45 +00:00
}
ImGui : : Separator ( ) ;
ImGui : : PushStyleColor ( ImGuiCol_Text , color ) ;
imgui . text ( " Camera: " ) ;
ImGui : : PopStyleColor ( ) ;
Vec3f target = wxGetApp ( ) . plater ( ) - > get_camera ( ) . get_target ( ) . cast < float > ( ) ;
ImGui : : InputFloat3 ( " Target " , target . data ( ) , " %.3f " , ImGuiInputTextFlags_ReadOnly ) ;
2019-10-09 12:18:43 +00:00
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-12-16 12:35:45 +00:00
ImGui : : Separator ( ) ;
if ( imgui . button ( _ ( L ( " Close " ) ) ) )
{
// the user clicked on the [Close] button
m_settings_dialog_closed_by_user = true ;
canvas . set_as_dirty ( ) ;
}
}
else
{
// the user clicked on the [X] button
m_settings_dialog_closed_by_user = true ;
canvas . set_as_dirty ( ) ;
}
}
2019-10-09 12:18:43 +00:00
2019-10-02 13:55:26 +00:00
imgui . end ( ) ;
2020-03-04 10:36:36 +00:00
if ( params_changed ) {
// Synchronize front end parameters to back end.
tbb : : mutex : : scoped_lock lock ( m_params_ui_mutex ) ;
auto pthis = const_cast < Mouse3DController * > ( this ) ;
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
if ( params_copy . input_queue_max_size ! = params_copy . input_queue_max_size )
// Reset the statistics counter.
m_state . input_queue_max_size_achieved = 0 ;
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
pthis - > m_params_ui = params_copy ;
pthis - > m_params_ui_changed = true ;
}
2019-10-02 13:55:26 +00:00
}
2020-03-04 10:36:36 +00:00
# if __APPLE__
void Mouse3DController : : connected ( std : : string device_name )
2019-09-27 12:52:19 +00:00
{
2020-03-09 09:36:55 +00:00
assert ( ! m_connected ) ;
assert ( m_device_str . empty ( ) ) ;
2020-03-04 10:36:36 +00:00
m_device_str = device_name ;
// Copy the parameters for m_device_str into the current parameters.
if ( auto it_params = m_params_by_device . find ( m_device_str ) ; it_params ! = m_params_by_device . end ( ) ) {
tbb : : mutex : : scoped_lock lock ( m_params_ui_mutex ) ;
m_params = m_params_ui = it_params - > second ;
}
m_connected = true ;
}
2019-11-25 09:34:42 +00:00
2020-03-04 10:36:36 +00:00
void Mouse3DController : : disconnected ( )
{
// Copy the current parameters for m_device_str into the parameter database.
2020-03-09 09:36:55 +00:00
assert ( m_connected = = ! m_device_str . empty ( ) ) ;
if ( m_connected ) {
2020-03-04 10:36:36 +00:00
tbb : : mutex : : scoped_lock lock ( m_params_ui_mutex ) ;
m_params_by_device [ m_device_str ] = m_params_ui ;
2020-03-09 09:36:55 +00:00
m_device_str . clear ( ) ;
m_connected = false ;
2020-03-13 13:57:45 +00:00
wxGetApp ( ) . plater ( ) - > CallAfter ( [ ] ( ) {
Plater * plater = wxGetApp ( ) . plater ( ) ;
if ( plater ! = nullptr ) {
plater - > get_camera ( ) . recover_from_free_camera ( ) ;
plater - > set_current_canvas_as_dirty ( ) ;
}
} ) ;
2020-03-04 10:36:36 +00:00
}
}
2019-09-27 12:52:19 +00:00
2020-03-04 11:18:59 +00:00
bool Mouse3DController : : handle_input ( const DataPacketAxis & packet )
2020-03-04 10:36:36 +00:00
{
if ( ! wxGetApp ( ) . IsActive ( ) )
2020-03-04 11:18:59 +00:00
return false ;
2020-03-04 10:36:36 +00:00
{
// Synchronize parameters between the UI thread and the background thread.
//FIXME is this necessary on OSX? Are these notifications triggered from the main thread or from a worker thread?
tbb : : mutex : : scoped_lock lock ( m_params_ui_mutex ) ;
if ( m_params_ui_changed ) {
m_params = m_params_ui ;
m_params_ui_changed = false ;
}
}
2020-01-21 13:34:22 +00:00
2020-03-04 10:36:36 +00:00
bool updated = false ;
2020-03-04 11:18:59 +00:00
// translation
double deadzone = m_params . translation . deadzone ;
2020-03-04 10:36:36 +00:00
Vec3d translation ( std : : abs ( packet [ 0 ] ) > deadzone ? - packet [ 0 ] : 0.0 ,
std : : abs ( packet [ 1 ] ) > deadzone ? packet [ 1 ] : 0.0 ,
std : : abs ( packet [ 2 ] ) > deadzone ? packet [ 2 ] : 0.0 ) ;
2020-03-04 11:18:59 +00:00
if ( ! translation . isApprox ( Vec3d : : Zero ( ) ) ) {
m_state . append_translation ( translation , m_params . input_queue_max_size ) ;
2020-03-04 10:36:36 +00:00
updated = true ;
}
2020-03-04 11:18:59 +00:00
// rotation
deadzone = m_params . rotation . deadzone ;
2020-03-04 10:36:36 +00:00
Vec3f rotation ( std : : abs ( packet [ 3 ] ) > deadzone ? ( float ) packet [ 3 ] : 0.0 ,
std : : abs ( packet [ 4 ] ) > deadzone ? ( float ) packet [ 4 ] : 0.0 ,
std : : abs ( packet [ 5 ] ) > deadzone ? ( float ) packet [ 5 ] : 0.0 ) ;
2020-03-04 11:18:59 +00:00
if ( ! rotation . isApprox ( Vec3f : : Zero ( ) ) ) {
m_state . append_rotation ( rotation , m_params . input_queue_max_size ) ;
2020-03-04 10:36:36 +00:00
updated = true ;
}
if ( updated ) {
wxGetApp ( ) . plater ( ) - > set_current_canvas_as_dirty ( ) ;
// ask for an idle event to update 3D scene
wxWakeUpIdle ( ) ;
}
return updated ;
}
# else //__APPLE__
// Initialize the application.
void Mouse3DController : : init ( )
{
assert ( ! m_thread . joinable ( ) ) ;
if ( ! m_thread . joinable ( ) ) {
m_stop = false ;
m_thread = std : : thread ( & Mouse3DController : : run , this ) ;
}
}
// Closing the application.
void Mouse3DController : : shutdown ( )
{
if ( m_thread . joinable ( ) ) {
// Stop the worker thread, if running.
{
// Notify the worker thread to cancel wait on detection polling.
2020-03-06 14:10:58 +00:00
std : : lock_guard < std : : mutex > lock ( m_stop_condition_mutex ) ;
2020-03-04 10:36:36 +00:00
m_stop = true ;
}
2020-03-06 14:10:58 +00:00
m_stop_condition . notify_all ( ) ;
2020-03-04 10:36:36 +00:00
// Wait for the worker thread to stop.
m_thread . join ( ) ;
2020-03-06 14:10:58 +00:00
m_stop = false ;
2020-03-04 10:36:36 +00:00
}
}
// Main routine of the worker thread.
void Mouse3DController : : run ( )
{
// Initialize the hidapi library
int res = hid_init ( ) ;
if ( res ! = 0 ) {
// Give up.
2020-03-12 08:38:22 +00:00
# if defined(__unix__) || defined(__unix) || defined(unix)
if ( res = = - 1 )
// Hopefully this error code comes from our bundled patched hidapi. In that case, -1 is returned by hid_wrapper_udev_init() and it mean
BOOST_LOG_TRIVIAL ( error ) < < " Unable to initialize hidapi library: failed to load libudev.so.1 or libudev.so.0 " ;
else if ( res = = - 2 )
// Hopefully this error code comes from our bundled patched hidapi. In that case, -2 is returned by hid_wrapper_udev_init() and it mean
BOOST_LOG_TRIVIAL ( error ) < < " Unable to initialize hidapi library: failed to resolve some function from libudev.so.1 or libudev.so.0 " ;
else
# endif // unixes
BOOST_LOG_TRIVIAL ( error ) < < " Unable to initialize hidapi library " ;
2020-03-04 10:36:36 +00:00
return ;
}
2020-03-13 13:19:02 +00:00
# ifdef _WIN32
// Enumerate once just after thread start.
m_wakeup = true ;
# endif // _WIN32
2020-03-04 10:36:36 +00:00
for ( ; ; ) {
{
tbb : : mutex : : scoped_lock lock ( m_params_ui_mutex ) ;
if ( m_stop )
break ;
if ( m_params_ui_changed ) {
m_params = m_params_ui ;
m_params_ui_changed = false ;
}
}
if ( m_device = = nullptr )
// Polls the HID devices, blocks for maximum 2 seconds.
m_connected = this - > connect_device ( ) ;
else
// Waits for 3DConnexion mouse input for maximum 100ms, then repeats.
this - > collect_input ( ) ;
}
this - > disconnect_device ( ) ;
// Finalize the hidapi library
hid_exit ( ) ;
}
bool Mouse3DController : : connect_device ( )
{
if ( m_stop )
return false ;
{
// Wait for 2 seconds, but cancellable by m_stop.
std : : unique_lock < std : : mutex > lock ( m_stop_condition_mutex ) ;
2020-03-13 13:19:02 +00:00
# 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
2020-03-04 10:36:36 +00:00
}
if ( m_stop )
return false ;
2019-11-25 09:34:42 +00:00
2019-09-27 12:52:19 +00:00
// Enumerates devices
hid_device_info * devices = hid_enumerate ( 0 , 0 ) ;
if ( devices = = nullptr )
2019-10-04 08:59:27 +00:00
{
2020-03-13 13:19:02 +00:00
BOOST_LOG_TRIVIAL ( trace ) < < " Mouse3DController::connect_device() - no HID device enumerated. " ;
2019-10-04 08:59:27 +00:00
return false ;
}
2019-09-27 12:52:19 +00:00
2020-03-13 13:19:02 +00:00
# ifdef _WIN32
BOOST_LOG_TRIVIAL ( trace ) < < " Mouse3DController::connect_device() - enumerating HID devices. " ;
# endif // _WIN32
2019-09-27 12:52:19 +00:00
// Searches for 1st connected 3Dconnexion device
2019-10-21 12:21:51 +00:00
struct DeviceData
{
2019-10-21 13:20:36 +00:00
std : : string path ;
unsigned short usage_page ;
unsigned short usage ;
DeviceData ( )
2019-11-08 10:51:56 +00:00
: path ( " " ) , usage_page ( 0 ) , usage ( 0 )
2019-10-21 13:20:36 +00:00
{ }
2019-11-08 10:51:56 +00:00
DeviceData ( const std : : string & path , unsigned short usage_page , unsigned short usage )
: path ( path ) , usage_page ( usage_page ) , usage ( usage )
2019-10-21 13:20:36 +00:00
{ }
2019-11-11 12:04:02 +00:00
bool has_valid_usage ( ) const { return ( usage_page = = 1 ) & & ( usage = = 8 ) ; }
2019-10-21 12:21:51 +00:00
} ;
2019-09-27 12:52:19 +00:00
2019-11-11 12:04:02 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
hid_device_info * cur = devices ;
std : : cout < < std : : endl < < " ====================================================================================================================================== " < < std : : endl ;
std : : cout < < " Detected devices: " < < std : : endl ;
while ( cur ! = nullptr )
{
std : : cout < < " \" " ;
std : : wcout < < ( ( cur - > manufacturer_string ! = nullptr ) ? cur - > manufacturer_string : L " Unknown " ) ;
std : : cout < < " / " ;
std : : wcout < < ( ( cur - > product_string ! = nullptr ) ? cur - > product_string : L " Unknown " ) ;
std : : cout < < " \" code: " < < cur - > vendor_id < < " / " < < cur - > product_id < < " ( " < < std : : hex < < cur - > vendor_id < < " / " < < cur - > product_id < < std : : dec < < " ) " ;
std : : cout < < " serial number: ' " ;
std : : wcout < < ( ( cur - > serial_number ! = nullptr ) ? cur - > serial_number : L " Unknown " ) ;
std : : cout < < " ' usage page: " < < cur - > usage_page < < " usage: " < < cur - > usage < < " interface number: " < < cur - > interface_number < < std : : endl ;
cur = cur - > next ;
}
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-10-09 12:18:43 +00:00
2019-10-21 12:21:51 +00:00
// When using 3Dconnexion universal receiver, multiple devices are detected sharing the same vendor_id and product_id.
// To choose from them the right one we use:
// On Windows and Mac: usage_page == 1 and usage == 8
2019-11-08 10:51:56 +00:00
// On Linux: as usage_page and usage are not defined (see hidapi.h) we try all detected devices until one is succesfully open
2019-10-21 12:21:51 +00:00
// When only a single device is detected, as for wired connections, vendor_id and product_id are enough
// First we count all the valid devices from the enumerated list,
2019-10-08 11:38:08 +00:00
hid_device_info * current = devices ;
2019-10-21 12:21:51 +00:00
typedef std : : pair < unsigned short , unsigned short > DeviceIds ;
typedef std : : vector < DeviceData > DeviceDataList ;
typedef std : : map < DeviceIds , DeviceDataList > DetectedDevices ;
DetectedDevices detected_devices ;
2019-11-08 10:51:56 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-11-11 12:04:02 +00:00
std : : cout < < std : : endl < < " Detected 3D connexion devices: " < < std : : endl ;
2019-11-08 10:51:56 +00:00
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-10-08 11:38:08 +00:00
while ( current ! = nullptr )
{
2019-10-21 12:21:51 +00:00
unsigned short vendor_id = 0 ;
unsigned short product_id = 0 ;
2019-09-27 12:52:19 +00:00
for ( size_t i = 0 ; i < _3DCONNEXION_VENDORS . size ( ) ; + + i )
{
if ( _3DCONNEXION_VENDORS [ i ] = = current - > vendor_id )
{
vendor_id = current - > vendor_id ;
break ;
}
}
if ( vendor_id ! = 0 )
{
for ( size_t i = 0 ; i < _3DCONNEXION_DEVICES . size ( ) ; + + i )
{
2019-10-21 12:21:51 +00:00
if ( _3DCONNEXION_DEVICES [ i ] = = current - > product_id )
2019-09-27 12:52:19 +00:00
{
product_id = current - > product_id ;
2019-10-21 12:21:51 +00:00
DeviceIds detected_device ( vendor_id , product_id ) ;
DetectedDevices : : iterator it = detected_devices . find ( detected_device ) ;
if ( it = = detected_devices . end ( ) )
it = detected_devices . insert ( DetectedDevices : : value_type ( detected_device , DeviceDataList ( ) ) ) . first ;
2019-11-08 10:51:56 +00:00
it - > second . emplace_back ( current - > path , current - > usage_page , current - > usage ) ;
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std : : wcout < < " \" " < < ( ( current - > manufacturer_string ! = nullptr ) ? current - > manufacturer_string : L " Unknown " ) ;
std : : cout < < " / " ;
std : : wcout < < ( ( current - > product_string ! = nullptr ) ? current - > product_string : L " Unknown " ) ;
std : : cout < < " \" code: " < < current - > vendor_id < < " / " < < current - > product_id < < " ( " < < std : : hex < < current - > vendor_id < < " / " < < current - > product_id < < std : : dec < < " ) " ;
std : : cout < < " serial number: ' " ;
std : : wcout < < ( ( current - > serial_number ! = nullptr ) ? current - > serial_number : L " Unknown " ) ;
std : : cout < < " ' usage page: " < < current - > usage_page < < " usage: " < < current - > usage < < std : : endl ;
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-09-27 12:52:19 +00:00
}
}
}
current = current - > next ;
}
// Free enumerated devices
hid_free_enumeration ( devices ) ;
2019-10-21 12:21:51 +00:00
if ( detected_devices . empty ( ) )
return false ;
std : : string path = " " ;
unsigned short vendor_id = 0 ;
unsigned short product_id = 0 ;
// Then we'll decide the choosing logic to apply in dependence of the device count and operating system
for ( const DetectedDevices : : value_type & device : detected_devices )
{
if ( device . second . size ( ) = = 1 )
{
2020-01-21 13:34:22 +00:00
# if defined(__linux__)
2019-11-11 12:41:50 +00:00
hid_device * test_device = hid_open ( device . first . first , device . first . second , nullptr ) ;
2019-11-11 12:04:02 +00:00
if ( test_device ! = nullptr )
{
hid_close ( test_device ) ;
# else
if ( device . second . front ( ) . has_valid_usage ( ) )
{
2020-01-21 13:34:22 +00:00
# endif // __linux__
2019-11-11 12:04:02 +00:00
vendor_id = device . first . first ;
product_id = device . first . second ;
break ;
}
2019-10-21 12:21:51 +00:00
}
else
{
2019-11-08 10:51:56 +00:00
bool found = false ;
2019-11-08 13:43:15 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std : : cout < < std : : endl ;
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-10-21 12:21:51 +00:00
for ( const DeviceData & data : device . second )
{
2019-11-08 13:43:15 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std : : cout < < " Test device: " < < std : : hex < < device . first . first < < std : : dec < < " / " < < std : : hex < < device . first . second < < std : : dec < < " \" " < < data . path < < " \" " ;
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2020-01-21 13:34:22 +00:00
2019-11-08 13:43:15 +00:00
# ifdef __linux__
2019-11-08 10:51:56 +00:00
hid_device * test_device = hid_open_path ( data . path . c_str ( ) ) ;
if ( test_device ! = nullptr )
{
path = data . path ;
vendor_id = device . first . first ;
product_id = device . first . second ;
found = true ;
2019-11-08 12:31:34 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-11-08 13:43:15 +00:00
std : : cout < < " -> PASSED " < < std : : endl ;
2019-11-08 12:31:34 +00:00
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-11-08 13:43:15 +00:00
hid_close ( test_device ) ;
2019-11-08 10:51:56 +00:00
break ;
}
2020-01-21 13:34:22 +00:00
# else // !__linux__
2019-11-11 12:04:02 +00:00
if ( data . has_valid_usage ( ) )
2019-10-21 12:21:51 +00:00
{
path = data . path ;
vendor_id = device . first . first ;
product_id = device . first . second ;
2019-11-08 10:51:56 +00:00
found = true ;
2019-11-08 13:43:15 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std : : cout < < " -> PASSED " < < std : : endl ;
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-10-21 12:21:51 +00:00
break ;
}
2019-11-08 14:54:44 +00:00
# endif // __linux__
2019-11-08 13:43:15 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
else
std : : cout < < " -> NOT PASSED " < < std : : endl ;
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-10-21 12:21:51 +00:00
}
2019-11-11 12:04:02 +00:00
2019-11-08 10:51:56 +00:00
if ( found )
break ;
2019-10-21 12:21:51 +00:00
}
}
if ( path . empty ( ) )
2019-11-08 12:31:34 +00:00
{
if ( ( vendor_id ! = 0 ) & & ( product_id ! = 0 ) )
2019-11-08 13:43:15 +00:00
{
2019-11-08 12:31:34 +00:00
// Open the 3Dconnexion device using vendor_id and product_id
2019-11-08 13:43:15 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std : : cout < < std : : endl < < " Opening device: " < < std : : hex < < vendor_id < < std : : dec < < " / " < < std : : hex < < product_id < < std : : dec < < " using hid_open() " < < std : : endl ;
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-11-08 12:31:34 +00:00
m_device = hid_open ( vendor_id , product_id , nullptr ) ;
2019-11-08 13:43:15 +00:00
}
2019-11-08 12:31:34 +00:00
else
return false ;
}
else
2019-11-08 13:43:15 +00:00
{
2019-11-08 12:31:34 +00:00
// Open the 3Dconnexion device using the device path
2019-11-08 13:43:15 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std : : cout < < std : : endl < < " Opening device: " < < std : : hex < < vendor_id < < std : : dec < < " / " < < std : : hex < < product_id < < std : : dec < < " \" " < < path < < " \" using hid_open_path() " < < std : : endl ;
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-11-08 12:31:34 +00:00
m_device = hid_open_path ( path . c_str ( ) ) ;
2019-11-08 13:43:15 +00:00
}
2019-09-27 12:52:19 +00:00
2019-10-03 08:26:28 +00:00
if ( m_device ! = nullptr )
{
2020-01-08 11:30:42 +00:00
wchar_t buffer [ 1024 ] ;
hid_get_manufacturer_string ( m_device , buffer , 1024 ) ;
m_device_str = boost : : nowide : : narrow ( buffer ) ;
// #3479 seems to show that sometimes an extra whitespace is added, so we remove it
boost : : algorithm : : trim ( m_device_str ) ;
hid_get_product_string ( m_device , buffer , 1024 ) ;
m_device_str + = " / " + boost : : nowide : : narrow ( buffer ) ;
// #3479 seems to show that sometimes an extra whitespace is added, so we remove it
boost : : algorithm : : trim ( m_device_str ) ;
2019-10-03 08:26:28 +00:00
2020-01-03 09:01:27 +00:00
BOOST_LOG_TRIVIAL ( info ) < < " Connected 3DConnexion device: " ;
BOOST_LOG_TRIVIAL ( info ) < < " Manufacturer/product: " < < m_device_str ;
BOOST_LOG_TRIVIAL ( info ) < < " Manufacturer id.....: " < < vendor_id < < " ( " < < std : : hex < < vendor_id < < std : : dec < < " ) " ;
BOOST_LOG_TRIVIAL ( info ) < < " Product id..........: " < < product_id < < " ( " < < std : : hex < < product_id < < std : : dec < < " ) " ;
if ( ! path . empty ( ) )
BOOST_LOG_TRIVIAL ( info ) < < " Path................: ' " < < path < < " ' " ;
2020-01-21 13:34:22 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std : : cout < < " Opened device. " < < std : : endl ;
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2020-03-04 10:36:36 +00:00
// Copy the parameters for m_device_str into the current parameters.
if ( auto it_params = m_params_by_device . find ( m_device_str ) ; it_params ! = m_params_by_device . end ( ) ) {
tbb : : mutex : : scoped_lock lock ( m_params_ui_mutex ) ;
m_params = m_params_ui = it_params - > second ;
}
2019-10-03 08:26:28 +00:00
}
2019-10-21 09:06:18 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
else
{
std : : cout < < std : : endl < < " Unable to connect to device: " < < std : : endl ;
std : : cout < < " Manufacturer/product: " < < m_device_str < < std : : endl ;
std : : cout < < " Manufacturer id.....: " < < vendor_id < < " ( " < < std : : hex < < vendor_id < < std : : dec < < " ) " < < std : : endl ;
std : : cout < < " Product id..........: " < < product_id < < " ( " < < std : : hex < < product_id < < std : : dec < < " ) " < < std : : endl ;
std : : cout < < " Path................: ' " < < path < < " ' " < < std : : endl ;
}
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-10-04 08:59:27 +00:00
return ( m_device ! = nullptr ) ;
2019-09-27 12:52:19 +00:00
}
void Mouse3DController : : disconnect_device ( )
{
2020-03-04 10:36:36 +00:00
if ( m_device ) {
hid_close ( m_device ) ;
m_device = nullptr ;
BOOST_LOG_TRIVIAL ( info ) < < " Disconnected device: " < < m_device_str ;
// Copy the current parameters for m_device_str into the parameter database.
{
tbb : : mutex : : scoped_lock lock ( m_params_ui_mutex ) ;
m_params_by_device [ m_device_str ] = m_params_ui ;
}
m_device_str . clear ( ) ;
m_connected = false ;
2020-03-13 13:19:02 +00:00
# ifdef _WIN32
// Enumerate once immediately after disconnect.
m_wakeup = true ;
2020-03-13 13:57:45 +00:00
# endif // _WIN32
wxGetApp ( ) . plater ( ) - > CallAfter ( [ ] ( ) {
Plater * plater = wxGetApp ( ) . plater ( ) ;
if ( plater ! = nullptr ) {
plater - > get_camera ( ) . recover_from_free_camera ( ) ;
plater - > set_current_canvas_as_dirty ( ) ;
}
} ) ;
2020-03-10 12:02:39 +00:00
}
2019-09-27 12:52:19 +00:00
}
2019-10-04 05:58:01 +00:00
void Mouse3DController : : collect_input ( )
2019-09-27 12:52:19 +00:00
{
2020-01-21 13:34:22 +00:00
DataPacketRaw packet = { 0 } ;
2020-03-04 10:36:36 +00:00
// Read packet, block maximum 100 ms. That means when closing the application, closing the application will be delayed by 100 ms.
2019-10-08 11:38:08 +00:00
int res = hid_read_timeout ( m_device , packet . data ( ) , packet . size ( ) , 100 ) ;
2020-03-04 10:36:36 +00:00
if ( res < 0 ) {
// An error occourred (device detached from pc ?). Close the 3Dconnexion device.
this - > disconnect_device ( ) ;
} else
this - > handle_input ( packet , res , m_params , m_state ) ;
2020-01-21 13:34:22 +00:00
}
2020-03-04 10:36:36 +00:00
// Unpack raw 3DConnexion HID packet of a wired 3D mouse into m_state. Called by the worker thread.
bool Mouse3DController : : handle_input ( const DataPacketRaw & packet , const int packet_lenght , const Params & params , State & state_in_out )
2020-01-21 13:34:22 +00:00
{
2020-03-04 10:36:36 +00:00
if ( ! wxGetApp ( ) . IsActive ( ) )
return false ;
2019-10-08 11:38:08 +00:00
2020-01-21 13:34:22 +00:00
int res = packet_lenght ;
2019-10-08 11:38:08 +00:00
bool updated = false ;
if ( res = = 7 )
2020-03-04 10:36:36 +00:00
updated = handle_packet ( packet , params , state_in_out ) ;
2019-10-08 11:38:08 +00:00
else if ( res = = 13 )
2020-03-04 10:36:36 +00:00
updated = handle_wireless_packet ( packet , params , state_in_out ) ;
2019-11-08 07:47:57 +00:00
else if ( ( res = = 3 ) & & ( packet [ 0 ] = = 3 ) )
// On Mac button packets can be 3 bytes long
2020-03-04 10:36:36 +00:00
updated = handle_packet ( packet , params , state_in_out ) ;
2019-10-11 07:16:20 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-10-08 11:38:08 +00:00
else if ( res > 0 )
std : : cout < < " Got unknown data packet of length: " < < res < < " , code: " < < ( int ) packet [ 0 ] < < std : : endl ;
2019-10-11 07:16:20 +00:00
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-10-08 11:38:08 +00:00
2020-03-04 10:36:36 +00:00
# if 1
if ( updated ) {
2020-01-03 10:41:29 +00:00
wxGetApp ( ) . plater ( ) - > set_current_canvas_as_dirty ( ) ;
2019-10-08 11:38:08 +00:00
// ask for an idle event to update 3D scene
wxWakeUpIdle ( ) ;
2020-01-03 10:41:29 +00:00
}
2020-03-04 10:36:36 +00:00
# endif
return updated ;
2019-10-08 11:38:08 +00:00
}
2020-03-04 10:36:36 +00:00
// Unpack raw 3DConnexion HID packet of a wired 3D mouse into m_state. Called by handle_input() from the worker thread.
bool Mouse3DController : : handle_packet ( const DataPacketRaw & packet , const Params & params , State & state_in_out )
2019-10-08 11:38:08 +00:00
{
switch ( packet [ 0 ] )
2019-09-27 12:52:19 +00:00
{
2019-10-08 11:38:08 +00:00
case 1 : // Translation
{
2020-03-04 10:36:36 +00:00
if ( handle_packet_translation ( packet , params , state_in_out ) )
2019-10-08 11:38:08 +00:00
return true ;
2019-10-07 07:31:23 +00:00
2019-10-08 11:38:08 +00:00
break ;
}
case 2 : // Rotation
2019-09-27 12:52:19 +00:00
{
2020-03-04 10:36:36 +00:00
if ( handle_packet_rotation ( packet , 1 , params , state_in_out ) )
2019-10-08 11:38:08 +00:00
return true ;
2019-09-27 12:52:19 +00:00
2019-10-08 11:38:08 +00:00
break ;
}
case 3 : // Button
{
2020-03-04 10:36:36 +00:00
if ( params . buttons_enabled & & handle_packet_button ( packet , packet . size ( ) - 1 , params , state_in_out ) )
2019-10-08 11:38:08 +00:00
return true ;
2019-09-27 12:52:19 +00:00
2019-10-11 13:29:57 +00:00
break ;
}
case 23 : // Battery charge
{
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2020-03-04 10:36:36 +00:00
std : : cout < < " 3DConnexion - battery level: " < < ( int ) packet [ 1 ] < < " percent " < < std : : endl ;
2019-10-11 13:29:57 +00:00
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-10-08 11:38:08 +00:00
break ;
}
default :
{
2019-10-11 07:16:20 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2020-03-04 10:36:36 +00:00
std : : cout < < " 3DConnexion - Got unknown data packet of code: " < < ( int ) packet [ 0 ] < < std : : endl ;
2019-10-11 07:16:20 +00:00
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-10-08 11:38:08 +00:00
break ;
}
}
2019-09-27 12:52:19 +00:00
2019-10-08 11:38:08 +00:00
return false ;
}
2020-03-04 10:36:36 +00:00
// Unpack raw 3DConnexion HID packet of a wireless 3D mouse into m_state. Called by handle_input() from the worker thread.
bool Mouse3DController : : handle_wireless_packet ( const DataPacketRaw & packet , const Params & params , State & state_in_out )
2019-10-08 11:38:08 +00:00
{
switch ( packet [ 0 ] )
{
case 1 : // Translation + Rotation
{
2020-03-04 10:36:36 +00:00
bool updated = handle_packet_translation ( packet , params , state_in_out ) ;
updated | = handle_packet_rotation ( packet , 7 , params , state_in_out ) ;
2019-10-08 11:38:08 +00:00
if ( updated )
return true ;
break ;
}
case 3 : // Button
{
2020-03-04 10:36:36 +00:00
if ( params . buttons_enabled & & handle_packet_button ( packet , 12 , params , state_in_out ) )
2019-10-08 11:38:08 +00:00
return true ;
2019-10-03 08:26:28 +00:00
2019-10-11 13:29:57 +00:00
break ;
}
case 23 : // Battery charge
{
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2020-03-04 10:36:36 +00:00
std : : cout < < " 3DConnexion - battery level: " < < ( int ) packet [ 1 ] < < " percent " < < std : : endl ;
2019-10-11 13:29:57 +00:00
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-09-27 12:52:19 +00:00
break ;
}
2019-10-08 11:38:08 +00:00
default :
{
2019-10-11 07:16:20 +00:00
# if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2020-03-04 10:36:36 +00:00
std : : cout < < " 3DConnexion - Got unknown data packet of code: " < < ( int ) packet [ 0 ] < < std : : endl ;
2019-10-11 07:16:20 +00:00
# endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
2019-10-08 11:38:08 +00:00
break ;
}
}
return false ;
}
2020-03-04 10:36:36 +00:00
// Convert a signed 16bit word from a 3DConnexion mouse HID packet into a double coordinate, apply a dead zone.
static double convert_input ( int coord_byte_low , int coord_byte_high , double deadzone )
2019-10-08 11:38:08 +00:00
{
2020-03-04 10:36:36 +00:00
int value = coord_byte_low | ( coord_byte_high < < 8 ) ;
if ( value > = 32768 )
value = value - 65536 ;
2019-10-09 12:39:28 +00:00
double ret = ( double ) value / 350.0 ;
return ( std : : abs ( ret ) > deadzone ) ? ret : 0.0 ;
2019-10-09 12:01:13 +00:00
}
2019-10-08 11:38:08 +00:00
2020-03-04 10:36:36 +00:00
// Unpack raw 3DConnexion HID packet, decode state of translation axes into state_in_out. Called by handle_input() from the worker thread.
bool Mouse3DController : : handle_packet_translation ( const DataPacketRaw & packet , const Params & params , State & state_in_out )
2019-10-08 11:38:08 +00:00
{
2020-03-04 10:36:36 +00:00
double deadzone = params . translation . deadzone ;
2019-10-09 12:39:28 +00:00
Vec3d translation ( - convert_input ( packet [ 1 ] , packet [ 2 ] , deadzone ) ,
convert_input ( packet [ 3 ] , packet [ 4 ] , deadzone ) ,
convert_input ( packet [ 5 ] , packet [ 6 ] , deadzone ) ) ;
if ( ! translation . isApprox ( Vec3d : : Zero ( ) ) )
2019-10-08 11:38:08 +00:00
{
2020-03-04 10:36:36 +00:00
state_in_out . append_translation ( translation , params . input_queue_max_size ) ;
2019-10-08 11:38:08 +00:00
return true ;
}
return false ;
}
2020-03-04 10:36:36 +00:00
// Unpack raw 3DConnexion HID packet, decode state of rotation axes into state_in_out. Called by the handle_input() from worker thread.
bool Mouse3DController : : handle_packet_rotation ( const DataPacketRaw & packet , unsigned int first_byte , const Params & params , State & state_in_out )
2019-10-08 11:38:08 +00:00
{
2020-03-04 10:36:36 +00:00
double deadzone = ( double ) params . rotation . deadzone ;
2020-01-15 11:49:34 +00:00
Vec3f rotation ( ( float ) convert_input ( packet [ first_byte + 0 ] , packet [ first_byte + 1 ] , deadzone ) ,
( float ) convert_input ( packet [ first_byte + 2 ] , packet [ first_byte + 3 ] , deadzone ) ,
( float ) convert_input ( packet [ first_byte + 4 ] , packet [ first_byte + 5 ] , deadzone ) ) ;
2019-10-08 12:32:05 +00:00
2019-10-09 12:39:28 +00:00
if ( ! rotation . isApprox ( Vec3f : : Zero ( ) ) )
2019-10-08 11:38:08 +00:00
{
2020-03-04 10:36:36 +00:00
state_in_out . append_rotation ( rotation , params . input_queue_max_size ) ;
2019-10-08 11:38:08 +00:00
return true ;
}
2019-10-07 07:31:23 +00:00
2019-10-08 11:38:08 +00:00
return false ;
}
2020-03-04 10:36:36 +00:00
// Unpack raw 3DConnexion HID packet, decode button state into state_in_out. Called by handle_input() from the worker thread.
bool Mouse3DController : : handle_packet_button ( const DataPacketRaw & packet , unsigned int packet_size , const Params & params , State & state_in_out )
2019-10-08 11:38:08 +00:00
{
2019-11-08 08:18:59 +00:00
unsigned int data = 0 ;
for ( unsigned int i = 1 ; i < packet_size ; + + i )
{
data | = packet [ i ] < < 8 * ( i - 1 ) ;
}
2019-10-09 12:01:13 +00:00
const std : : bitset < 32 > data_bits { data } ;
for ( size_t i = 0 ; i < data_bits . size ( ) ; + + i )
2019-10-08 11:38:08 +00:00
{
2019-10-09 12:01:13 +00:00
if ( data_bits . test ( i ) )
2019-10-08 11:38:08 +00:00
{
2020-03-04 10:36:36 +00:00
state_in_out . append_button ( ( unsigned int ) i , params . input_queue_max_size ) ;
2019-10-09 12:01:13 +00:00
return true ;
2019-10-08 11:38:08 +00:00
}
2019-09-27 12:52:19 +00:00
}
2019-10-08 11:38:08 +00:00
return false ;
2019-09-27 12:52:19 +00:00
}
2020-03-04 10:36:36 +00:00
# endif //__APPLE__
2019-09-27 12:52:19 +00:00
} // namespace GUI
} // namespace Slic3r