From 15391da512f68e5bd748fe52d0f53b841fdbed46 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 21 Jan 2020 14:34:22 +0100 Subject: [PATCH] OSX handler for 3dconnexion drivers --- src/CMakeLists.txt | 2 +- src/platform/osx/entitlements.plist | 9 + src/slic3r/CMakeLists.txt | 1 + src/slic3r/GUI/Mouse3DController.cpp | 78 ++++++-- src/slic3r/GUI/Mouse3DController.hpp | 42 ++++- src/slic3r/GUI/Mouse3DHandlerMac.mm | 255 +++++++++++++++++++++++++++ 6 files changed, 364 insertions(+), 23 deletions(-) create mode 100644 src/platform/osx/entitlements.plist create mode 100644 src/slic3r/GUI/Mouse3DHandlerMac.mm diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 54ebf33b9..11996af1a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,7 +84,7 @@ endif (MINGW) if (NOT WIN32) # Binary name on unix like systems (OSX, Linux) - set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer") + set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer") endif () target_link_libraries(PrusaSlicer libslic3r cereal) diff --git a/src/platform/osx/entitlements.plist b/src/platform/osx/entitlements.plist new file mode 100644 index 000000000..59274f955 --- /dev/null +++ b/src/platform/osx/entitlements.plist @@ -0,0 +1,9 @@ + + + + + + com.apple.security.cs.disable-library-validation + + + diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 9fd846272..751a1fcaf 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -173,6 +173,7 @@ if (APPLE) Utils/MacDarkMode.mm GUI/RemovableDriveManagerMM.mm GUI/RemovableDriveManagerMM.h + GUI/Mouse3DHandlerMac.mm ) #DK FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index e28336913..fd58447b5 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -15,6 +15,9 @@ #include +//unofficial linux lib +//#include + // WARN: If updating these lists, please also update resources/udev/90-3dconnexion.rules static const std::vector _3DCONNEXION_VENDORS = @@ -204,7 +207,11 @@ Mouse3DController::Mouse3DController() , m_device_str("") , m_running(false) , m_show_settings_dialog(false) + , m_mac_mouse_connected(false) , m_settings_dialog_closed_by_user(false) +#if __APPLE__ + ,m_handler_mac(new Mouse3DHandlerMac(this)) +#endif //__APPLE__ { m_last_time = std::chrono::high_resolution_clock::now(); } @@ -244,7 +251,7 @@ bool Mouse3DController::apply(Camera& camera) return false; // check if the user unplugged the device - if (!m_running && is_device_connected()) + if (!is_running() && is_device_connected()) { disconnect_device(); // hides the settings dialog if the user un-plug the device @@ -261,7 +268,7 @@ bool Mouse3DController::apply(Camera& camera) void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const { - if (!m_running || !m_show_settings_dialog) + if (!is_running() || !m_show_settings_dialog) return; // when the user clicks on [X] or [Close] button we need to trigger @@ -397,6 +404,9 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const bool Mouse3DController::connect_device() { +#ifdef __APPLE__ + return false; +#endif//__APPLE__ static const long long DETECTION_TIME_MS = 2000; // two seconds if (is_device_connected()) @@ -405,7 +415,7 @@ bool Mouse3DController::connect_device() // check time since last detection took place if (std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_last_time).count() < DETECTION_TIME_MS) return false; - + m_last_time = std::chrono::high_resolution_clock::now(); // Enumerates devices @@ -528,7 +538,7 @@ bool Mouse3DController::connect_device() { if (device.second.size() == 1) { -#ifdef __linux__ +#if defined(__linux__) hid_device* test_device = hid_open(device.first.first, device.first.second, nullptr); if (test_device != nullptr) { @@ -536,7 +546,7 @@ bool Mouse3DController::connect_device() #else if (device.second.front().has_valid_usage()) { -#endif // __linux__ +#endif // __linux__ vendor_id = device.first.first; product_id = device.first.second; break; @@ -553,6 +563,7 @@ bool Mouse3DController::connect_device() #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 + #ifdef __linux__ hid_device* test_device = hid_open_path(data.path.c_str()); if (test_device != nullptr) @@ -567,7 +578,7 @@ bool Mouse3DController::connect_device() hid_close(test_device); break; } -#else +#else // !__linux__ if (data.has_valid_usage()) { path = data.path; @@ -632,7 +643,9 @@ bool Mouse3DController::connect_device() BOOST_LOG_TRIVIAL(info) << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")"; if (!path.empty()) BOOST_LOG_TRIVIAL(info) << "Path................: '" << path << "'"; - +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout << "Opened device." << std::endl; +#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT // get device parameters from the config, if present double translation_speed = 4.0; float rotation_speed = 4.0; @@ -717,7 +730,7 @@ void Mouse3DController::run() } void Mouse3DController::collect_input() { - DataPacket packet = { 0 }; + DataPacketRaw packet = { 0 }; int res = hid_read_timeout(m_device, packet.data(), packet.size(), 100); if (res < 0) { @@ -725,10 +738,47 @@ void Mouse3DController::collect_input() stop(); return; } - + handle_input(packet, res); +} + +void Mouse3DController::handle_input_axis(const DataPacketAxis& packet) +{ + if (!wxGetApp().IsActive()) + return; + bool appended = false; + //translation + double deadzone = m_state.get_translation_deadzone(); + 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); + if (!translation.isApprox(Vec3d::Zero())) + { + m_state.append_translation(translation); + appended = true; + } + //rotation + deadzone = m_state.get_rotation_deadzone(); + 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); + if (!rotation.isApprox(Vec3f::Zero())) + { + m_state.append_rotation(rotation); + appended = true; + } + if (appended) + { + wxGetApp().plater()->set_current_canvas_as_dirty(); + // ask for an idle event to update 3D scene + wxWakeUpIdle(); + } +} +void Mouse3DController::handle_input(const DataPacketRaw& packet, const int packet_lenght) +{ if (!wxGetApp().IsActive()) return; + int res = packet_lenght; bool updated = false; if (res == 7) @@ -751,7 +801,7 @@ void Mouse3DController::collect_input() } } -bool Mouse3DController::handle_packet(const DataPacket& packet) +bool Mouse3DController::handle_packet(const DataPacketRaw& packet) { switch (packet[0]) { @@ -795,7 +845,7 @@ bool Mouse3DController::handle_packet(const DataPacket& packet) return false; } -bool Mouse3DController::handle_wireless_packet(const DataPacket& packet) +bool Mouse3DController::handle_wireless_packet(const DataPacketRaw& packet) { switch (packet[0]) { @@ -842,7 +892,7 @@ double convert_input(unsigned char first, unsigned char second, double deadzone) return (std::abs(ret) > deadzone) ? ret : 0.0; } -bool Mouse3DController::handle_packet_translation(const DataPacket& packet) +bool Mouse3DController::handle_packet_translation(const DataPacketRaw& packet) { double deadzone = m_state.get_translation_deadzone(); Vec3d translation(-convert_input(packet[1], packet[2], deadzone), @@ -858,7 +908,7 @@ bool Mouse3DController::handle_packet_translation(const DataPacket& packet) return false; } -bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigned int first_byte) +bool Mouse3DController::handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte) { double deadzone = (double)m_state.get_rotation_deadzone(); #if ENABLE_6DOF_CAMERA @@ -880,7 +930,7 @@ bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigne return false; } -bool Mouse3DController::handle_packet_button(const DataPacket& packet, unsigned int packet_size) +bool Mouse3DController::handle_packet_button(const DataPacketRaw& packet, unsigned int packet_size) { unsigned int data = 0; for (unsigned int i = 1; i < packet_size; ++i) diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index f3826f8e2..018cb98e6 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -14,9 +14,15 @@ #include #include + + namespace Slic3r { namespace GUI { +#if __APPLE__ + class Mouse3DHandlerMac; +#endif//__APPLE__ + struct Camera; class GLCanvas3D; @@ -141,6 +147,7 @@ class Mouse3DController hid_device* m_device; std::string m_device_str; bool m_running; + bool m_mac_mouse_connected; mutable bool m_show_settings_dialog; // set to true when ther user closes the dialog by clicking on [X] or [Close] buttons mutable bool m_settings_dialog_closed_by_user; @@ -152,9 +159,11 @@ public: void init(); void shutdown(); - bool is_device_connected() const { return m_device != nullptr; } - bool is_running() const { return m_running; } + bool is_device_connected() const { return m_device != nullptr || m_mac_mouse_connected; } + bool is_running() const { return m_running || m_mac_mouse_connected; } + void set_mac_mouse_connected(bool b){m_mac_mouse_connected = b;}; + bool process_mouse_wheel() { return m_state.process_mouse_wheel(); } bool apply(Camera& camera); @@ -163,26 +172,43 @@ public: void show_settings_dialog(bool show) { m_show_settings_dialog = show && is_running(); } void render_settings_dialog(GLCanvas3D& canvas) const; + typedef std::array DataPacketAxis; + void handle_input_axis(const DataPacketAxis& packet); private: bool connect_device(); void disconnect_device(); void start(); void stop() { m_running = false; } + typedef std::array DataPacketRaw; // secondary thread methods void run(); void collect_input(); + void handle_input(const DataPacketRaw& packet, const int packet_lenght); + bool handle_packet(const DataPacketRaw& packet); + bool handle_wireless_packet(const DataPacketRaw& packet); + bool handle_packet_translation(const DataPacketRaw& packet); + bool handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte); + bool handle_packet_button(const DataPacketRaw& packet, unsigned int packet_size); - typedef std::array DataPacket; - bool handle_packet(const DataPacket& packet); - bool handle_wireless_packet(const DataPacket& packet); - bool handle_packet_translation(const DataPacket& packet); - bool handle_packet_rotation(const DataPacket& packet, unsigned int first_byte); - bool handle_packet_button(const DataPacket& packet, unsigned int packet_size); +#if __APPLE__ + Mouse3DHandlerMac* m_handler_mac; +#endif//__APPLE__ }; +#if __APPLE__ +class Mouse3DHandlerMac{ + public: + Mouse3DHandlerMac(Mouse3DController* controller); + ~Mouse3DHandlerMac(); + + bool available(); +}; +#endif//__APPLE__ + } // namespace GUI } // namespace Slic3r + #endif // slic3r_Mouse3DController_hpp_ diff --git a/src/slic3r/GUI/Mouse3DHandlerMac.mm b/src/slic3r/GUI/Mouse3DHandlerMac.mm new file mode 100644 index 000000000..60006b8c1 --- /dev/null +++ b/src/slic3r/GUI/Mouse3DHandlerMac.mm @@ -0,0 +1,255 @@ + +#include "Mouse3DController.hpp" + +#include +#include + +#include + +#include +#include + + +static Slic3r::GUI::Mouse3DController* mouse_3d_controller = NULL; + +static uint16_t clientID = 0; + +static bool driver_loaded = false; +static bool has_new_driver = false; // drivers >= 10.2.2 are "new", and can process events on a separate thread + +// replicate just enough of the 3Dx API for our uses, not everything the driver provides + +#define kConnexionClientModeTakeOver 1 +#define kConnexionMaskAxis 0x3f00 +#define kConnexionMaskAll 0x3fff +#define kConnexionMaskAllButtons 0xffffffff +#define kConnexionCmdHandleButtons 2 +#define kConnexionCmdHandleAxis 3 +#define kConnexionCmdAppSpecific 10 +#define kConnexionMsgDeviceState '3dSR' +#define kConnexionCtlGetDeviceID '3did' + +#pragma pack(push, 2) +struct ConnexionDeviceState { + uint16_t version; + uint16_t client; + uint16_t command; + int16_t param; + int32_t value; + uint64_t time; + uint8_t report[8]; + uint16_t buttons8; // obsolete! (pre-10.x drivers) + int16_t axis[6]; // tx, ty, tz, rx, ry, rz + uint16_t address; + uint32_t buttons; +}; +#pragma pack(pop) + +// callback functions: +typedef void (*AddedHandler)(uint32_t); +typedef void (*RemovedHandler)(uint32_t); +typedef void (*MessageHandler)(uint32_t, uint32_t msg_type, void *msg_arg); + +// driver functions: +typedef int16_t (*SetConnexionHandlers_ptr)(MessageHandler, AddedHandler, RemovedHandler, bool); +typedef int16_t (*InstallConnexionHandlers_ptr)(MessageHandler, AddedHandler, RemovedHandler); +typedef void (*CleanupConnexionHandlers_ptr)(); +typedef uint16_t (*RegisterConnexionClient_ptr)(uint32_t signature, + const char *name, + uint16_t mode, + uint32_t mask); +typedef void (*SetConnexionClientButtonMask_ptr)(uint16_t clientID, uint32_t buttonMask); +typedef void (*UnregisterConnexionClient_ptr)(uint16_t clientID); +typedef int16_t (*ConnexionClientControl_ptr)(uint16_t clientID, + uint32_t message, + int32_t param, + int32_t *result); + +#define DECLARE_FUNC(name) name##_ptr name = NULL + +DECLARE_FUNC(SetConnexionHandlers); +DECLARE_FUNC(InstallConnexionHandlers); +DECLARE_FUNC(CleanupConnexionHandlers); +DECLARE_FUNC(RegisterConnexionClient); +DECLARE_FUNC(SetConnexionClientButtonMask); +DECLARE_FUNC(UnregisterConnexionClient); +DECLARE_FUNC(ConnexionClientControl); + +static void *load_func(void *module, const char *func_name) +{ + void *func = dlsym(module, func_name); + +//#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + if (func) { + BOOST_LOG_TRIVIAL(info) << func_name <<" loaded"; + } + else { + //printf(" %s\n", dlerror()); + BOOST_LOG_TRIVIAL(error) <<"loading 3dx drivers dlsym error: "<< dlerror(); + } +//#endif + + return func; +} + +#define LOAD_FUNC(name) name = (name##_ptr)load_func(module, #name) + +static void *module; // handle to the whole driver + +static bool load_driver_functions() +{ + if (driver_loaded) { + return true; + } + + module = dlopen("/Library/Frameworks/3DconnexionClient.framework/3DconnexionClient", + RTLD_LAZY | RTLD_LOCAL); + + if (module) { + BOOST_LOG_TRIVIAL(info) << "loading 3dx drivers"; + LOAD_FUNC(SetConnexionHandlers); + + if (SetConnexionHandlers != NULL) { + driver_loaded = true; + has_new_driver = true; + } + else { + BOOST_LOG_TRIVIAL(info) << "installing 3dx drivers"; + LOAD_FUNC(InstallConnexionHandlers); + + driver_loaded = (InstallConnexionHandlers != NULL); + } + + if (driver_loaded) { + LOAD_FUNC(CleanupConnexionHandlers); + LOAD_FUNC(RegisterConnexionClient); + LOAD_FUNC(SetConnexionClientButtonMask); + LOAD_FUNC(UnregisterConnexionClient); + LOAD_FUNC(ConnexionClientControl); + } + } + else { + BOOST_LOG_TRIVIAL(error) << "3dx drivers module loading error: "<< dlerror() ; +#if DENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + printf(" %s\n", dlerror()); +#endif + } +#if DENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + printf("loaded: %s\n", driver_loaded ? "YES" : "NO"); + printf("new: %s\n", has_new_driver ? "YES" : "NO"); +#endif + BOOST_LOG_TRIVIAL(info) << "3dx drivers loaded: "<< driver_loaded ? "YES" : "NO" ; + return driver_loaded; +} + +static void unload_driver() +{ + dlclose(module); +} + +static void DeviceAdded(uint32_t unused) +{ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + std::cout<<"3D device added"<> 16; + int16_t productID = result & 0xffff; + + //TODO: verify device + + + mouse_3d_controller->set_mac_mouse_connected(true); +} + +static void DeviceRemoved(uint32_t unused) +{ +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + printf("3d device removed\n"); +#endif + BOOST_LOG_TRIVIAL(info) << "3dx device removed\n"; + mouse_3d_controller->set_mac_mouse_connected(true); +} + +static void DeviceEvent(uint32_t unused, uint32_t msg_type, void *msg_arg) +{ + if (msg_type == kConnexionMsgDeviceState) { + ConnexionDeviceState *s = (ConnexionDeviceState *)msg_arg; + if (s->client == clientID) { + switch (s->command) { + case kConnexionCmdHandleAxis: { + /* + The axis field is an array of 6 signed 16-bit integers corresponding to the 6 device axes. Data is ordered as Tx, Tz, Ty, Rx, Rz, Ry. The values reported are scaled by the driver according to the speed slider settings on the 3Dconnexion preference panel. At maximum speed, the range is - 1024 to 1024. Typical range that you should optimize your application for should be -500 to 500. + */ + //Actually we are getting values way over 1024. Max is probably 2048 now. + std::array packet; + for (int i = 0; i < 6; i++) { + packet[i] = (double)s->axis[i]/350.0;//wanted to divide by 500 but 350 is used at raw input so i used same value. + } + mouse_3d_controller->handle_input_axis(packet); + + + break; + } + case kConnexionCmdHandleButtons: + break; + case kConnexionCmdAppSpecific: + break; + default: + break; + } + } + } + +} + +namespace Slic3r { +namespace GUI { +Mouse3DHandlerMac::Mouse3DHandlerMac(Mouse3DController* controller) +{ + BOOST_LOG_TRIVIAL(info) << "3dx mac handler starts"; + if (load_driver_functions()) { + mouse_3d_controller = controller; + + uint16_t error; + if (has_new_driver) { + error = SetConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved, false); + } + else { + error = InstallConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved); + } + + if (error) { + return; + } + + // Registration is done either by 4letter constant (CFBundleSignature - obsolete + //and we dont have that) or Executable name in pascal string(first byte is string lenght). + //If no packets are recieved the name might be different - check cmake. If debugging try commenting + // set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer") + + clientID = RegisterConnexionClient( + 0, "\013PrusaSlicer", kConnexionClientModeTakeOver, kConnexionMaskAxis); + BOOST_LOG_TRIVIAL(info) << "3dx mac handler registered"; + } +} + +Mouse3DHandlerMac::~Mouse3DHandlerMac() +{ + if (driver_loaded) { + UnregisterConnexionClient(clientID); + CleanupConnexionHandlers(); + unload_driver(); + } + mouse_3d_controller = nullptr; +} + +bool Mouse3DHandlerMac::available() +{ + return driver_loaded; +} + +}}//namespace Slic3r::GUI